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-prof flag)
  • CG-RAG CLI installed and available as cgr in 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-prof enables V8’s sampling profiler
  • --cpu-prof-name specifies 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-path tells the converter where the source tree lives for frame-to-symbol resolution
  • --workload assigns 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 details
  • codebase_rag/trace/node_tracer/ – Contains the conversion logic that parses V8 cpuprofile JSON and emits standardized trace records
  • src/bin/cgr – The CLI entry point implementing cgr trace convert and cgr trace ingest commands

Summary

  • Dynamic tracing in CG-RAG enriches static analysis with runtime CALLS edges derived from V8 CPU profiles.
  • The three-step workflow involves recording with --cpu-prof, converting via cgr trace convert, and ingesting via cgr trace ingest.
  • Converted traces produce cgr-trace.jsonl files containing resolved call records with workload labels.
  • Ingested edges carry the property dynamic: true and statistics like dynamic_call_count to distinguish them from static analysis results.
  • The codebase_rag/trace/node_tracer/ module handles V8-specific parsing, while src/bin/cgr provides 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →