You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
150 lines
4.9 KiB
150 lines
4.9 KiB
#!/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()
|