Flamegraph Export - The Renacer Book - Pure Rust System Call Tracing
Description: A comprehensive guide to renacer: syscall tracing, DWARF correlation, and performance analysis

# Flamegraph Visualization

Flamegraphs are a powerful visualization technique for profiling data, showing call stacks and their time distribution. This chapter shows how to export Renacer's profiling data and visualize it with external flamegraph tools.

> TDD-Verified: Export functionality tested in `tests/sprint22_html_output_tests.rs`, profiling in `tests/sprint13_function_profiling_tests.rs`

> Parent Chapter: See Function Profiling for overview and basic usage.

## Overview

Flamegraphs visualize call stacks with:

- X-axis: Alphabetically sorted function names (not time!)
- Y-axis: Stack depth (call hierarchy)
- Width: Proportion of total time spent in function
- Color: Usually random (for visual distinction) or can indicate metrics

Why flamegraphs?

- Visual bottleneck identification - Wide bars = hot code paths
- Call hierarchy understanding - See parent-child relationships
- Interactive exploration - Click to zoom, search functions
- Shareable analysis - Export as SVG for reports

Example flamegraph:

    ┌─────────────────────────────────────────────┐ 100% (main)
    │                main()                      │
    ├─────────────┬───────────────────────────────┤
    │  process()  │     network_call()           │ 60%
    │    40%      │                               │
    ├─────┬───────┼───────┬───────────────────────┤
    │read │write  │connect│  send_request()      │ 35%
    │ 20% │ 20%   │  5%   │                       │
    └─────┴───────┴───────┴───────────────────────┘

Interpretation: `send_request()` is the bottleneck (35% of total time).

## Exporting Profiling Data

Renacer supports JSON export for post-processing:

    $ renacer --function-time --source --format json -- ./myapp > profile.json

Tested by: `test_html_format_flag_accepted` (Sprint 22 - verifies format flag acceptance)

JSON Output Structure:

    {
      "syscalls": [...],
      "function_profile": [
        {
          "function": "src/main.rs:42",
          "calls": 150,
          "total_time_us": 234567,
          "avg_time_us": 1563,
          "slow_io_count": 148
        },
        ...
      ],
      "summary": {
        "total_syscalls": 500,
        "total_duration_ms": 580.245
      }
    }

## Generating Flamegraphs

### Method 1: Using Brendan Gregg's Flamegraph Tools

Prerequisites:

    $ git clone https://github.com/brendangregg/FlameGraph
    $ cd FlameGraph

Step 1: Convert JSON to Folded Stack Format

Create a conversion script `renacer_to_folded.py`:

    #!/usr/bin/env python3
    import json
    import sys

    # Read Renacer JSON output
    with open(sys.argv[1]) as f:
        data = json.load(f)

    # Convert function profile to folded stacks
    for func in data.get('function_profile', []):
        function_name = func['function']
        # Use total time (microseconds) as sample count
        samples = func['total_time_us']

        # Format: function_name samples
        print(f"{function_name} {samples}")

Step 2: Generate Flamegraph

    # Convert Renacer JSON → folded stacks
    $ python3 renacer_to_folded.py profile.json > profile.folded

    # Generate flamegraph SVG
    $ ./FlameGraph/flamegraph.pl profile.folded > flamegraph.svg

    # Open in browser
    $ firefox flamegraph.svg

Result: Interactive SVG flamegraph showing function time distribution!

### Method 2: Using speedscope.app

Speedscope is a web-based flamegraph viewer:

Step 1: Export profiling data

    $ renacer --function-time --source --format json -- ./myapp > profile.json

Step 2: Visit speedscope.app

    $ firefox https://www.speedscope.app/

Step 3: Upload `profile.json`

Note: Speedscope expects specific JSON formats (Chrome timeline, Firefox profiler, etc.). Renacer's format may need conversion. Use Method 1 (Brendan Gregg's tools) for simplest workflow.

### Method 3: Manual HTML Visualization

For small datasets, create a simple HTML visualization:

    <!DOCTYPE html>
    <html>
    <head>
        <title>Renacer Flamegraph</title>
        <style>
            .bar { margin: 2px; padding: 5px; background: #f90; }
            .function { font-family: monospace; }
            .time { float: right; color: #666; }
        </style>
    </head>
    <body>
        <h1>Function Profile</h1>
        <div id="chart"></div>
        <script>
            // Load profile.json (served via http server)
            fetch('profile.json')
                .then(r => r.json())
                .then(data => {
                    const total = data.summary.total_duration_ms * 1000; // Convert to μs
                    const chart = document.getElementById('chart');

                    data.function_profile
                        .sort((a, b) => b.total_time_us - a.total_time_us)
                        .forEach(func => {
                            const pct = (func.total_time_us / total * 100).toFixed(1);
                            const bar = document.createElement('div');
                            bar.className = 'bar';
                            bar.style.width = pct + '%';
                            bar.innerHTML = `
                                <span class="function">${func.function}</span>
                                <span class="time">${func.total_time_us}μs (${pct}%)</span>
                            `;
                            chart.appendChild(bar);
                        });
                });
        </script>
    </body>
    </html>

Serve and view:

    $ python3 -m http.server 8080
    $ firefox http://localhost:8080/

## Practical Examples

### Example 1: Identify Database Bottlenecks

Scenario: Web application with database calls

    # Profile application
    $ renacer --function-time --source --format json -e trace=network -- ./webapp > profile.json

    # Convert to flamegraph
    $ python3 renacer_to_folded.py profile.json | ./FlameGraph/flamegraph.pl > db-bottlenecks.svg

Flamegraph shows:

- Wide bar for `execute_query()` → Database calls dominate execution time
- Narrow bar for `cache_lookup()` → Fast cache hits
- Medium bar for `json_serialize()` → Moderate CPU time

Action: Add caching layer to reduce wide `execute_query()` bar.

### Example 2: Build System Analysis

Scenario: Slow cargo build

    $ renacer --function-time --source --format json -e trace=file -- cargo build > build-profile.json
    $ python3 renacer_to_folded.py build-profile.json | ./FlameGraph/flamegraph.pl > build-flame.svg

Flamegraph shows:

- Wide bar for `rustc:link` → Linking is the bottleneck
- Narrow bars for `cargo:download_crate` → Dependencies already cached
- Medium bar for `rustc:codegen` → Compilation time is acceptable

Action: Enable incremental compilation to reduce linking time.

### Example 3: Comparing Before/After Optimizations

Before optimization:

    $ renacer --function-time --source --format json -- ./app-v1 > before.json
    $ python3 renacer_to_folded.py before.json | ./FlameGraph/flamegraph.pl > before.svg

After optimization:

    $ renacer --function-time --source --format json -- ./app-v2 > after.json
    $ python3 renacer_to_folded.py after.json | ./FlameGraph/flamegraph.pl > after.svg

Compare side-by-side:

    $ firefox before.svg after.svg

Look for:

- Narrower bars (faster functions)
- Shorter stacks (reduced call depth)
- Fewer wide bars (eliminated bottlenecks)

## Advanced Workflows

### Filtering Hot Paths Only

Show only functions consuming >1% of total time:

    #!/usr/bin/env python3
    import json
    import sys

    with open(sys.argv[1]) as f:
        data = json.load(f)

    total_time = data['summary']['total_duration_ms'] * 1000  # μs
    threshold = total_time * 0.01  # 1% threshold

    for func in data.get('function_profile', []):
        if func['total_time_us'] > threshold:
            print(f"{func['function']} {func['total_time_us']}")

Usage:

    $ python3 filter_hot_paths.py profile.json | ./FlameGraph/flamegraph.pl > hot-paths.svg

Result: Cleaner flamegraph focusing on significant bottlenecks.

### Differential Flamegraphs

Compare two profiles to see what changed:

    # Requires flamegraph-diff tool
    $ python3 renacer_to_folded.py before.json > before.folded
    $ python3 renacer_to_folded.py after.json > after.folded

    $ ./FlameGraph/difffolded.pl before.folded after.folded | ./FlameGraph/flamegraph.pl > diff.svg

Red regions: Functions slower in after.json Blue regions: Functions faster in after.json Gray regions: No significant change

### Color-Coding by Slow I/O

Customize flamegraph colors based on slow I/O count:

    #!/usr/bin/env python3
    import json
    import sys

    with open(sys.argv[1]) as f:
        data = json.load(f)

    for func in data.get('function_profile', []):
        slow_io_pct = func['slow_io_count'] / max(func['calls'], 1) * 100
        # Color code: red for >50% slow I/O, orange for >10%, default otherwise
        color = 'red' if slow_io_pct > 50 else ('orange' if slow_io_pct > 10 else 'default')

        print(f"{func['function']} {func['total_time_us']} # {color}")

Result: Flamegraph visually highlights I/O bottlenecks with color!

## Troubleshooting

### Empty Flamegraph

Problem: Flamegraph shows no data or "No function profiling data collected"

Cause: Function profiling not enabled or no debug symbols

Solution:

    # Ensure both --function-time and --source are used
    $ renacer --function-time --source --format json -- ./myapp > profile.json

    # Verify debug symbols
    $ file ./myapp  # Should show "with debug_info, not stripped"

### Incorrect Stack Heights

Problem: All functions appear at same level (no hierarchy)

Cause: Renacer currently exports immediate callers only, not full stacks

Workaround:

- Flamegraphs work best with full stack traces
- Renacer's function profiling shows caller-callee pairs
- For hierarchical visualization, manually construct stacks from profiling data

### Conversion Errors

Problem: `renacer_to_folded.py` fails with JSON parse errors

Cause: Invalid JSON output or large files

Solution:

    # Verify JSON is valid
    $ jq '.' profile.json > /dev/null

    # For large files, use streaming JSON parser
    $ cat profile.json | jq -c '.function_profile[]' | while read line; do
        # Process line by line
    done

## Limitations

Current limitations:

1. No native flamegraph export - Requires external tools (Brendan Gregg's scripts)
2. Immediate callers only - Full stack traces not yet supported
3. Time-based only - Cannot flamegraph by call count or other metrics (without custom scripts)

Future enhancements:

- Native folded stack export
- Full stack trace capture (multi-level unwinding)
- Direct SVG flamegraph generation

## Summary

Flamegraph visualization provides:

- ✅ Visual bottleneck identification via JSON export + external tools
- ✅ Interactive exploration with Brendan Gregg's flamegraph.pl
- ✅ Comparison workflows for before/after optimization analysis
- ✅ Custom color-coding based on slow I/O metrics
- ✅ Integration with existing flamegraph ecosystems

Workflow:

1. Profile with `--function-time --source --format json`
2. Convert JSON to folded stack format
3. Generate flamegraph SVG with external tools
4. Analyze and optimize

All export functionality tested in: `tests/sprint22_html_output_tests.rs`, function profiling in `tests/sprint13_function_profiling_tests.rs`

## Related

- Function Profiling - Parent chapter with basic usage
- Call Graph Analysis - Understanding function relationships
- I/O Bottleneck Detection - Identifying slow I/O for visualization
- Export to JSON/CSV - Data export workflows