blob: 78899f71930fe811cb213a61ec570a8a565e0b8f [file]
# Copyright 2026 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Query executors for perf-analyze."""
import json
import os
from typing import Any
from tp_shell import PerfettoTraceProcessor
class TpShell:
"""Runs raw SQL queries via Perfetto TraceProcessor."""
def query(
self,
trace_path: str,
sql: str | None = None,
batch: str | None = None,
cache: bool = True,
) -> list[dict[str, Any]]:
"""Executes the query specified by args on the given trace.
Args:
trace_path: Path to the trace file to ingest.
sql: Optional raw SQL query string to execute.
batch: Optional JSON string or @filepath of batch queries to execute.
cache: If True, caches trace locally when downloading (default: True).
Returns:
A list of dictionary results representing the query output.
"""
if not sql and not batch:
raise ValueError("Either sql or batch must be provided")
with PerfettoTraceProcessor(trace_path, cache=cache) as tp:
if sql:
return tp.run_query(sql)
elif batch:
batch_data = self._load_batch(batch)
results: list[dict[str, Any]] = []
for item in batch_data:
if not isinstance(item, dict):
raise ValueError("Batch items must be JSON objects")
q_name = item.get("name")
q_sql = item.get("sql")
if not q_name or not q_sql:
raise ValueError(
"Batch items must contain 'name' and 'sql'"
)
try:
q_results = tp.run_query(q_sql)
results.append({"name": q_name, "results": q_results})
except Exception as e:
results.append({"name": q_name, "error": str(e)})
return results
return []
def _load_batch(self, batch_arg: str) -> list[dict[str, Any]]:
"""Loads batch queries from JSON or file path starting with '@'."""
if batch_arg.startswith("@"):
file_path = batch_arg[1:]
if not os.path.exists(file_path):
raise FileNotFoundError(f"Batch file not found: {file_path}")
with open(file_path, "r", encoding="utf-8") as f:
content = f.read()
else:
content = batch_arg
try:
data = json.loads(content)
if not isinstance(data, list):
raise ValueError("Batch content must be a JSON array")
return data
except json.JSONDecodeError as e:
raise ValueError(f"Invalid JSON in batch: {e}")