1*288bf522SAndroid Build Coastguard Worker#!/usr/bin/env python 2*288bf522SAndroid Build Coastguard Worker# 3*288bf522SAndroid Build Coastguard Worker# Copyright (C) 2022 The Android Open Source Project 4*288bf522SAndroid Build Coastguard Worker# 5*288bf522SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 6*288bf522SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 7*288bf522SAndroid Build Coastguard Worker# You may obtain a copy of the License at 8*288bf522SAndroid Build Coastguard Worker# 9*288bf522SAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 10*288bf522SAndroid Build Coastguard Worker# 11*288bf522SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 12*288bf522SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 13*288bf522SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14*288bf522SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 15*288bf522SAndroid Build Coastguard Worker# limitations under the License. 16*288bf522SAndroid Build Coastguard Worker# 17*288bf522SAndroid Build Coastguard Worker"""Trace parser for f2fs traces.""" 18*288bf522SAndroid Build Coastguard Worker 19*288bf522SAndroid Build Coastguard Workerimport collections 20*288bf522SAndroid Build Coastguard Workerimport re 21*288bf522SAndroid Build Coastguard Worker 22*288bf522SAndroid Build Coastguard Worker# ex) bt_stack_manage-21277 [000] .... 5879.043608: f2fs_datawrite_start: entry_name /misc/bluedroid/bt_config.bak.new, offset 0, bytes 408, cmdline bt_stack_manage, pid 21277, i_size 0, ino 9103 23*288bf522SAndroid Build Coastguard WorkerRE_WRITE_START = r".+-([0-9]+).*\s+([0-9]+\.[0-9]+):\s+f2fs_datawrite_start:\sentry_name\s(\S+)\,\soffset\s([0-9]+)\,\sbytes\s([0-9]+)\,\scmdline\s(\S+)\,\spid\s([0-9]+)\,\si_size\s([0-9]+)\,\sino\s([0-9]+)" 24*288bf522SAndroid Build Coastguard Worker 25*288bf522SAndroid Build Coastguard Worker# ex) dumpsys-21321 [001] .... 5877.599324: f2fs_dataread_start: entry_name /system/lib64/libbinder.so, offset 311296, bytes 4096, cmdline dumpsys, pid 21321, i_size 848848, ino 2397 26*288bf522SAndroid Build Coastguard WorkerRE_READ_START = r".+-([0-9]+).*\s+([0-9]+\.[0-9]+):\s+f2fs_dataread_start:\sentry_name\s(\S+)\,\soffset\s([0-9]+)\,\sbytes\s([0-9]+)\,\scmdline\s(\S+)\,\spid\s([0-9]+)\,\si_size\s([0-9]+)\,\sino\s([0-9]+)" 27*288bf522SAndroid Build Coastguard Worker 28*288bf522SAndroid Build Coastguard WorkerMIN_PID_BYTES = 1024 * 1024 # 1 MiB 29*288bf522SAndroid Build Coastguard WorkerSMALL_FILE_BYTES = 1024 # 1 KiB 30*288bf522SAndroid Build Coastguard Worker 31*288bf522SAndroid Build Coastguard Worker 32*288bf522SAndroid Build Coastguard Workerclass ProcessTrace: 33*288bf522SAndroid Build Coastguard Worker 34*288bf522SAndroid Build Coastguard Worker def __init__(self, cmdLine, filename, numBytes): 35*288bf522SAndroid Build Coastguard Worker self.cmdLine = cmdLine 36*288bf522SAndroid Build Coastguard Worker self.totalBytes = numBytes 37*288bf522SAndroid Build Coastguard Worker self.bytesByFiles = {filename: numBytes} 38*288bf522SAndroid Build Coastguard Worker 39*288bf522SAndroid Build Coastguard Worker def add_file_trace(self, filename, numBytes): 40*288bf522SAndroid Build Coastguard Worker self.totalBytes += numBytes 41*288bf522SAndroid Build Coastguard Worker if filename in self.bytesByFiles: 42*288bf522SAndroid Build Coastguard Worker self.bytesByFiles[filename] += numBytes 43*288bf522SAndroid Build Coastguard Worker else: 44*288bf522SAndroid Build Coastguard Worker self.bytesByFiles[filename] = numBytes 45*288bf522SAndroid Build Coastguard Worker 46*288bf522SAndroid Build Coastguard Worker def dump(self, mode, outputFile): 47*288bf522SAndroid Build Coastguard Worker smallFileCnt = 0 48*288bf522SAndroid Build Coastguard Worker smallFileBytes = 0 49*288bf522SAndroid Build Coastguard Worker for _, numBytes in self.bytesByFiles.items(): 50*288bf522SAndroid Build Coastguard Worker if numBytes < SMALL_FILE_BYTES: 51*288bf522SAndroid Build Coastguard Worker smallFileCnt += 1 52*288bf522SAndroid Build Coastguard Worker smallFileBytes += numBytes 53*288bf522SAndroid Build Coastguard Worker 54*288bf522SAndroid Build Coastguard Worker if (smallFileCnt != 0): 55*288bf522SAndroid Build Coastguard Worker outputFile.write( 56*288bf522SAndroid Build Coastguard Worker "Process: {}, Traced {} KB: {}, Small file count: {}, Small file KB: {}\n" 57*288bf522SAndroid Build Coastguard Worker .format(self.cmdLine, mode, to_kib(self.totalBytes), smallFileCnt, 58*288bf522SAndroid Build Coastguard Worker to_kib(smallFileBytes))) 59*288bf522SAndroid Build Coastguard Worker 60*288bf522SAndroid Build Coastguard Worker else: 61*288bf522SAndroid Build Coastguard Worker outputFile.write("Process: {}, Traced {} KB: {}\n".format( 62*288bf522SAndroid Build Coastguard Worker self.cmdLine, mode, to_kib(self.totalBytes))) 63*288bf522SAndroid Build Coastguard Worker 64*288bf522SAndroid Build Coastguard Worker if (smallFileCnt == len(self.bytesByFiles)): 65*288bf522SAndroid Build Coastguard Worker return 66*288bf522SAndroid Build Coastguard Worker 67*288bf522SAndroid Build Coastguard Worker sortedEntries = collections.OrderedDict( 68*288bf522SAndroid Build Coastguard Worker sorted( 69*288bf522SAndroid Build Coastguard Worker self.bytesByFiles.items(), key=lambda item: item[1], reverse=True)) 70*288bf522SAndroid Build Coastguard Worker 71*288bf522SAndroid Build Coastguard Worker for i in range(len(sortedEntries)): 72*288bf522SAndroid Build Coastguard Worker filename, numBytes = sortedEntries.popitem(last=False) 73*288bf522SAndroid Build Coastguard Worker if numBytes < SMALL_FILE_BYTES: 74*288bf522SAndroid Build Coastguard Worker # Entries are sorted by bytes. So, break on the first small file entry. 75*288bf522SAndroid Build Coastguard Worker break 76*288bf522SAndroid Build Coastguard Worker 77*288bf522SAndroid Build Coastguard Worker outputFile.write("File: {}, {} KB: {}\n".format(filename, mode, 78*288bf522SAndroid Build Coastguard Worker to_kib(numBytes))) 79*288bf522SAndroid Build Coastguard Worker 80*288bf522SAndroid Build Coastguard Worker 81*288bf522SAndroid Build Coastguard Workerclass UidTrace: 82*288bf522SAndroid Build Coastguard Worker 83*288bf522SAndroid Build Coastguard Worker def __init__(self, uid, cmdLine, filename, numBytes): 84*288bf522SAndroid Build Coastguard Worker self.uid = uid 85*288bf522SAndroid Build Coastguard Worker self.packageName = "" 86*288bf522SAndroid Build Coastguard Worker self.totalBytes = numBytes 87*288bf522SAndroid Build Coastguard Worker self.traceByProcess = {cmdLine: ProcessTrace(cmdLine, filename, numBytes)} 88*288bf522SAndroid Build Coastguard Worker 89*288bf522SAndroid Build Coastguard Worker def add_process_trace(self, cmdLine, filename, numBytes): 90*288bf522SAndroid Build Coastguard Worker self.totalBytes += numBytes 91*288bf522SAndroid Build Coastguard Worker if cmdLine in self.traceByProcess: 92*288bf522SAndroid Build Coastguard Worker self.traceByProcess[cmdLine].add_file_trace(filename, numBytes) 93*288bf522SAndroid Build Coastguard Worker else: 94*288bf522SAndroid Build Coastguard Worker self.traceByProcess[cmdLine] = ProcessTrace(cmdLine, filename, numBytes) 95*288bf522SAndroid Build Coastguard Worker 96*288bf522SAndroid Build Coastguard Worker def dump(self, mode, outputFile): 97*288bf522SAndroid Build Coastguard Worker outputFile.write("Traced {} KB: {}\n\n".format(mode, 98*288bf522SAndroid Build Coastguard Worker to_kib(self.totalBytes))) 99*288bf522SAndroid Build Coastguard Worker 100*288bf522SAndroid Build Coastguard Worker if self.totalBytes < MIN_PID_BYTES: 101*288bf522SAndroid Build Coastguard Worker return 102*288bf522SAndroid Build Coastguard Worker 103*288bf522SAndroid Build Coastguard Worker sortedEntries = collections.OrderedDict( 104*288bf522SAndroid Build Coastguard Worker sorted( 105*288bf522SAndroid Build Coastguard Worker self.traceByProcess.items(), 106*288bf522SAndroid Build Coastguard Worker key=lambda item: item[1].totalBytes, 107*288bf522SAndroid Build Coastguard Worker reverse=True)) 108*288bf522SAndroid Build Coastguard Worker totalEntries = len(sortedEntries) 109*288bf522SAndroid Build Coastguard Worker for i in range(totalEntries): 110*288bf522SAndroid Build Coastguard Worker _, processTrace = sortedEntries.popitem(last=False) 111*288bf522SAndroid Build Coastguard Worker if processTrace.totalBytes < MIN_PID_BYTES: 112*288bf522SAndroid Build Coastguard Worker # Entries are sorted by bytes. So, break on the first small PID entry. 113*288bf522SAndroid Build Coastguard Worker break 114*288bf522SAndroid Build Coastguard Worker 115*288bf522SAndroid Build Coastguard Worker processTrace.dump(mode, outputFile) 116*288bf522SAndroid Build Coastguard Worker if i < totalEntries - 1: 117*288bf522SAndroid Build Coastguard Worker outputFile.write("\n") 118*288bf522SAndroid Build Coastguard Worker 119*288bf522SAndroid Build Coastguard Worker 120*288bf522SAndroid Build Coastguard Workerclass AndroidFsParser: 121*288bf522SAndroid Build Coastguard Worker 122*288bf522SAndroid Build Coastguard Worker def __init__(self, re_string, uidProcessMapper): 123*288bf522SAndroid Build Coastguard Worker self.traceByUid = {} # Key: uid, Value: UidTrace 124*288bf522SAndroid Build Coastguard Worker if (re_string == RE_WRITE_START): 125*288bf522SAndroid Build Coastguard Worker self.mode = "write" 126*288bf522SAndroid Build Coastguard Worker else: 127*288bf522SAndroid Build Coastguard Worker self.mode = "read" 128*288bf522SAndroid Build Coastguard Worker self.re_matcher = re.compile(re_string) 129*288bf522SAndroid Build Coastguard Worker self.uidProcessMapper = uidProcessMapper 130*288bf522SAndroid Build Coastguard Worker self.totalBytes = 0 131*288bf522SAndroid Build Coastguard Worker 132*288bf522SAndroid Build Coastguard Worker def parse(self, line): 133*288bf522SAndroid Build Coastguard Worker match = self.re_matcher.match(line) 134*288bf522SAndroid Build Coastguard Worker if not match: 135*288bf522SAndroid Build Coastguard Worker return False 136*288bf522SAndroid Build Coastguard Worker try: 137*288bf522SAndroid Build Coastguard Worker self.do_parse_start(line, match) 138*288bf522SAndroid Build Coastguard Worker except Exception: 139*288bf522SAndroid Build Coastguard Worker print("cannot parse: {}".format(line)) 140*288bf522SAndroid Build Coastguard Worker raise 141*288bf522SAndroid Build Coastguard Worker return True 142*288bf522SAndroid Build Coastguard Worker 143*288bf522SAndroid Build Coastguard Worker def do_parse_start(self, line, match): 144*288bf522SAndroid Build Coastguard Worker pid = int(match.group(1)) 145*288bf522SAndroid Build Coastguard Worker # start_time = float(match.group(2)) * 1000 #ms 146*288bf522SAndroid Build Coastguard Worker filename = match.group(3) 147*288bf522SAndroid Build Coastguard Worker # offset = int(match.group(4)) 148*288bf522SAndroid Build Coastguard Worker numBytes = int(match.group(5)) 149*288bf522SAndroid Build Coastguard Worker cmdLine = match.group(6) 150*288bf522SAndroid Build Coastguard Worker pid = int(match.group(7)) 151*288bf522SAndroid Build Coastguard Worker # isize = int(match.group(8)) 152*288bf522SAndroid Build Coastguard Worker # ino = int(match.group(9)) 153*288bf522SAndroid Build Coastguard Worker self.totalBytes += numBytes 154*288bf522SAndroid Build Coastguard Worker uid = self.uidProcessMapper.get_uid(cmdLine, pid) 155*288bf522SAndroid Build Coastguard Worker 156*288bf522SAndroid Build Coastguard Worker if uid in self.traceByUid: 157*288bf522SAndroid Build Coastguard Worker self.traceByUid[uid].add_process_trace(cmdLine, filename, numBytes) 158*288bf522SAndroid Build Coastguard Worker else: 159*288bf522SAndroid Build Coastguard Worker self.traceByUid[uid] = UidTrace(uid, cmdLine, filename, numBytes) 160*288bf522SAndroid Build Coastguard Worker 161*288bf522SAndroid Build Coastguard Worker def dumpTotal(self, outputFile): 162*288bf522SAndroid Build Coastguard Worker if self.totalBytes > 0: 163*288bf522SAndroid Build Coastguard Worker outputFile.write("Traced system-wide {} KB: {}\n\n".format( 164*288bf522SAndroid Build Coastguard Worker self.mode, to_kib(self.totalBytes))) 165*288bf522SAndroid Build Coastguard Worker 166*288bf522SAndroid Build Coastguard Worker def dump(self, uid, outputFile): 167*288bf522SAndroid Build Coastguard Worker if uid not in self.traceByUid: 168*288bf522SAndroid Build Coastguard Worker return 169*288bf522SAndroid Build Coastguard Worker 170*288bf522SAndroid Build Coastguard Worker uidTrace = self.traceByUid[uid] 171*288bf522SAndroid Build Coastguard Worker uidTrace.dump(self.mode, outputFile) 172*288bf522SAndroid Build Coastguard Worker 173*288bf522SAndroid Build Coastguard Worker 174*288bf522SAndroid Build Coastguard Workerdef to_kib(bytes): 175*288bf522SAndroid Build Coastguard Worker return bytes / 1024 176