OSDN Git Service

Perfprofd: Fix missing symbol crash in stack script
[android-x86/system-extras.git] / perfprofd / scripts / perf_proto_stack.py
1 #!/usr/bin/python
2 #
3 # Copyright (C) 2017 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
17 # Super simplistic printer of a perfprofd output proto. Illustrates
18 # how to parse and traverse a perfprofd output proto in Python.
19
20 # Generate with:
21 #  aprotoc -I=system/extras/perfprofd --python_out=system/extras/perfprofd/scripts \
22 #      system/extras/perfprofd/perf_profile.proto
23 import perf_profile_pb2
24
25 # Make sure that symbol is on the PYTHONPATH, e.g., run as
26 # PYTHONPATH=$PYTHONPATH:$ANDROID_BUILD_TOP/development/scripts python ...
27 import symbol
28
29 # This is wrong. But then the symbol module is a bad quagmire.
30 symbol.SetAbi(["ABI: 'arm64'"])
31 print "Reading symbols from", symbol.SYMBOLS_DIR
32
33 # TODO: accept argument for parsing.
34 file = open('perf.data.encoded.0', 'rb')
35 data = file.read()
36
37 profile = perf_profile_pb2.AndroidPerfProfile()
38 profile.ParseFromString(data)
39
40 print "Total samples: ", profile.total_samples
41
42 module_list = profile.load_modules
43
44 counters = {}
45
46 def indent(txt, stops = 1):
47     return '\n'.join('  ' * stops + line for line in txt.splitlines())
48
49
50 def print_samples(module_list, programs, counters):
51     print 'Samples:'
52     for program in programs:
53         print indent(program.name, 1)
54         for module in program.modules:
55             if module.HasField('load_module_id'):
56                 module_descr = module_list[module.load_module_id]
57                 print indent(module_descr.name, 2)
58                 has_build_id = module_descr.HasField('build_id')
59                 if has_build_id:
60                     print indent('Build ID: %s' % (module_descr.build_id), 3)
61                 for addr in module.address_samples:
62                     # TODO: Stacks vs single samples.
63                     addr_rel = addr.address[0]
64                     addr_rel_hex = "%x" % addr_rel
65                     print indent('%d %s' % (addr.count, addr_rel_hex), 3)
66                     if module_descr.name != '[kernel.kallsyms]':
67                         if has_build_id:
68                             info = symbol.SymbolInformation(module_descr.name, addr_rel_hex)
69                             # As-is, only info[0] (inner-most inlined function) is recognized.
70                             (source_symbol, source_location, object_symbol_with_offset) = info[0]
71                             if object_symbol_with_offset is not None:
72                                 print indent(object_symbol_with_offset, 4)
73                             if source_symbol is not None:
74                                 for (sym_inlined, loc_inlined, _) in info:
75                                     # TODO: Figure out what's going on here:
76                                     if sym_inlined is not None:
77                                         print indent(sym_inlined, 5)
78                                     else:
79                                         print indent('???', 5)
80                                     if loc_inlined is not None:
81                                         print ' %s' % (indent(loc_inlined, 5))
82                         elif module_descr.symbol and (addr_rel & 0x8000000000000000 != 0):
83                             index = 0xffffffffffffffff - addr_rel
84                             source_symbol = module_descr.symbol[index]
85                             print indent(source_symbol, 4)
86                         counters_key = None
87                         if source_symbol is not None:
88                             counters_key = (module_descr.name, source_symbol)
89                         else:
90                             counters_key = (module_descr.name, addr_rel_hex)
91                         if counters_key in counters:
92                             counters[counters_key] = counters[counters_key] + addr.count
93                         else:
94                             counters[counters_key] = addr.count
95             else:
96                 print indent('<Missing module>', 2)
97
98 def print_histogram(counters, size):
99     # Create a sorted list of top samples.
100     counter_list = []
101     for key, value in counters.iteritems():
102         temp = (key,value)
103         counter_list.append(temp)
104     counter_list.sort(key=lambda counter: counter[1], reverse=True)
105
106     # Print top-size samples.
107     print 'Histogram top-%d:' % (size)
108     for i in xrange(0, min(len(counter_list), size)):
109         print indent('%d: %s' % (i+1, counter_list[i]), 1)
110
111 def print_modules(module_list):
112     print 'Modules:'
113     for module in module_list:
114         print indent(module.name, 1)
115         if module.HasField('build_id'):
116             print indent('Build ID: %s' % (module.build_id), 2)
117         print indent('Symbols:', 2)
118         for symbol in module.symbol:
119             print indent(symbol, 3)
120
121 print_samples(module_list, profile.programs, counters)
122 print_modules(module_list)
123 print_histogram(counters, 100)