Skip to content

Commit 36f51b9

Browse files
authored
Merge pull request #3 from peixuana/RenderStage_split_byProcess
Add tools to split RenderStage data by process in Perfetto Trace file
2 parents b6ab5ad + 3f27965 commit 36f51b9

6 files changed

Lines changed: 789 additions & 0 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,6 @@ wheels/
1212
# Input/output data files
1313
input/
1414
output/
15+
16+
# Local AI tool state
17+
.claude/

CLAUDE.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Environment
6+
7+
- Python 3.13+ required. Dependencies managed by [`uv`](https://docs.astral.sh/uv/); `uv sync` creates `.venv` and installs dev deps (pytest).
8+
- Runtime dependencies list is empty (stdlib only); pytest is the sole dev dependency.
9+
10+
## Common commands
11+
12+
```bash
13+
uv sync # create venv, install deps
14+
uv run main.py --help # list registered tools
15+
uv run main.py converter <csv> [-o out.json | --output-dir dir/]
16+
uv run main.py split-renderstage <input.pftrace> <output.pftrace>
17+
uv run main.py split-renderstage --debug <input.pftrace>
18+
19+
uv run python -m tools.converter.convert_sdp_csv_to_perfetto_json <csv> # bypass dispatcher
20+
uv run python -m tools.converter.split_renderstage_by_process <input.pftrace> <output.pftrace>
21+
uv run split-renderstage-by-process <input.pftrace> <output.pftrace> # console script
22+
23+
uv run pytest # full suite
24+
uv run pytest -v
25+
uv run pytest tests/test_convert_sdp_csv_to_perfetto_json.py::TestExampleCSV -v
26+
```
27+
28+
Commits must be DCO-signed (`git commit -s`). External PRs are scanned by Semgrep in CI.
29+
30+
On Windows, if `uv run` fails while trying to remove `.venv\lib64` with `Access is denied`, close terminals/processes using `.venv`, remove and recreate `.venv` with `uv sync`, or use direct `python ...` invocation as a stdlib-only workaround for tools without runtime dependencies.
31+
32+
## Architecture
33+
34+
**Dispatcher pattern.** `main.py` is a thin CLI dispatcher. `TOOLS_REGISTRY` maps a tool name (e.g. `converter`) to a dotted module path; the dispatcher imports the module, rewrites `sys.argv` to hide its own name, then calls the module's `main()`. To add a new tool: create `tools/<name>/<module>.py` exposing `main()` that uses argparse, then add one line to `TOOLS_REGISTRY`. Each tool is independently invokable via `python -m ...` and is also registered as a console script in `pyproject.toml` (`[project.scripts]`).
35+
36+
**Converter (`tools/converter/convert_sdp_csv_to_perfetto_json.py`).** The SDP CSV → Perfetto JSON pipeline auto-detects the CSV variant by inspecting the header row:
37+
38+
- **Realtime** header has `Category`, `Metric`, `Timestamp``convert_realtime()`. Emits only counter events (`ph: "C"`). Synthesizes PIDs starting at `800000`, one per `(Process, Category)` pair.
39+
- **Trace** header has `Group`, `Track`, `TimestampStart``convert_trace()`. Emits duration slices (`ph: "X"`) and counters (`ph: "C"`). Uses *real* PIDs from the CSV for `Process` rows and synthesizes PIDs starting at `900000` for `System` rows (one per `Group`). Trace-mode conversion does a two-pass read: pass 1 finds main-thread names (rows where `TID == PID`) to label processes; pass 2 emits events.
40+
41+
Counter vs slice disambiguation in trace mode is driven by the `Track` column: tracks containing `CounterValue` are explicit counters; rows with both `TimestampStart` and `TimestampEnd` are slices; rows with only one timestamp are treated as implicit counters.
42+
43+
Output always wraps events as `{"traceEvents": [...]}` with metadata events (`ph: "M"`, `process_name`/`thread_name`) prepended. `_write_perfetto()` is the single write path; `_metadata_events()` is the single metadata builder — reuse these when adding new event sources rather than open-coding JSON structure.
44+
45+
**Batch / glob handling.** `resolve_input_files()` expands glob patterns and deduplicates by normalized path. When multiple inputs are given, `-o` is ignored (warned) and each input is written next to itself (or into `--output-dir`) with the basename + `.json`.
46+
47+
**RenderStage splitter (`tools/converter/split_renderstage_by_process.py`).** This tool reads binary Perfetto `.pftrace` files using stdlib-only protobuf decode/encode helpers. It extracts `GpuRenderStageEvent` packets, resolves each event to a process, groups slices by process, then appends new process-scoped RenderStage tracks to the output trace while preserving the original trace content.
48+
49+
Process ownership resolution is prioritized as: packet `trusted_pid`, stage-spec descriptions like `Process <name> [<pid>]`, Vulkan queue submission IDs, Vulkan debug-utils device context, then GPU context fallback. Debug mode (`--debug <input.pftrace>`) scans packet fields, trusted PIDs, track descriptors, interned names, and sample RenderStage events without writing an output file.
50+
51+
## Tests
52+
53+
- Live in `tests/test_convert_sdp_csv_to_perfetto_json.py`. `test_main.py` is a backward-compat shim that re-exports the same tests so `pytest test_main.py` still works.
54+
- `tests/__init__.py` and `tools/__init__.py` exist so tests can import `tools.converter...` directly — keep them.

README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ This converter supports CSV files exported from both SDP capture modes:
2626
| System scheduling slices | `"ph": "X"` | Kernel scheduling events (e.g., Sched CPU) |
2727
| Metadata | `"ph": "M"` | Auto-generated `process_name` and `thread_name` labels |
2828

29+
### RenderStage Splitter
30+
31+
Split GPU RenderStage events in a Perfetto `.pftrace` by process. The tool reads a Perfetto trace, extracts `GpuRenderStageEvent` packets, resolves each event to a process when possible, and writes an augmented `.pftrace` with separate RenderStage tracks per process for easier analysis in [Perfetto UI](https://ui.perfetto.dev/).
32+
33+
The tool also includes a debug mode that scans the input trace and prints packet, process, track, and RenderStage diagnostics.
34+
2935
## Installation Instructions
3036

3137
```bash
@@ -50,6 +56,9 @@ uv run main.py --help
5056

5157
# Run a specific tool (e.g., converter)
5258
uv run main.py converter <tool-specific arguments ...>
59+
60+
# Run the RenderStage splitter
61+
uv run main.py split-renderstage <input.pftrace> <output.pftrace>
5362
```
5463

5564
### Converter Examples
@@ -77,6 +86,25 @@ You can also invoke the converter directly (bypassing the dispatcher):
7786
uv run python -m tools.converter.convert_sdp_csv_to_perfetto_json trace1.csv
7887
```
7988

89+
### RenderStage Splitter Examples
90+
91+
```bash
92+
# Split GPU RenderStage events into process-specific tracks
93+
uv run main.py split-renderstage input.pftrace output.pftrace
94+
95+
# Inspect trace contents without writing an output file
96+
uv run main.py split-renderstage --debug input.pftrace
97+
```
98+
99+
You can also invoke the RenderStage splitter directly (bypassing the dispatcher):
100+
101+
```bash
102+
uv run python -m tools.converter.split_renderstage_by_process input.pftrace output.pftrace
103+
104+
# Or use the uv-installed console script
105+
uv run split-renderstage-by-process input.pftrace output.pftrace
106+
```
107+
80108
## Development
81109

82110
To contribute new features or fixes, please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines on branching, submitting pull requests, and coding standards.

main.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
Examples:
1919
python main.py converter input.csv -o output.json
2020
python main.py converter input1.csv input2.csv --output-dir output/
21+
python main.py split-renderstage input.pftrace output.pftrace
2122
"""
2223

2324
import importlib
@@ -29,6 +30,7 @@
2930
# ---------------------------------------------------------------------------
3031
TOOLS_REGISTRY: dict[str, str] = {
3132
"converter": "tools.converter.convert_sdp_csv_to_perfetto_json",
33+
"split-renderstage": "tools.converter.split_renderstage_by_process",
3234
}
3335

3436

@@ -61,6 +63,7 @@ def _print_help():
6163
print(f" {name:20s}{mod}")
6264
print("\nPass -h/--help after a tool name for tool-specific help.")
6365
print("Example: python main.py converter --help")
66+
print("Example: python main.py split-renderstage --debug input.pftrace")
6467

6568

6669
if __name__ == "__main__":

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ dependencies = []
99
[project.scripts]
1010
snapdragon-profiler-ecosystem = "main:main"
1111
sdp-csv-to-perfetto-json = "tools.converter.convert_sdp_csv_to_perfetto_json:main"
12+
split-renderstage-by-process = "tools.converter.split_renderstage_by_process:main"
1213

1314
[dependency-groups]
1415
dev = ["pytest>=8.0"]

0 commit comments

Comments
 (0)