blob: 12426deceae66f1c3b3df5020beaba0989c992b8 [file] [log] [blame]
# Copyright 2025 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import os
import pandas as pd
import io
import contextlib
import asyncio
import signal
from jinja2 import Template
from contextlib import ExitStack
import tempfile
from . import command_line
class TraceFile:
"""A class to represent and query a recorded trace file."""
def __init__(self, trace_file):
"""Initializes the TraceFile object with the path to the trace file.
Args:
trace_file: The path to the recorded trace file.
"""
self.trace_file = trace_file
async def query(self, query):
"""Queries the trace file using the trace_processor tool.
Args:
query: The SQL query string to execute on the trace.
Returns:
The result of the query as a pandas dataframe.
"""
result = await command_line.run(
"third_party/perfetto/tools/trace_processor", self.trace_file,
"-Q", query)
# Use io.StringIO to treat the string as a file and read it with pandas.
return pd.read_csv(io.StringIO(result.stdout), na_values=['[NULL]'])
@contextlib.asynccontextmanager
async def record(self, config_file):
"""Records a trace from the configured device using the specified
config.
Args:
config_file: The config file to use for recording.
"""
recording_task = asyncio.create_task(
command_line.run("third_party/perfetto/tools/record_android_trace",
"-o",
self.trace_file,
"-c",
config_file,
"-n",
interruption_signal=signal.SIGINT))
if recording_task.done():
Exception(f"Recording failed: {recording_task.result()}")
try:
yield
finally:
recording_task.cancel()
try:
await recording_task
except asyncio.CancelledError:
pass
@contextlib.contextmanager
def histograms_trace_config(histograms, duration=10000):
"""
Create a temporary trace configuration file for recording the histogram(s)
listed in `histograms`.
Args:
histograms: A string or a list of strings with the name of the
histogram(s) to record.
duration: The duration of the trace.
Returns:
The path to the temporary trace configuration file.
"""
if isinstance(histograms, str):
histograms = [histograms]
elif not isinstance(histograms, list):
raise TypeError("histograms must be a string or a list of strings")
# This assumes the current working directory is the chromium src root.
jinja_file = os.path.join(os.getcwd(), "tools", "android", "colabutils",
"res", "histogram_trace_cfg.pbtxt.j2")
with open(jinja_file, 'r') as f:
template_content = f.read()
template = Template(template_content)
rendered_config = template.render(histogram_names=histograms,
duration=duration)
with tempfile.NamedTemporaryFile(mode='w') as temporary_config_file:
temporary_config_file.write(rendered_config)
temporary_config_file.flush()
yield temporary_config_file.name
def histogram_values_query(histogram):
"""
Create a perfetto trace query to obtain all recorded values of a histogram.
Args:
histogram: The name of the histogram to query.
Returns:
A string containing the query.
"""
jinja_file = os.path.join(os.getcwd(), "tools", "android", "colabutils",
"res", "histogram_query.j2")
with open(jinja_file, 'r') as f:
template_content = f.read()
template = Template(template_content)
rendered_config = template.render(histogram_name=histogram)
return rendered_config