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