# How to Pull eBPF Profiles for Code‑Graph‑RAG Analysis: A Complete 3‑Step Pipeline

> Learn how to pull eBPF profiles for Code-Graph-RAG analysis in 3 simple steps. Convert and ingest dynamic CALLS edges into your knowledge graph efficiently.

- Repository: [Vitali Avagyan/code-graph-rag](https://github.com/vitali87/code-graph-rag)
- Tags: how-to-guide
- Published: 2026-09-06

---

**Pull eBPF profiles using `cgr trace pull`, convert them to JSONL with `cgr trace convert`, then ingest dynamic `CALLS` edges into your knowledge graph via `cgr trace ingest`.**

Code‑Graph‑RAG bridges static source analysis with runtime behavior by ingesting eBPF‑derived continuous profiles from systems like Parca, Pyroscope, OpenTelemetry, and `perf`. This integration adds **dynamic call edges** to your knowledge graph, annotating them with actual production dispatch patterns that static parsing cannot detect. The workflow relies on three CLI commands that fetch pprof‑encoded profiles, transform them into the project's interchange format, and merge the results into Memgraph.

## The Three‑Stage eBPF Ingestion Pipeline

Code‑Graph‑RAG treats eBPF profiles identically to test‑run traces, with one key distinction: you must specify `--format ebpf` to ensure proper parsing of the gzipped protobuf format shared by Go's pprof tooling.

### Stage 1: Fetch the Profile with `cgr trace pull`

The `pull` sub‑command downloads a profile from an HTTP(S) endpoint and persists it locally. In [[`codebase_rag/trace/cli.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/trace/cli.py)](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/trace/cli.py), the `pull_cmd` function orchestrates this fetch, handling optional authentication headers and writing the binary to a temporary file.

```bash
cgr trace pull "https://parca.example/api/v1/profile?format=pprof" \
    --repo-path /path/to/your/repo \
    --build-id 8f3a1234abcd \
    --path-map /build/src/=/path/to/your/repo/src/ \
    --label endpoint \
    --header "Authorization=Bearer $TOKEN"

```

Key parameters for `cgr trace pull`:

- **`--build-id`** — Filters the profile to symbols matching your target binary, discarding noise from other containers.
- **`--path-map`** — Rewrites build‑time absolute paths to your local repository structure, ensuring frame‑to‑source alignment.
- **`--header`** — Injects arbitrary HTTP headers for authenticated endpoints.
- **`--label`** — Attaches workload metadata that propagates through conversion and ingestion.

### Stage 2: Convert to JSONL with `cgr trace convert`

The `convert` sub‑command transforms the raw pprof blob into `cgr‑trace.jsonl`, a line‑delimited JSON format the graph loader consumes. This step performs **symbol demangling**, **path re‑anchoring**, and **language‑specific processing** based on your `--format` and `--language` selections.

```bash
cgr trace convert prod.pb.gz \
    --format ebpf \
    --repo-path /path/to/your/repo \
    --language go \
    --build-id 8f3a1234abcd \
    --path-map /build/src/=/path/to/your/repo/src/ \
    --label endpoint \
    --service service_name=checkout

```

The implementation in [[`codebase_rag/trace/trace_convert.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/trace/trace_convert.py)](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/trace/trace_convert.py) handles:

- Parsing gzipped protobuf profiles
- Demangling C++ or Rust symbols when detected
- Applying path mappings to reconcile build and source locations
- Emitting structured call records with metadata payloads

### Stage 3: Ingest Dynamic Edges with `cgr trace ingest`

The final stage merges runtime observations into your existing knowledge graph. The `ingest` sub‑command creates `CALLS` edges marked with `dynamic: true` and `dynamic_sampled: true`, annotated with properties like `dynamic_call_count`, `dynamic_workloads`, and `dynamic_receiver_types`.

```bash

# Ensure static graph exists first

cgr start --repo-path /path/to/your/repo --update-graph

# Ingest the converted trace

cgr trace ingest cgr-trace.jsonl --repo-path /path/to/your/repo

```

The ingestion logic in [[`codebase_rag/trace/trace_ingest.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/trace/trace_ingest.py)](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/trace/trace_ingest.py) performs upsert operations: existing static edges gain dynamic properties, while new edges are created for calls never observed in static analysis.

## Automating Continuous Profile Ingestion

Production deployments benefit from automated, recurring ingestion. The following cron‑compatible script refreshes your dynamic overlay every five minutes:

```bash
#!/bin/bash
set -euo pipefail

PROFILE_URL="https://parca.example/api/v1/profile?format=pprof"
REPO="/path/to/your/repo"
BUILD_ID="8f3a1234abcd"
PATH_MAP="/build/src/=/path/to/your/repo/src/"
TOKEN="${PARCA_TOKEN}"

while true; do
    cgr trace pull "$PROFILE_URL" \
        --repo-path "$REPO" \
        --build-id "$BUILD_ID" \
        --path-map "$PATH_MAP" \
        --header "Authorization=Bearer $TOKEN"
    
    cgr trace convert prod.pb.gz \
        --format ebpf \
        --repo-path "$REPO" \
        --language go \
        --build-id "$BUILD_ID" \
        --path-map "$PATH_MAP" \
        --label endpoint \
        --service service_name=checkout
    
    cgr trace ingest cgr-trace.jsonl --repo-path "$REPO"
    
    sleep 300
done

```

## Key Implementation Files

| File | Purpose |
|------|---------|
| [[`docs/guide/dynamic-tracing.md`](https://github.com/vitali87/code-graph-rag/blob/main/docs/guide/dynamic-tracing.md)](https://github.com/vitali87/code-graph-rag/blob/main/docs/guide/dynamic-tracing.md) | Complete user documentation for the tracing workflow, including [eBPF profile pull procedures](https://github.com/vitali87/code-graph-rag/blob/main/docs/guide/dynamic-tracing.md) |
| [[`codebase_rag/trace/cli.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/trace/cli.py)](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/trace/cli.py) | CLI dispatch for `pull`, `convert`, and `ingest` sub‑commands; `pull_cmd` implements argument parsing and HTTP orchestration |
| [[`codebase_rag/trace/trace_convert.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/trace/trace_convert.py)](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/trace/trace_convert.py) | pprof parsing, symbol demangling, path mapping, and JSONL generation |
| [[`codebase_rag/trace/trace_ingest.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/trace/trace_ingest.py)](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/trace/trace_ingest.py) | Memgraph edge upserts with dynamic property injection |
| [[`codebase_rag/trace/trace_pull.py`](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/trace/trace_pull.py)](https://github.com/vitali87/code-graph-rag/blob/main/codebase_rag/trace/trace_pull.py) | HTTP fetch implementation, header handling, and temporary file management |

## Summary

- **Pull** eBPF profiles from profilers using `cgr trace pull` with `--build-id` and `--path-map` for precise symbol alignment.
- **Convert** pprof data to JSONL via `cgr trace convert --format ebpf`, enabling language‑aware processing.
- **Ingest** dynamic call edges into Memgraph with `cgr trace ingest`, enriching static analysis with production runtime behavior.
- Automate the three‑stage pipeline for continuous observability integration.

## Frequently Asked Questions

### What eBPF profilers are compatible with Code‑Graph‑RAG?

Any profiler emitting **pprof‑encoded profiles** works, including Parca, Pyroscope, OpenTelemetry's profiling signal, and raw `perf` output converted to pprof. The `--format ebpf` flag indicates the shared protobuf format, not a specific tool.

### Why is `--build-id` critical for accurate ingestion?

The build ID filters the profile to symbols from your exact binary. Without it, containerized deployments often include shared libraries and runtime frames from base images that pollute the call graph with irrelevant edges.

### Can I ingest multiple services simultaneously?

Yes. Run separate `convert` and `ingest` commands per service with distinct `--service` labels, or aggregate profiles at the pull stage. The graph stores `dynamic_workloads` as an array property, supporting multi‑service analysis.

### How does path mapping handle container build paths?

The `--path-map` argument accepts `source=destination` pairs. For Docker builds where source lives at `/build/src/`, map to your local checkout: `--path-map /build/src/=/home/user/project/src/`. This ensures stack frames resolve to correct repository files.