1#!/usr/bin/env python3 2# 3# Copyright 2020, The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17import argparse 18import os 19import sys 20from typing import Dict, List, NamedTuple, Tuple 21 22DIR = os.path.abspath(os.path.dirname(__file__)) 23sys.path.append(os.path.dirname(DIR)) # framework/base/startop/script 24import lib.print_utils as print_utils 25 26# Include generated protos. 27dir_name = os.path.dirname(os.path.realpath(__file__)) 28sys.path.append(dir_name + "/generated") 29 30from TraceFile_pb2 import * 31 32def parse_options(argv: List[str] = None): 33 """Parses command line arguments and returns an argparse Namespace object.""" 34 parser = argparse.ArgumentParser(description="Analyze compiled_trace iorap protos.") 35 required_named = parser.add_argument_group('required named arguments') 36 37 required_named.add_argument('-i', dest='input', metavar='FILE', 38 help='Read protobuf file as input') 39 40 optional_named = parser.add_argument_group('optional named arguments') 41 42 optional_named.add_argument('-up', dest='upper_percent', type=float, 43 default=95.0, 44 help='Only show the top-most entries up to this value.') 45 46 optional_named.add_argument('-r', dest='raw', action='store_true', 47 help='Output entire raw file.') 48 optional_named.add_argument('-o', dest='output', 49 help='The results are stored into the output file') 50 optional_named.add_argument('-d', dest='debug', action='store_true' 51 , help='Activity of the app to be compiled') 52 53 return parser.parse_args(argv) 54 55def open_iorap_prefetch_file(file_path: str) -> TraceFile: 56 with open(file_path, "rb") as f: 57 tf = TraceFile() 58 tf.ParseFromString(f.read()) 59 return tf 60 61def print_stats_summary(trace_file: TraceFile, upper_percent): 62 tf_dict = convert_to_dict(trace_file) 63 print_utils.debug_print(tf_dict) 64 65 total_length = 0 66 summaries = [] 67 for name, entries_list in tf_dict.items(): 68 summary = entries_sum(entries_list) 69 summaries.append(summary) 70 71 total_length += summary.length 72 73 # Sort by length 74 summaries.sort(reverse=True, key=lambda s: s.length) 75 76 percent_sum = 0.0 77 skipped_entries = 0 78 79 print("===========================================") 80 print("Total length: {:,} bytes".format(total_length)) 81 print("Displayed upper percent: {:0.2f}%".format(upper_percent)) 82 print("===========================================") 83 print("") 84 print("name,length,percent_of_total,upper_percent") 85 for sum in summaries: 86 percent_of_total = (sum.length * 1.0) / (total_length * 1.0) * 100.0 87 88 percent_sum += percent_of_total 89 90 if percent_sum > upper_percent: 91 skipped_entries = skipped_entries + 1 92 continue 93 94 #print("%s,%d,%.2f%%" %(sum.name, sum.length, percent_of_total)) 95 print("{:s},{:d},{:0.2f}%,{:0.2f}%".format(sum.name, sum.length, percent_of_total, percent_sum)) 96 97 if skipped_entries > 0: 98 print("[WARNING] Skipped {:d} entries, use -up=100 to show everything".format(skipped_entries)) 99 100 pass 101 102class FileEntry(NamedTuple): 103 id: int 104 name: str 105 offset: int 106 length: int 107 108class FileEntrySummary(NamedTuple): 109 name: str 110 length: int 111 112def entries_sum(entries: List[FileEntry]) -> FileEntrySummary: 113 if not entries: 114 return None 115 116 summary = FileEntrySummary(name=entries[0].name, length=0) 117 for entry in entries: 118 summary = FileEntrySummary(summary.name, summary.length + entry.length) 119 120 return summary 121 122def convert_to_dict(trace_file: TraceFile) -> Dict[str, FileEntry]: 123 trace_file_index = trace_file.index 124 125 # entries.id -> entry.file_name 126 entries_map = {} 127 128 index_entries = trace_file_index.entries 129 for entry in index_entries: 130 entries_map[entry.id] = entry.file_name 131 132 final_map = {} 133 134 file_entries_map = {} 135 file_entries = trace_file.list.entries 136 for entry in file_entries: 137 print_utils.debug_print(entry) 138 139 lst = file_entries_map.get(entry.index_id, []) 140 file_entries_map[entry.index_id] = lst 141 142 file_name = entries_map[entry.index_id] 143 file_entry = \ 144 FileEntry(id=entry.index_id, name=file_name, offset=entry.file_offset, length=entry.file_length) 145 146 lst.append(file_entry) 147 148 final_map[file_name] = lst 149 150 return final_map 151 152def main(argv: List[str]) -> int: 153 opts = parse_options(argv[1:]) 154 if opts.debug: 155 print_utils.DEBUG = opts.debug 156 print_utils.debug_print(opts) 157 158 prefetch_file = open_iorap_prefetch_file(opts.input) 159 160 if opts.raw: 161 print(prefetch_file) 162 163 print_stats_summary(prefetch_file, opts.upper_percent) 164 165 return 0 166 167if __name__ == '__main__': 168 sys.exit(main(sys.argv)) 169