xref: /aosp_15_r20/system/extras/simpleperf/scripts/sample_filter.py (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
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