How to Enable Dynamic Tracing for Node.js with Code-Graph-RAG
Code-Graph-RAG (CG-RAG) augments its static call-graph with runtime-observed edges by converting V8 CPU profiles into dynamic CALLS relationships using the cgr trace convert and cgr trace ingest CLI commands.
Dynamic tracing bridges the gap between static analysis and actual runtime behavior in the vitali87/code-graph-rag repository. For Node.js applications, this process leverages the built-in V8 sampling profiler to capture real-world execution paths, then enriches the knowledge graph with edges and statistics that static parsing alone cannot detect, such as dynamic dispatch patterns and event-driven call chains.
Understanding Dynamic Tracing in CG-RAG
Static analysis often misses runtime-generated code paths, including dynamic imports, event emitters, and polymorphic method dispatches. CG-RAG solves this by ingesting V8 CPU profiles (*.cpuprofile) collected from live Node.js workloads. According to the source code, the system creates CALLS edges marked with dynamic: true and populates metrics like dynamic_call_count and dynamic_workload to distinguish these runtime observations from statically derived relationships.
The conversion logic resides in codebase_rag/trace/node_tracer/, which parses the V8 JSON format and translates stack frames into the CG-RAG interchange format (cgr-trace.jsonl). The CLI entry point at src/bin/cgr implements the two-step workflow: conversion followed by ingestion.
Prerequisites
Before capturing traces, ensure your environment meets these requirements:
- Node.js with V8 profiling support (Node.js 12.0.0+ includes the
--cpu-profflag) - CG-RAG CLI installed and available as
cgrin your shell - Access to the target repository root path for symbol resolution
Step-by-Step: Capturing and Ingesting Node.js Traces
Step 1: Record a V8 CPU Profile
Run your Node.js application or test suite with the V8 sampling profiler enabled. This generates a .cpuprofile file containing sampled stack traces.
node --cpu-prof --cpu-prof-name=run.cpuprofile app.js
--cpu-profenables V8’s sampling profiler--cpu-prof-namespecifies the output filename (default pattern:isolate-*.cpuprofile)
Step 2: Convert the Profile to CG-RAG Format
Use the cgr trace convert command to transform the V8 profile into cgr-trace.jsonl. You must provide the repository root path and a workload label for filtering.
cgr trace convert run.cpuprofile \
--repo-path /path/to/your-repo \
--workload smoke
--repo-pathtells the converter where the source tree lives for frame-to-symbol resolution--workloadassigns a label (e.g., "smoke", "unit-tests") used later for filtering dynamic edges
This command outputs a cgr-trace.jsonl file containing resolved call records in the CG-RAG interchange format.
Step 3: Ingest the Trace into the Graph
Feed the generated JSON-Lines file into the CG-RAG graph database to create dynamic edges.
cgr trace ingest cgr-trace.jsonl \
--repo-path /path/to/your-repo
During ingestion, the system resolves recorded frames back to source nodes, creates CALLS edges annotated with dynamic: true, and aggregates statistics including dynamic_call_count and dynamic_workload properties on the relationships.
Working with Multiple Profiles
When running parallel Node.js workers or distributed test suites (such as with pytest-xdist equivalents), you will generate multiple *.cpuprofile files. Convert each profile individually, then ingest all resulting cgr-trace.jsonl files in a single batch operation:
# Convert multiple profiles
cgr trace convert worker-1.cpuprofile --repo-path ./repo --workload parallel-suite
cgr trace convert worker-2.cpuprofile --repo-path ./repo --workload parallel-suite
# Ingest all traces
cgr trace ingest cgr-trace.jsonl --repo-path ./repo
Key Implementation Files
The dynamic tracing pipeline relies on these specific components in the vitali87/code-graph-rag repository:
docs/guide/dynamic-tracing.md– The comprehensive user guide explaining the full workflow for all supported runtimes, including Node.js-specific detailscodebase_rag/trace/node_tracer/– Contains the conversion logic that parses V8cpuprofileJSON and emits standardized trace recordssrc/bin/cgr– The CLI entry point implementingcgr trace convertandcgr trace ingestcommands
Summary
- Dynamic tracing in CG-RAG enriches static analysis with runtime
CALLSedges derived from V8 CPU profiles. - The three-step workflow involves recording with
--cpu-prof, converting viacgr trace convert, and ingesting viacgr trace ingest. - Converted traces produce
cgr-trace.jsonlfiles containing resolved call records with workload labels. - Ingested edges carry the property
dynamic: trueand statistics likedynamic_call_countto distinguish them from static analysis results. - The
codebase_rag/trace/node_tracer/module handles V8-specific parsing, whilesrc/bin/cgrprovides the user-facing CLI interface.
Frequently Asked Questions
What Node.js versions support dynamic tracing with CG-RAG?
Any Node.js version 12.0.0 or later supports dynamic tracing, as these versions include the --cpu-prof flag for the V8 sampling profiler. The CG-RAG converter handles the standard V8 CPU profile format consistently across Node.js releases.
How does CG-RAG resolve minified or transpiled code in profiles?
The cgr trace convert command uses the --repo-path parameter to map profile frames back to original source locations. When you point to the repository root, the system attempts to resolve frames against the local source tree, though accuracy depends on having source maps or original source files available alongside the executed code.
Can I merge multiple CPU profiles into a single graph?
Yes. Convert each individual *.cpuprofile to its own cgr-trace.jsonl file, then run cgr trace ingest on all generated trace files. The ingestion process aggregates statistics across all provided traces, incrementing dynamic_call_count for edges that appear in multiple profiles while maintaining the dynamic_workload labels for filtering.
What is the difference between static and dynamic CALLS edges in CG-RAG?
Static CALLS edges are derived from parsing source code (e.g., AST analysis) and represent syntactic function invocations. Dynamic CALLS edges are created from runtime CPU profiles and capture actual executed paths, including those involving dynamic dispatch, closures, and event-driven architectures that static analysis cannot predict. Dynamic edges carry the dynamic: true property and workload-specific metadata.
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 →