141 lines
4.1 KiB
Python
141 lines
4.1 KiB
Python
#!/usr/bin/env python3
|
|
# Copyright (C) 2021 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.
|
|
""" Given a trace file, gives the self-time of userspace slices broken
|
|
down by process, thread and thread state.
|
|
"""
|
|
|
|
import argparse
|
|
import cmd
|
|
import logging
|
|
import numpy as np
|
|
import pandas as pd
|
|
import plotille
|
|
|
|
from perfetto.batch_trace_processor.api import BatchTraceProcessor, BatchTraceProcessorConfig
|
|
from perfetto.trace_processor import TraceProcessorException, TraceProcessorConfig
|
|
from typing import List
|
|
|
|
|
|
class TpBatchShell(cmd.Cmd):
|
|
|
|
def __init__(self, files: List[str], batch_tp: BatchTraceProcessor):
|
|
super().__init__()
|
|
self.files = files
|
|
self.batch_tp = batch_tp
|
|
|
|
def do_table(self, arg: str):
|
|
try:
|
|
data = self.batch_tp.query_and_flatten(arg)
|
|
print(data)
|
|
except TraceProcessorException as ex:
|
|
logging.error("Query failed: {}".format(ex))
|
|
|
|
def do_histogram(self, arg: str):
|
|
try:
|
|
data = self.batch_tp.query_single_result(arg)
|
|
print(plotille.histogram(data))
|
|
self.print_percentiles(data)
|
|
except TraceProcessorException as ex:
|
|
logging.error("Query failed: {}".format(ex))
|
|
|
|
def do_vhistogram(self, arg: str):
|
|
try:
|
|
data = self.batch_tp.query_single_result(arg)
|
|
print(plotille.hist(data))
|
|
self.print_percentiles(data)
|
|
except TraceProcessorException as ex:
|
|
logging.error("Query failed: {}".format(ex))
|
|
|
|
def do_count(self, arg: str):
|
|
try:
|
|
data = self.batch_tp.query_single_result(arg)
|
|
counts = dict()
|
|
for i in data:
|
|
counts[i] = counts.get(i, 0) + 1
|
|
print(counts)
|
|
except TraceProcessorException as ex:
|
|
logging.error("Query failed: {}".format(ex))
|
|
|
|
def do_close(self, _):
|
|
return True
|
|
|
|
def do_quit(self, _):
|
|
return True
|
|
|
|
def do_EOF(self, _):
|
|
print("")
|
|
return True
|
|
|
|
def print_percentiles(self, data):
|
|
percentiles = [25, 50, 75, 95, 99, 99.9]
|
|
nearest = np.percentile(data, percentiles, interpolation='nearest')
|
|
logging.info("Representative traces for percentiles")
|
|
for i, near in enumerate(nearest):
|
|
print("{}%: {}".format(percentiles[i], self.files[data.index(near)]))
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument('--shell-path', default=None)
|
|
parser.add_argument('--verbose', action='store_true', default=False)
|
|
parser.add_argument('--file-list', default=None)
|
|
parser.add_argument('--query-file', default=None)
|
|
parser.add_argument('--interactive', default=None)
|
|
parser.add_argument('files', nargs='*')
|
|
args = parser.parse_args()
|
|
|
|
logging.basicConfig(level=logging.DEBUG)
|
|
|
|
files = args.files
|
|
if args.file_list:
|
|
with open(args.file_list, 'r') as f:
|
|
files += f.read().splitlines()
|
|
|
|
if not files:
|
|
logging.info("At least one file must be specified in files or file list")
|
|
|
|
logging.info('Loading traces...')
|
|
config = BatchTraceProcessorConfig(
|
|
tp_config=TraceProcessorConfig(
|
|
bin_path=args.shell_path,
|
|
verbose=args.verbose,
|
|
))
|
|
|
|
with BatchTraceProcessor(files, config) as batch_tp:
|
|
if args.query_file:
|
|
logging.info('Running query file...')
|
|
|
|
with open(args.query_file, 'r') as f:
|
|
queries_str = f.read()
|
|
|
|
queries = [q.strip() for q in queries_str.split(";\n")]
|
|
for q in queries[:-1]:
|
|
batch_tp.query(q)
|
|
|
|
res = batch_tp.query_and_flatten(queries[-1])
|
|
print(res.to_csv(index=False))
|
|
|
|
if args.interactive or not args.query_file:
|
|
try:
|
|
TpBatchShell(files, batch_tp).cmdloop()
|
|
except KeyboardInterrupt:
|
|
pass
|
|
|
|
logging.info("Closing; please wait...")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
exit(main())
|