xref: /aosp_15_r20/system/extras/ioblame/androidFsParser.py (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
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