• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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