xref: /aosp_15_r20/external/perfetto/python/tools/batch_trace_processor_shell.py (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1#!/usr/bin/env python3
2# Copyright (C) 2021 The Android Open Source Project
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8#      http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15""" Given a trace file, gives the self-time of userspace slices broken
16down by process, thread and thread state.
17"""
18
19import argparse
20import cmd
21import logging
22import numpy as np
23import pandas as pd
24import plotille
25
26from perfetto.batch_trace_processor.api import BatchTraceProcessor, BatchTraceProcessorConfig
27from perfetto.trace_processor import TraceProcessorException, TraceProcessorConfig
28from typing import List
29
30
31class TpBatchShell(cmd.Cmd):
32
33  def __init__(self, files: List[str], batch_tp: BatchTraceProcessor):
34    super().__init__()
35    self.files = files
36    self.batch_tp = batch_tp
37
38  def do_table(self, arg: str):
39    try:
40      data = self.batch_tp.query_and_flatten(arg)
41      print(data)
42    except TraceProcessorException as ex:
43      logging.error("Query failed: {}".format(ex))
44
45  def do_histogram(self, arg: str):
46    try:
47      data = self.batch_tp.query_single_result(arg)
48      print(plotille.histogram(data))
49      self.print_percentiles(data)
50    except TraceProcessorException as ex:
51      logging.error("Query failed: {}".format(ex))
52
53  def do_vhistogram(self, arg: str):
54    try:
55      data = self.batch_tp.query_single_result(arg)
56      print(plotille.hist(data))
57      self.print_percentiles(data)
58    except TraceProcessorException as ex:
59      logging.error("Query failed: {}".format(ex))
60
61  def do_count(self, arg: str):
62    try:
63      data = self.batch_tp.query_single_result(arg)
64      counts = dict()
65      for i in data:
66        counts[i] = counts.get(i, 0) + 1
67      print(counts)
68    except TraceProcessorException as ex:
69      logging.error("Query failed: {}".format(ex))
70
71  def do_close(self, _):
72    return True
73
74  def do_quit(self, _):
75    return True
76
77  def do_EOF(self, _):
78    print("")
79    return True
80
81  def print_percentiles(self, data):
82    percentiles = [25, 50, 75, 95, 99, 99.9]
83    nearest = np.percentile(data, percentiles, interpolation='nearest')
84    logging.info("Representative traces for percentiles")
85    for i, near in enumerate(nearest):
86      print("{}%: {}".format(percentiles[i], self.files[data.index(near)]))
87
88
89def main():
90  parser = argparse.ArgumentParser()
91  parser.add_argument('--shell-path', default=None)
92  parser.add_argument('--verbose', action='store_true', default=False)
93  parser.add_argument('--file-list', default=None)
94  parser.add_argument('--query-file', default=None)
95  parser.add_argument('--interactive', default=None)
96  parser.add_argument('files', nargs='*')
97  args = parser.parse_args()
98
99  logging.basicConfig(level=logging.DEBUG)
100
101  files = args.files
102  if args.file_list:
103    with open(args.file_list, 'r') as f:
104      files += f.read().splitlines()
105
106  if not files:
107    logging.info("At least one file must be specified in files or file list")
108
109  logging.info('Loading traces...')
110  config = BatchTraceProcessorConfig(
111      tp_config=TraceProcessorConfig(
112          bin_path=args.shell_path,
113          verbose=args.verbose,
114      ))
115
116  with BatchTraceProcessor(files, config) as batch_tp:
117    if args.query_file:
118      logging.info('Running query file...')
119
120      with open(args.query_file, 'r') as f:
121        queries_str = f.read()
122
123      queries = [q.strip() for q in queries_str.split(";\n")]
124      for q in queries[:-1]:
125        batch_tp.query(q)
126
127      res = batch_tp.query_and_flatten(queries[-1])
128      print(res.to_csv(index=False))
129
130    if args.interactive or not args.query_file:
131      try:
132        TpBatchShell(files, batch_tp).cmdloop()
133      except KeyboardInterrupt:
134        pass
135
136    logging.info("Closing; please wait...")
137
138
139if __name__ == '__main__':
140  exit(main())
141