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