How to Ingest Node.js CPU Profile Trace Data with code-graph-rag: A Complete Guide
Convert V8 .cpuprofile files into weighted call-graph edges using cgr trace convert and cgr trace ingest commands.
Node.js emits V8 CPU profile files (.cpuprofile) that contain sampled call-stack information from JavaScript execution. The code-graph-rag repository—maintained at vitali87/code-graph-rag—ships a purpose-built converter in codebase_rag/trace/cpuprofile.py that transforms these profiles into the cgr-trace interchange format for graph ingestion. This workflow captures dynamic caller-callee relationships with sampling weights, enabling graph-based analysis of actual runtime behavior.
Generate a V8 CPU Profile
V8's built-in profiler samples the JavaScript execution stack at fixed intervals (default 1ms). To capture a profile from your Node.js application:
node --cpu-prof --cpu-prof-name=run.cpuprofile app.js
The --cpu-prof flag enables sampling, and --cpu-prof-name specifies the output filename. The resulting .cpuprofile file contains a JSON tree of sampled call stacks with timing metadata.
Convert the Profile to cgr-trace Format
The cgr CLI detects the .cpuprofile extension and dispatches to convert_cpuprofile in codebase_rag/trace/cpuprofile.py. Run the conversion:
cgr trace convert run.cpuprofile \
--repo-path /path/to/your-repo \
--workload smoke
This command produces cgr-trace.jsonl, a line-delimited JSON file with structured call records.
Key Conversion Logic in cpuprofile.py
The converter implements three critical transformations according to the source code:
_aggregate_project_edges— Collapses the sampled call-tree into weighted caller-callee pairs while filtering through internal V8 frames that lack meaningful source mapping._project_frame— Resolves generated file URLs to repository-relative paths, with automatic source-map relocation for transpiled code (TypeScript, Babel, etc.).convert_cpuprofile— WritesCallRecordobjects with aTraceHeadermarkedTRACE_TOOL_NAME_CPUPROFILE, preserving the sampled count asdynamic_call_count.
Frames that resolve outside the repository root—such as node: built-ins or node_modules dependencies—are automatically discarded, ensuring the graph contains only project-relevant edges.
Ingest the Trace into the Graph
Merge the converted call records into your repository's knowledge graph:
cgr trace ingest cgr-trace.jsonl --repo-path /path/to/your-repo
The ingest command:
- Reads each line-delimited JSON record
- Resolves paths against the provided repository root
- Creates dynamic edges with
dynamic: trueflag - Attaches the workload label (e.g.,
smoke) and sampled weight
Verify Successful Ingestion
Confirm that dynamic edges were added from the CPU profile:
cgr graph query 'CALLS' --filter dynamic:true
This returns caller-callee pairs observed during sampling, complete with dynamic_call_count weights representing relative execution frequency.
Source-Map Handling for Transpiled Projects
A critical capability of code-graph-rag's Node.js ingestion is transparent source-map support. When the profile references generated .js files, the converter:
- Looks for adjacent
.js.mapfiles - Parses the source map via
codebase_rag/trace/sourcemap.py - Remaps frames to original TypeScript/JavaScript source locations
This ensures accurate graph nodes even for compiled or bundled applications, with the original source positions preserved in the final trace records.
Core Files and Their Roles
| File | Purpose |
|---|---|
codebase_rag/trace/cpuprofile.py |
Core converter; parses .cpuprofile JSON, aggregates edges, applies source-maps, emits cgr-trace records |
codebase_rag/trace/cli.py |
CLI dispatcher; detects .cpuprofile suffix and routes to convert_cpuprofile |
codebase_rag/trace/sourcemap.py |
Source-map loader and location remapping utilities |
codebase_rag/constants/trace.py |
Trace format constants including TRACE_TOOL_NAME_CPUPROFILE |
docs/guide/dynamic-tracing.md |
Complete user documentation for Node.js and other runtimes |
Summary
- Generate Node.js CPU profiles with
node --cpu-prof - Convert via
cgr trace convert, which handles sampling aggregation and source-map resolution incodebase_rag/trace/cpuprofile.py - Ingest with
cgr trace ingestto create weighted dynamic edges - Sampled counts are preserved as
dynamic_call_count, not exact call frequencies - Source-maps are automatically resolved for transpiled code accuracy
- Out-of-scope frames (built-ins, dependencies) are filtered to maintain project focus
Frequently Asked Questions
What sampling interval does V8 use for CPU profiles?
V8 samples the JavaScript execution stack every 1 millisecond by default. This produces statistically representative call-stack data without prohibitive overhead, though rare fast functions may be missed entirely. The code-graph-rag converter treats sample counts as relative weights rather than absolute call frequencies.
Can I ingest profiles from TypeScript or bundled applications?
Yes. The converter in codebase_rag/trace/cpuprofile.py automatically detects and applies source maps through codebase_rag/trace/sourcemap.py. When a .js.map file exists adjacent to the generated JavaScript, frames are remapped to original source locations before graph insertion, preserving accurate file paths and line numbers.
Why are some frames missing from the ingested trace?
Frames are filtered when they resolve outside the repository root—specifically node: built-in modules, node_modules dependencies, or V8 internal frames without source mappings. This intentional scoping ensures the knowledge graph contains only edges relevant to your project's own code.
How does dynamic_call_count differ from static call analysis?
dynamic_call_count reflects observed execution frequency from sampling, weighted by sample hits. Static analysis would show all possible call paths regardless of actual runtime usage. The CPU profile ingestion thus surfaces hot paths and frequently executed edges that static analysis cannot identify.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →