AI-explored tools for the Source Engine profiling system.
|
|
há 1 semana atrás | |
|---|---|---|
| server-patch | há 1 semana atrás | |
| .gitignore | há 1 semana atrás | |
| README.md | há 1 semana atrás | |
| VPROF_FORMAT.md | há 1 semana atrás | |
| fix-vprof-timings.patch | há 1 semana atrás | |
| index.html | há 1 semana atrás | |
| vprof_to_json.py | há 1 semana atrás |
Tools for reading Source Engine VProf profiling captures (.vprof) and
turning them into JSON.
VPROF_FORMAT.md — reverse-engineered binary format
documentation (with references to the leaked engine source).vprof_to_json.py — standalone Python 3 parser /
converter.index.html — self-contained browser viewer (no build step,
no network access required).For the Python tool: Python 3.8+ (uses dataclasses and statistics.fmean).
No third-party packages. The browser viewer needs only a modern browser.
Open index.html directly from disk or serve it as a static file. Choose (or
drag & drop) a .vprof capture and the page parses it locally in a Web
Worker — nothing is uploaded. It shows:
The first parse records a tiny index: the byte offset of every frame plus the frame in which each tree node first appeared. Nothing per-sample is kept in memory for large files. When a frame range is selected, the viewer asks the worker to re-parse only that byte span from the source file and aggregates it, so scoping works on arbitrarily large recordings without a memory blow-up. (The tree order at any frame is the final order filtered by each node's add-frame, because VProf only ever prepends children — so no per-frame tree snapshots are needed.)
When the browser supports the File System Access
API,
the source file handle is remembered in IndexedDB and a Reopen last recording
button is offered next time, so on-demand range reads work without re-picking
the file. Plain <input type=file> and drag & drop continue to work
(within the session) when the API is unavailable.
# Print JSON to stdout (compact)
python vprof_to_json.py capture.vprof
# Write pretty JSON to a file
python vprof_to_json.py capture.vprof -o capture.json --indent 2
# Metadata, budget groups and the summary only (small, fast)
python vprof_to_json.py capture.vprof --summary-only --indent 2
# Only the first 100 frames
python vprof_to_json.py capture.vprof --max-frames 100 -o first100.json
# Drop the per-frame samples or the node table
python vprof_to_json.py capture.vprof --no-frames
python vprof_to_json.py capture.vprof --no-nodes
Full options are available via python vprof_to_json.py --help.
{
"format": "source-vprof",
"source": {
"file": "capture.vprof",
"size_bytes": 1310867,
"version": 1,
"root_node_id": 0
},
// All budget groups, including the 31 built-in defaults (is_default=true).
"budget_groups": [
{ "id": 0, "name": "Unaccounted", "flags": 6,
"flags_decoded": ["SERVER", "OTHER"], "is_default": true }
],
// Node table. `children` gives the final tree order (child IDs).
"nodes": [
{ "id": 100, "name": "CEngine::Frame", "parent_id": 0,
"budget_group_id": 0, "budget_group": "Unaccounted", "children": [] }
],
// One entry per recorded frame.
"frames": [
{
"index": 0,
"tick": 54863,
"total_microseconds": 0, // time of the root node
"samples": { // node id -> timing for this frame
"0": { "calls": 1, "microseconds": 0 },
"100": { "calls": 0, "microseconds": 0 }
},
"added_nodes": [100], // nodes first seen in this frame
"added_budget_groups": [31] // groups first seen in this frame
}
],
// Aggregate statistics.
"summary": {
"frame_count": 1303,
"node_count": 352,
"budget_group_count": 38,
"tick_first": 54863,
"tick_last": 56165,
"tick_span": 1302,
"total_recorded_microseconds": 0,
"frame_time_microseconds": {
"min": 0, "max": 0, "mean": 0.0, "median": 0, "total": 0, "p95": 0
},
"top_nodes_by_total_microseconds": [
{ "node_id": 0, "name": "Root", "budget_group": "Unaccounted",
"total_calls": 1303, "total_microseconds": 0,
"avg_microseconds": 0.0, "max_microseconds": 0,
"percent_of_total": 0.0 }
]
}
}
children list is the final order after all frames. Because
AddNode prepends children, the per-frame sample values are keyed by node ID
rather than nested, so the tree can always be rebuilt from nodes.CVProfile; the
parser seeds them so files that only record the groups added after
construction still resolve correctly.PreUpdateProfile → MarkFrame() → VProfRecord_Snapshot()),
MarkFrame() copies the current frame's counters to the previous frame and
clears them before the recorder runs, so the writer reads cleared
m_CurFrameTime / m_nCurFrameCalls values. Such captures still contain the
full call tree and per-frame call counts.255 (extended calls) and
0xFFFF (extended time) escape codes.--summary-only,
--no-frames, --max-frames, or -o to manage output size and memory.