Yabin Cui | fd4adae | 2021-10-25 13:55:01 -0700 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | # |
| 3 | # Copyright (C) 2021 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 | |
| 18 | """stackcollapse.py: convert perf.data to Brendan Gregg's "Folded Stacks" format, |
| 19 | which can be read by https://github.com/brendangregg/FlameGraph, and many |
| 20 | other tools. |
| 21 | |
| 22 | Example: |
| 23 | ./app_profiler.py |
| 24 | ./stackcollapse.py | ~/FlameGraph/flamegraph.pl --color=java --countname=ns > flamegraph.svg |
| 25 | """ |
| 26 | |
| 27 | from collections import defaultdict |
| 28 | from simpleperf_report_lib import ReportLib |
Yabin Cui | ab19bc5 | 2022-03-25 11:14:57 -0700 | [diff] [blame] | 29 | from simpleperf_utils import BaseArgumentParser, flatten_arg_list, ReportLibOptions |
Yabin Cui | 61a61ca | 2022-01-26 16:27:26 -0800 | [diff] [blame] | 30 | from typing import DefaultDict, List, Optional, Set |
Yabin Cui | fd4adae | 2021-10-25 13:55:01 -0700 | [diff] [blame] | 31 | |
| 32 | import logging |
| 33 | import sys |
| 34 | |
| 35 | |
| 36 | def collapse_stacks( |
| 37 | record_file: str, |
| 38 | symfs_dir: str, |
| 39 | kallsyms_file: str, |
Yabin Cui | fd4adae | 2021-10-25 13:55:01 -0700 | [diff] [blame] | 40 | event_filter: str, |
| 41 | include_pid: bool, |
| 42 | include_tid: bool, |
| 43 | annotate_kernel: bool, |
| 44 | annotate_jit: bool, |
| 45 | include_addrs: bool, |
Yabin Cui | ab19bc5 | 2022-03-25 11:14:57 -0700 | [diff] [blame] | 46 | report_lib_options: ReportLibOptions): |
Yabin Cui | fd4adae | 2021-10-25 13:55:01 -0700 | [diff] [blame] | 47 | """read record_file, aggregate per-stack and print totals per-stack""" |
| 48 | lib = ReportLib() |
| 49 | |
| 50 | if include_addrs: |
| 51 | lib.ShowIpForUnknownSymbol() |
Yabin Cui | fd4adae | 2021-10-25 13:55:01 -0700 | [diff] [blame] | 52 | if symfs_dir is not None: |
| 53 | lib.SetSymfs(symfs_dir) |
| 54 | if record_file is not None: |
| 55 | lib.SetRecordFile(record_file) |
| 56 | if kallsyms_file is not None: |
| 57 | lib.SetKallsymsFile(kallsyms_file) |
Yabin Cui | ab19bc5 | 2022-03-25 11:14:57 -0700 | [diff] [blame] | 58 | lib.SetReportOptions(report_lib_options) |
Yabin Cui | fd4adae | 2021-10-25 13:55:01 -0700 | [diff] [blame] | 59 | |
| 60 | stacks: DefaultDict[str, int] = defaultdict(int) |
| 61 | event_defaulted = False |
| 62 | event_warning_shown = False |
| 63 | while True: |
| 64 | sample = lib.GetNextSample() |
| 65 | if sample is None: |
| 66 | lib.Close() |
| 67 | break |
Yabin Cui | fd4adae | 2021-10-25 13:55:01 -0700 | [diff] [blame] | 68 | event = lib.GetEventOfCurrentSample() |
| 69 | symbol = lib.GetSymbolOfCurrentSample() |
| 70 | callchain = lib.GetCallChainOfCurrentSample() |
| 71 | if not event_filter: |
| 72 | event_filter = event.name |
| 73 | event_defaulted = True |
| 74 | elif event.name != event_filter: |
| 75 | if event_defaulted and not event_warning_shown: |
| 76 | logging.warning( |
Yabin Cui | 61a61ca | 2022-01-26 16:27:26 -0800 | [diff] [blame] | 77 | 'Input has multiple event types. Filtering for the first event type seen: %s' % |
| 78 | event_filter) |
Yabin Cui | fd4adae | 2021-10-25 13:55:01 -0700 | [diff] [blame] | 79 | event_warning_shown = True |
| 80 | continue |
| 81 | |
| 82 | stack = [] |
| 83 | for i in range(callchain.nr): |
| 84 | entry = callchain.entries[i] |
| 85 | func = entry.symbol.symbol_name |
| 86 | if annotate_kernel and "kallsyms" in entry.symbol.dso_name or ".ko" in entry.symbol.dso_name: |
| 87 | func += '_[k]' # kernel |
| 88 | if annotate_jit and entry.symbol.dso_name == "[JIT app cache]": |
| 89 | func += '_[j]' # jit |
| 90 | stack.append(func) |
| 91 | if include_tid: |
| 92 | stack.append("%s-%d/%d" % (sample.thread_comm, sample.pid, sample.tid)) |
| 93 | elif include_pid: |
| 94 | stack.append("%s-%d" % (sample.thread_comm, sample.pid)) |
| 95 | else: |
| 96 | stack.append(sample.thread_comm) |
| 97 | stack.reverse() |
| 98 | stacks[";".join(stack)] += sample.period |
| 99 | |
| 100 | for k in sorted(stacks.keys()): |
| 101 | print("%s %d" % (k, stacks[k])) |
| 102 | |
| 103 | |
| 104 | def main(): |
| 105 | parser = BaseArgumentParser(description=__doc__) |
| 106 | parser.add_argument('--symfs', |
| 107 | help='Set the path to find binaries with symbols and debug info.') |
| 108 | parser.add_argument('--kallsyms', help='Set the path to find kernel symbols.') |
| 109 | parser.add_argument('-i', '--record_file', nargs='?', default='perf.data', |
| 110 | help='Default is perf.data.') |
Yabin Cui | fd4adae | 2021-10-25 13:55:01 -0700 | [diff] [blame] | 111 | parser.add_argument('--pid', action='store_true', help='Include PID with process names') |
| 112 | parser.add_argument('--tid', action='store_true', help='Include TID and PID with process names') |
| 113 | parser.add_argument('--kernel', action='store_true', |
| 114 | help='Annotate kernel functions with a _[k]') |
| 115 | parser.add_argument('--jit', action='store_true', help='Annotate JIT functions with a _[j]') |
| 116 | parser.add_argument('--addrs', action='store_true', |
| 117 | help='include raw addresses where symbols can\'t be found') |
Yabin Cui | 61a61ca | 2022-01-26 16:27:26 -0800 | [diff] [blame] | 118 | sample_filter_group = parser.add_argument_group('Sample filter options') |
Yabin Cui | 61a61ca | 2022-01-26 16:27:26 -0800 | [diff] [blame] | 119 | sample_filter_group.add_argument('--event-filter', nargs='?', default='', |
| 120 | help='Event type filter e.g. "cpu-cycles" or "instructions"') |
Yabin Cui | ab19bc5 | 2022-03-25 11:14:57 -0700 | [diff] [blame] | 121 | parser.add_report_lib_options(sample_filter_group=sample_filter_group, |
| 122 | sample_filter_with_pid_shortcut=False) |
Yabin Cui | fd4adae | 2021-10-25 13:55:01 -0700 | [diff] [blame] | 123 | args = parser.parse_args() |
| 124 | collapse_stacks( |
| 125 | record_file=args.record_file, |
| 126 | symfs_dir=args.symfs, |
| 127 | kallsyms_file=args.kallsyms, |
Yabin Cui | fd4adae | 2021-10-25 13:55:01 -0700 | [diff] [blame] | 128 | event_filter=args.event_filter, |
| 129 | include_pid=args.pid, |
| 130 | include_tid=args.tid, |
| 131 | annotate_kernel=args.kernel, |
| 132 | annotate_jit=args.jit, |
| 133 | include_addrs=args.addrs, |
Yabin Cui | ab19bc5 | 2022-03-25 11:14:57 -0700 | [diff] [blame] | 134 | report_lib_options=args.report_lib_options) |
Yabin Cui | fd4adae | 2021-10-25 13:55:01 -0700 | [diff] [blame] | 135 | |
| 136 | |
| 137 | if __name__ == '__main__': |
| 138 | main() |