Source code for bundle.perf_report.extractor.tracy

# Copyright 2026 HorusElohim

# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you 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.

"""Tracy CSV profiling data extraction."""

from __future__ import annotations

import csv
from dataclasses import dataclass, field
from pathlib import Path


[docs] @dataclass class ProfileRecord: """A single zone entry from a Tracy CSV export (tracy-csvexport).""" name: str src_file: str src_line: int total_ns: int total_perc: float counts: int mean_ns: int min_ns: int max_ns: int std_ns: float
[docs] @dataclass class ProfileData: """All zone records extracted from one Tracy CSV file.""" csv_path: Path records: list[ProfileRecord] = field(default_factory=list) @property def name(self) -> str: return self.csv_path.stem @property def total_calls(self) -> int: return sum(r.counts for r in self.records)
[docs] class ProfileExtractor: """Extract profiling data from Tracy CSV files produced by tracy-csvexport.""" @staticmethod def _parse_rows(reader) -> list[ProfileRecord]: records = [] for row in reader: records.append( ProfileRecord( name=row["name"], src_file=row["src_file"], src_line=int(row["src_line"]), total_ns=int(float(row["total_ns"])), total_perc=float(row["total_perc"]), counts=int(row["counts"]), mean_ns=int(float(row["mean_ns"])), min_ns=int(float(row["min_ns"])), max_ns=int(float(row["max_ns"])), std_ns=float(row["std_ns"]), ) ) records.sort(key=lambda r: r.total_ns, reverse=True) return records
[docs] @staticmethod def extract(csv_path: Path) -> ProfileData: """Parse a single Tracy CSV file and return structured data.""" csv_path = Path(csv_path) profile = ProfileData(csv_path=csv_path) with open(csv_path, newline="", encoding="utf-8") as f: profile.records = ProfileExtractor._parse_rows(csv.DictReader(f)) return profile
[docs] @staticmethod def extract_from_tracy(tracy_path: Path) -> ProfileData: """Run tracy-csvexport on a .tracy file, save a sibling .csv, and return ProfileData. Requires tracy-csvexport to be on PATH (built via: bundle tracy build csvexport). The CSV is saved alongside the .tracy file so it can be reused without re-exporting. """ import shutil import subprocess tracy_path = Path(tracy_path) csvexport = shutil.which("tracy-csvexport") if not csvexport: raise FileNotFoundError("tracy-csvexport not found on PATH — run: bundle tracy build csvexport") result = subprocess.run([csvexport, str(tracy_path)], capture_output=True, text=True, check=True) csv_path = tracy_path.with_suffix(".csv") csv_path.write_text(result.stdout, encoding="utf-8") return ProfileExtractor.extract(csv_path)
[docs] @staticmethod def extract_all(path: Path) -> list[ProfileData]: """Extract from a .tracy file, a single CSV file, or all CSV files in a directory.""" path = Path(path) if path.is_file(): if path.suffix == ".tracy": return [ProfileExtractor.extract_from_tracy(path)] return [ProfileExtractor.extract(path)] return [ProfileExtractor.extract(f) for f in sorted(path.glob("*.csv"))]