xref: /aosp_15_r20/system/extras/perf2cfg/perf2cfg.py (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
1*288bf522SAndroid Build Coastguard Worker#!/usr/bin/env python3
2*288bf522SAndroid Build Coastguard Worker# Copyright (C) 2020 The Android Open Source Project
3*288bf522SAndroid Build Coastguard Worker#
4*288bf522SAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
5*288bf522SAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
6*288bf522SAndroid Build Coastguard Worker# You may obtain a copy of the License at
7*288bf522SAndroid Build Coastguard Worker#
8*288bf522SAndroid Build Coastguard Worker#   http://www.apache.org/licenses/LICENSE-2.0
9*288bf522SAndroid Build Coastguard Worker#
10*288bf522SAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
11*288bf522SAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
12*288bf522SAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*288bf522SAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
14*288bf522SAndroid Build Coastguard Worker# limitations under the License.
15*288bf522SAndroid Build Coastguard Worker"""This script annotates a CFG file with profiling information from simpleperf
16*288bf522SAndroid Build Coastguard Workerrecord files.
17*288bf522SAndroid Build Coastguard Worker
18*288bf522SAndroid Build Coastguard WorkerExample:
19*288bf522SAndroid Build Coastguard Worker    perf2cfg --cfg bench.cfg --perf-data perf.data
20*288bf522SAndroid Build Coastguard Worker"""
21*288bf522SAndroid Build Coastguard Worker
22*288bf522SAndroid Build Coastguard Workerimport argparse
23*288bf522SAndroid Build Coastguard Workerimport logging
24*288bf522SAndroid Build Coastguard Workerimport os
25*288bf522SAndroid Build Coastguard Workerimport sys
26*288bf522SAndroid Build Coastguard Workerimport textwrap
27*288bf522SAndroid Build Coastguard Worker
28*288bf522SAndroid Build Coastguard Workerfrom perf2cfg import analyze
29*288bf522SAndroid Build Coastguard Workerfrom perf2cfg import edit
30*288bf522SAndroid Build Coastguard Worker
31*288bf522SAndroid Build Coastguard Worker
32*288bf522SAndroid Build Coastguard Workerdef parse_arguments() -> argparse.Namespace:
33*288bf522SAndroid Build Coastguard Worker    """Parses program arguments.
34*288bf522SAndroid Build Coastguard Worker
35*288bf522SAndroid Build Coastguard Worker    Returns:
36*288bf522SAndroid Build Coastguard Worker        argparse.Namespace: A populated argument namespace.
37*288bf522SAndroid Build Coastguard Worker    """
38*288bf522SAndroid Build Coastguard Worker    parser = argparse.ArgumentParser(
39*288bf522SAndroid Build Coastguard Worker        # Hardcode the usage string as argparse does not display long options
40*288bf522SAndroid Build Coastguard Worker        # if short ones are specified
41*288bf522SAndroid Build Coastguard Worker        usage=textwrap.dedent("""\
42*288bf522SAndroid Build Coastguard Worker        perf2cfg [-h|--help] --cfg CFG --perf-data PERF_DATA [PERF_DATA ...]
43*288bf522SAndroid Build Coastguard Worker                        [--output-file OUTPUT_FILE] [-e|--events EVENTS]
44*288bf522SAndroid Build Coastguard Worker                        [--primary-event PRIMARY_EVENT]"""),
45*288bf522SAndroid Build Coastguard Worker        description='Annotates a CFG file with profiling information from '
46*288bf522SAndroid Build Coastguard Worker        'simpleperf data files.',
47*288bf522SAndroid Build Coastguard Worker        add_help=False)
48*288bf522SAndroid Build Coastguard Worker    required = parser.add_argument_group('required arguments')
49*288bf522SAndroid Build Coastguard Worker    required.add_argument('--cfg',
50*288bf522SAndroid Build Coastguard Worker                          required=True,
51*288bf522SAndroid Build Coastguard Worker                          help='The CFG file to annotate.')
52*288bf522SAndroid Build Coastguard Worker    required.add_argument(
53*288bf522SAndroid Build Coastguard Worker        '--perf-data',
54*288bf522SAndroid Build Coastguard Worker        nargs='+',
55*288bf522SAndroid Build Coastguard Worker        required=True,
56*288bf522SAndroid Build Coastguard Worker        help='The perf data files to extract information from.')
57*288bf522SAndroid Build Coastguard Worker    parser.add_argument('-h',
58*288bf522SAndroid Build Coastguard Worker                        '--help',
59*288bf522SAndroid Build Coastguard Worker                        action='help',
60*288bf522SAndroid Build Coastguard Worker                        default=argparse.SUPPRESS,
61*288bf522SAndroid Build Coastguard Worker                        help='Show this help message and exit.')
62*288bf522SAndroid Build Coastguard Worker    parser.add_argument('--output-file', help='A path to the output CFG file.')
63*288bf522SAndroid Build Coastguard Worker    parser.add_argument(
64*288bf522SAndroid Build Coastguard Worker        '-e',
65*288bf522SAndroid Build Coastguard Worker        '--events',
66*288bf522SAndroid Build Coastguard Worker        type=lambda events: events.split(',') if events else [],
67*288bf522SAndroid Build Coastguard Worker        help='A comma-separated list of events only to use for annotating a '
68*288bf522SAndroid Build Coastguard Worker        'CFG (default: use all events found in perf data). An error is '
69*288bf522SAndroid Build Coastguard Worker        'reported if the events are not present in perf data.')
70*288bf522SAndroid Build Coastguard Worker    parser.add_argument(
71*288bf522SAndroid Build Coastguard Worker        '--primary-event',
72*288bf522SAndroid Build Coastguard Worker        default='cpu-cycles',
73*288bf522SAndroid Build Coastguard Worker        help='The event to be used for basic blocks hotness analysis '
74*288bf522SAndroid Build Coastguard Worker        '(default: %(default)s). Basic blocks are color highlighted according '
75*288bf522SAndroid Build Coastguard Worker        'to their hotness. An error is reported if the primary event is not '
76*288bf522SAndroid Build Coastguard Worker        'present in perf data.')
77*288bf522SAndroid Build Coastguard Worker    args = parser.parse_args()
78*288bf522SAndroid Build Coastguard Worker
79*288bf522SAndroid Build Coastguard Worker    if not args.output_file:
80*288bf522SAndroid Build Coastguard Worker        root, ext = os.path.splitext(args.cfg)
81*288bf522SAndroid Build Coastguard Worker        args.output_file = f'{root}-annotated{ext}'
82*288bf522SAndroid Build Coastguard Worker
83*288bf522SAndroid Build Coastguard Worker    return args
84*288bf522SAndroid Build Coastguard Worker
85*288bf522SAndroid Build Coastguard Worker
86*288bf522SAndroid Build Coastguard Workerdef analyze_record_files(args: argparse.Namespace) -> analyze.RecordAnalyzer:
87*288bf522SAndroid Build Coastguard Worker    """Analyzes simpleperf record files.
88*288bf522SAndroid Build Coastguard Worker
89*288bf522SAndroid Build Coastguard Worker    Args:
90*288bf522SAndroid Build Coastguard Worker        args (argparse.Namespace): An argument namespace.
91*288bf522SAndroid Build Coastguard Worker
92*288bf522SAndroid Build Coastguard Worker    Returns:
93*288bf522SAndroid Build Coastguard Worker        analyze.RecordAnalyzer: A RecordAnalyzer object.
94*288bf522SAndroid Build Coastguard Worker    """
95*288bf522SAndroid Build Coastguard Worker    analyzer = analyze.RecordAnalyzer(args.events)
96*288bf522SAndroid Build Coastguard Worker    for record_file in args.perf_data:
97*288bf522SAndroid Build Coastguard Worker        analyzer.analyze(record_file)
98*288bf522SAndroid Build Coastguard Worker
99*288bf522SAndroid Build Coastguard Worker    return analyzer
100*288bf522SAndroid Build Coastguard Worker
101*288bf522SAndroid Build Coastguard Worker
102*288bf522SAndroid Build Coastguard Workerdef validate_events(analyzer: analyze.RecordAnalyzer,
103*288bf522SAndroid Build Coastguard Worker                    args: argparse.Namespace) -> None:
104*288bf522SAndroid Build Coastguard Worker    """Validates event names given on the command line.
105*288bf522SAndroid Build Coastguard Worker
106*288bf522SAndroid Build Coastguard Worker    Args:
107*288bf522SAndroid Build Coastguard Worker        analyzer (analyze.RecordAnalyzer): A RecordAnalyzer object.
108*288bf522SAndroid Build Coastguard Worker        args (argparse.Namespace): An argument namespace.
109*288bf522SAndroid Build Coastguard Worker    """
110*288bf522SAndroid Build Coastguard Worker    if not analyzer.event_counts:
111*288bf522SAndroid Build Coastguard Worker        logging.error('The selected events are not present in perf data')
112*288bf522SAndroid Build Coastguard Worker        sys.exit(1)
113*288bf522SAndroid Build Coastguard Worker
114*288bf522SAndroid Build Coastguard Worker    if args.primary_event not in analyzer.event_counts:
115*288bf522SAndroid Build Coastguard Worker        logging.error(
116*288bf522SAndroid Build Coastguard Worker            'The selected primary event %s is not present in perf data',
117*288bf522SAndroid Build Coastguard Worker            args.primary_event)
118*288bf522SAndroid Build Coastguard Worker        sys.exit(1)
119*288bf522SAndroid Build Coastguard Worker
120*288bf522SAndroid Build Coastguard Worker
121*288bf522SAndroid Build Coastguard Workerdef annotate_cfg_file(analyzer: analyze.RecordAnalyzer,
122*288bf522SAndroid Build Coastguard Worker                      args: argparse.Namespace) -> None:
123*288bf522SAndroid Build Coastguard Worker    """Annotates a CFG file.
124*288bf522SAndroid Build Coastguard Worker
125*288bf522SAndroid Build Coastguard Worker    Args:
126*288bf522SAndroid Build Coastguard Worker        analyzer (analyze.RecordAnalyzer): A RecordAnalyzer object.
127*288bf522SAndroid Build Coastguard Worker        args (argparse.Namespace): An argument namespace.
128*288bf522SAndroid Build Coastguard Worker    """
129*288bf522SAndroid Build Coastguard Worker    input_stream = open(args.cfg, 'r')
130*288bf522SAndroid Build Coastguard Worker    output_stream = open(args.output_file, 'w')
131*288bf522SAndroid Build Coastguard Worker
132*288bf522SAndroid Build Coastguard Worker    editor = edit.CfgEditor(analyzer, input_stream, output_stream,
133*288bf522SAndroid Build Coastguard Worker                            args.primary_event)
134*288bf522SAndroid Build Coastguard Worker    editor.edit()
135*288bf522SAndroid Build Coastguard Worker
136*288bf522SAndroid Build Coastguard Worker    input_stream.close()
137*288bf522SAndroid Build Coastguard Worker    output_stream.close()
138*288bf522SAndroid Build Coastguard Worker
139*288bf522SAndroid Build Coastguard Worker
140*288bf522SAndroid Build Coastguard Workerdef main() -> None:
141*288bf522SAndroid Build Coastguard Worker    """Annotates a CFG file with information from simpleperf record files."""
142*288bf522SAndroid Build Coastguard Worker    args = parse_arguments()
143*288bf522SAndroid Build Coastguard Worker    analyzer = analyze_record_files(args)
144*288bf522SAndroid Build Coastguard Worker    validate_events(analyzer, args)
145*288bf522SAndroid Build Coastguard Worker    annotate_cfg_file(analyzer, args)
146*288bf522SAndroid Build Coastguard Worker
147*288bf522SAndroid Build Coastguard Worker
148*288bf522SAndroid Build Coastguard Workerif __name__ == '__main__':
149*288bf522SAndroid Build Coastguard Worker    main()
150