blob: b010d1a629ae0eb525835bbd397dc779882d6dd8 [file] [log] [blame]
#!/usr/bin/env python3
# Copyright (C) 2020 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""This script annotates a CFG file with profiling information from simpleperf
record files.
Example:
perf2cfg --cfg bench.cfg --perf-data perf.data
"""
import argparse
import logging
import os
import sys
import textwrap
from perf2cfg import analyze
from perf2cfg import edit
def parse_arguments() -> argparse.Namespace:
"""Parses program arguments.
Returns:
argparse.Namespace: A populated argument namespace.
"""
parser = argparse.ArgumentParser(
# Hardcode the usage string as argparse does not display long options
# if short ones are specified
usage=textwrap.dedent("""\
perf2cfg [-h|--help] --cfg CFG --perf-data PERF_DATA [PERF_DATA ...]
[--output-file OUTPUT_FILE] [-e|--events EVENTS]
[--primary-event PRIMARY_EVENT]"""),
description='Annotates a CFG file with profiling information from '
'simpleperf data files.',
add_help=False)
required = parser.add_argument_group('required arguments')
required.add_argument('--cfg',
required=True,
help='The CFG file to annotate.')
required.add_argument(
'--perf-data',
nargs='+',
required=True,
help='The perf data files to extract information from.')
parser.add_argument('-h',
'--help',
action='help',
default=argparse.SUPPRESS,
help='Show this help message and exit.')
parser.add_argument('--output-file', help='A path to the output CFG file.')
parser.add_argument(
'-e',
'--events',
type=lambda events: events.split(',') if events else [],
help='A comma-separated list of events only to use for annotating a '
'CFG (default: use all events found in perf data). An error is '
'reported if the events are not present in perf data.')
parser.add_argument(
'--primary-event',
default='cpu-cycles',
help='The event to be used for basic blocks hotness analysis '
'(default: %(default)s). Basic blocks are color highlighted according '
'to their hotness. An error is reported if the primary event is not '
'present in perf data.')
args = parser.parse_args()
if not args.output_file:
root, ext = os.path.splitext(args.cfg)
args.output_file = f'{root}-annotated{ext}'
return args
def analyze_record_files(args: argparse.Namespace) -> analyze.RecordAnalyzer:
"""Analyzes simpleperf record files.
Args:
args (argparse.Namespace): An argument namespace.
Returns:
analyze.RecordAnalyzer: A RecordAnalyzer object.
"""
analyzer = analyze.RecordAnalyzer(args.events)
for record_file in args.perf_data:
analyzer.analyze(record_file)
return analyzer
def validate_events(analyzer: analyze.RecordAnalyzer,
args: argparse.Namespace) -> None:
"""Validates event names given on the command line.
Args:
analyzer (analyze.RecordAnalyzer): A RecordAnalyzer object.
args (argparse.Namespace): An argument namespace.
"""
if not analyzer.event_counts:
logging.error('The selected events are not present in perf data')
sys.exit(1)
if args.primary_event not in analyzer.event_counts:
logging.error(
'The selected primary event %s is not present in perf data',
args.primary_event)
sys.exit(1)
def annotate_cfg_file(analyzer: analyze.RecordAnalyzer,
args: argparse.Namespace) -> None:
"""Annotates a CFG file.
Args:
analyzer (analyze.RecordAnalyzer): A RecordAnalyzer object.
args (argparse.Namespace): An argument namespace.
"""
input_stream = open(args.cfg, 'r')
output_stream = open(args.output_file, 'w')
editor = edit.CfgEditor(analyzer, input_stream, output_stream,
args.primary_event)
editor.edit()
input_stream.close()
output_stream.close()
def main() -> None:
"""Annotates a CFG file with information from simpleperf record files."""
args = parse_arguments()
analyzer = analyze_record_files(args)
validate_events(analyzer, args)
annotate_cfg_file(analyzer, args)
if __name__ == '__main__':
main()