1#!/usr/bin/env python3 2# 3# Copyright (C) 2024 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17 18"""sample_filter.py: generate sample filter files, which can be passed in the 19 --filter-file option when reporting. 20 21Example: 22 ./sample_filter.py -i perf.data --split-time-range 2 -o sample_filter 23 ./gecko_profile_generator.py -i perf.data --filter-file sample_filter_part1 \ 24 | gzip >profile-part1.json.gz 25 ./gecko_profile_generator.py -i perf.data --filter-file sample_filter_part2 \ 26 | gzip >profile-part2.json.gz 27""" 28 29import logging 30from simpleperf_report_lib import ReportLib 31from simpleperf_utils import BaseArgumentParser 32from typing import Tuple 33 34 35class RecordFileReader: 36 def __init__(self, record_file: str): 37 self.record_file = record_file 38 39 def get_time_range(self) -> Tuple[int, int]: 40 """ Return a tuple of (min_timestamp, max_timestamp). """ 41 min_timestamp = 0 42 max_timestamp = 0 43 lib = ReportLib() 44 lib.SetRecordFile(self.record_file) 45 while True: 46 sample = lib.GetNextSample() 47 if not sample: 48 break 49 if not min_timestamp or sample.time < min_timestamp: 50 min_timestamp = sample.time 51 if not max_timestamp or sample.time > max_timestamp: 52 max_timestamp = sample.time 53 lib.Close() 54 return (min_timestamp, max_timestamp) 55 56 57def show_time_range(record_file: str) -> None: 58 reader = RecordFileReader(record_file) 59 time_range = reader.get_time_range() 60 print('time range of samples is %.3f s' % ((time_range[1] - time_range[0]) / 1e9)) 61 62 63def filter_samples( 64 record_file: str, split_time_range: int, exclude_first_seconds: int, 65 exclude_last_seconds: int, output_file_prefix: str) -> None: 66 reader = RecordFileReader(record_file) 67 min_timestamp, max_timestamp = reader.get_time_range() 68 comment = 'total time range: %d seconds' % ((max_timestamp - min_timestamp) // 1e9) 69 if exclude_first_seconds: 70 min_timestamp += int(exclude_first_seconds * 1e9) 71 comment += ', exclude first %d seconds' % exclude_first_seconds 72 if exclude_last_seconds: 73 max_timestamp -= int(exclude_last_seconds * 1e9) 74 comment += ', exclude last %d seconds' % exclude_last_seconds 75 if min_timestamp > max_timestamp: 76 logging.error('All samples are filtered out') 77 return 78 if not split_time_range: 79 output_file = output_file_prefix 80 with open(output_file, 'w') as fh: 81 fh.write('// %s\n' % comment) 82 fh.write('GLOBAL_BEGIN %d\n' % min_timestamp) 83 fh.write('GLOBAL_END %d\n' % max_timestamp) 84 print('Generate sample filter file: %s' % output_file) 85 else: 86 step = (max_timestamp - min_timestamp) // split_time_range 87 cur_timestamp = min_timestamp 88 for i in range(split_time_range): 89 output_file = output_file_prefix + '_part%s' % (i + 1) 90 with open(output_file, 'w') as fh: 91 time_range_comment = 'current range: %d to %d seconds' % ( 92 (cur_timestamp - min_timestamp) // 1e9, 93 (cur_timestamp + step - min_timestamp) // 1e9) 94 fh.write('// %s, %s\n' % (comment, time_range_comment)) 95 fh.write('GLOBAL_BEGIN %d\n' % cur_timestamp) 96 if i == split_time_range - 1: 97 cur_timestamp = max_timestamp 98 else: 99 cur_timestamp += step 100 fh.write('GLOBAL_END %d\n' % (cur_timestamp + 1)) 101 cur_timestamp += 1 102 print('Generate sample filter file: %s' % output_file) 103 104 105def main(): 106 parser = BaseArgumentParser(description=__doc__) 107 parser.add_argument('-i', '--record-file', nargs='?', default='perf.data', 108 help='Default is perf.data.') 109 parser.add_argument('--show-time-range', action='store_true', help='show time range of samples') 110 parser.add_argument('--split-time-range', type=int, 111 help='split time ranges of samples into several parts') 112 parser.add_argument('--exclude-first-seconds', type=int, 113 help='exclude samples recorded in the first seconds') 114 parser.add_argument('--exclude-last-seconds', type=int, 115 help='exclude samples recorded in the last seconds') 116 parser.add_argument( 117 '-o', '--output-file-prefix', default='sample_filter', 118 help='prefix for the generated sample filter files') 119 args = parser.parse_args() 120 121 if args.show_time_range: 122 show_time_range(args.record_file) 123 124 if args.split_time_range or args.exclude_first_seconds or args.exclude_last_seconds: 125 filter_samples(args.record_file, args.split_time_range, args.exclude_first_seconds, 126 args.exclude_last_seconds, args.output_file_prefix) 127 128 129if __name__ == '__main__': 130 main() 131