Transpiler Integration

Renacer supports source mapping for transpiled code, allowing you to trace binaries back to their original high-level source (Python, C, TypeScript, etc.) instead of just the generated Rust code.

Overview

When you transpile code (e.g., Python → Rust via Depyler, C → Rust via Decy), Renacer can:

  • Map syscalls back to original source files (.py, .c, .ts)
  • Show original function names instead of generated ones
  • Display original line numbers from your source code
  • Track transpiler optimization decisions

Supported Transpilers

Depyler (Python → Rust)

# Transpile Python to Rust with source map
depyler transpile app.py --output app.rs --source-map app.map.json

# Compile with debug info
rustc app.rs -g -o app

# Trace with source mapping
renacer --transpiler-map app.map.json -- ./app

Output:

read(3, buf, 1024) = 42    [app.py:15 in read_config]  ← Original Python!

Decy (C → Rust)

# Transpile C to Rust with source map
decy convert main.c --output main.rs --source-map main.map.json

# Compile with debug info
rustc main.rs -g -o main

# Trace with source mapping
renacer --transpiler-map main.map.json -- ./main

Output:

write(1, "Hello", 5) = 5   [main.c:42 in printf_wrapper]  ← Original C!

Generic Transpiler Support

Renacer supports any transpiler that generates source maps in this format:

{
  "version": 1,
  "source_language": "python",
  "target_language": "rust",
  "mappings": [
    {
      "generated_file": "app.rs",
      "generated_line": 150,
      "original_file": "app.py",
      "original_line": 15,
      "original_function": "read_config",
      "transpiler_decision": {
        "optimization": "inline_small_function",
        "reasoning": "Function body <10 lines"
      }
    }
  ]
}

Source Map Format

Required Fields

  • version: Source map format version (currently 1)
  • source_language: Original language (e.g., "python", "c", "typescript")
  • target_language: Target language (typically "rust")
  • mappings: Array of line mappings

Mapping Entry

Each mapping entry contains:

{
  "generated_file": "output.rs",      // Generated Rust file
  "generated_line": 100,              // Line in generated code
  "original_file": "input.py",        // Original source file
  "original_line": 25,                // Line in original source
  "original_function": "my_function", // Original function name (optional)
  "transpiler_decision": {            // Optimization metadata (optional)
    "optimization": "vectorize_loop",
    "reasoning": "Simple iteration pattern detected"
  }
}

Optional Fields

  • original_function: Function name in original source
  • transpiler_decision: Metadata about transpiler optimizations
    • optimization: Name of optimization applied
    • reasoning: Human-readable explanation

Basic Usage

1. Simple Source Mapping

renacer --transpiler-map source.map.json -- ./app

Shows original source locations:

openat(AT_FDCWD, "/config.json", O_RDONLY) = 3
  [config.py:10 in load_settings]

read(3, buf, 1024) = 512
  [config.py:11 in load_settings]

2. Combined with DWARF

renacer --source --transpiler-map source.map.json -- ./app

Renacer prefers transpiler mappings over DWARF when available:

  1. Check transpiler map first
  2. Fall back to DWARF debug info if no mapping found
  3. Fall back to no source info if neither available

3. With Function Profiling

renacer --function-time --transpiler-map source.map.json -- ./app

Output:

Function Profiling Summary:
========================
Top 10 Hot Paths (by total time):
  1. load_settings [config.py:10]      - 45.2% (1.2s, 67 syscalls)
  2. process_data [main.py:25]         - 32.1% (850ms, 45 syscalls)
  3. write_output [output.py:100]      - 15.3% (400ms, 23 syscalls)

Original function names from your source code!

4. With OTLP Export

renacer \
  --transpiler-map source.map.json \
  --otlp-endpoint http://localhost:4317 \
  -- ./app

Spans include transpiler attributes:

{
  "source.file": "config.py",
  "source.line": 10,
  "source.function": "load_settings",
  "transpiler.source_language": "python",
  "transpiler.decision": "inline_small_function"
}

Advanced Features

Tracking Transpiler Decisions

Source maps can include optimization metadata:

{
  "generated_line": 200,
  "original_line": 50,
  "transpiler_decision": {
    "optimization": "simd_vectorization",
    "reasoning": "Loop with constant stride, vectorizable"
  }
}

View in traces:

renacer --transpiler-map source.map.json --show-transpiler-decisions -- ./app

Output:

read(3, buf, 8192) = 8192
  [data.py:50 in process_batch]
  💡 Transpiler: simd_vectorization (Loop with constant stride, vectorizable)

Multi-Language Projects

Support multiple transpiled modules:

# Combine source maps
renacer \
  --transpiler-map module1.map.json \
  --transpiler-map module2.map.json \
  --transpiler-map module3.map.json \
  -- ./app

Renacer automatically:

  • Merges all mappings
  • Detects conflicts (warns if same generated line maps to multiple sources)
  • Routes each syscall to correct source map

Ruchy Runtime Integration

Renacer integrates with Ruchy Runtime for transpiler decision tracking:

# Trace with Ruchy runtime context
renacer \
  --transpiler-map output.map.json \
  --ruchy-trace-decisions \
  --otlp-endpoint http://localhost:4317 \
  -- ./ruchy-transpiled-app

This links:

  • Syscalls → Original source lines
  • Source lines → Transpiler decisions
  • Decisions → Runtime performance
  • Performance → OTLP observability backend

Trueno SIMD Block Tracing

When tracing Trueno-accelerated code:

renacer \
  --transpiler-map trueno.map.json \
  --trace-simd-blocks \
  -- ./trueno-app

Renacer emits special spans for SIMD compute blocks:

Span: simd_block
  source.file: stats.py
  source.line: 100
  source.function: calculate_percentiles
  simd.instruction_set: AVX2
  simd.vector_width: 256
  compute.block_id: trueno_block_42

Generating Source Maps

From Depyler

depyler transpile input.py \
  --output output.rs \
  --source-map output.map.json \
  --track-decisions

From Decy

decy convert input.c \
  --output output.rs \
  --source-map output.map.json \
  --preserve-line-mapping

Custom Transpiler

If building your own transpiler, implement source map generation:

use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
struct SourceMap {
    version: u32,
    source_language: String,
    target_language: String,
    mappings: Vec<Mapping>,
}

#[derive(Serialize, Deserialize)]
struct Mapping {
    generated_file: String,
    generated_line: u32,
    original_file: String,
    original_line: u32,
    original_function: Option<String>,
    transpiler_decision: Option<Decision>,
}

#[derive(Serialize, Deserialize)]
struct Decision {
    optimization: String,
    reasoning: String,
}

// Generate mappings during transpilation
fn transpile_with_mapping() {
    let mut mappings = Vec::new();

    // For each line transformation
    mappings.push(Mapping {
        generated_file: "output.rs".to_string(),
        generated_line: 100,
        original_file: "input.py".to_string(),
        original_line: 25,
        original_function: Some("process_data".to_string()),
        transpiler_decision: Some(Decision {
            optimization: "loop_unrolling".to_string(),
            reasoning: "Fixed iteration count detected".to_string(),
        }),
    });

    let source_map = SourceMap {
        version: 1,
        source_language: "python".to_string(),
        target_language: "rust".to_string(),
        mappings,
    };

    // Write to file
    std::fs::write(
        "output.map.json",
        serde_json::to_string_pretty(&source_map).unwrap()
    ).unwrap();
}

Validation

Renacer validates source maps on load:

Version Check

Error: Unsupported source map version: 2 (expected: 1)

Solution: Update Renacer or regenerate source map with version 1.

Required Fields

Error: Missing required field: source_language

Solution: Ensure source map includes all required fields.

Line Number Bounds

Warning: Mapping references line 1000 in output.rs (file only has 500 lines)

Solution: Regenerate source map after modifying generated code.

Best Practices

1. Always Generate with Debug Symbols

# ✅ Good: Debug symbols + source map
rustc output.rs -g -o app
renacer --transpiler-map output.map.json -- ./app

# ❌ Bad: Source map without debug symbols (limited utility)
rustc output.rs -o app
renacer --transpiler-map output.map.json -- ./app

2. Keep Source Maps Up-to-Date

# Regenerate after each transpilation
depyler transpile app.py --output app.rs --source-map app.map.json

3. Include Function Names

{
  "original_function": "load_config",  // ✅ Helpful for profiling
  "original_function": null             // ❌ Less useful
}

4. Track Important Decisions

{
  "transpiler_decision": {
    "optimization": "vectorize_loop",      // ✅ Useful for debugging
    "reasoning": "Performance boost +40%"
  }
}

5. Combine with OTLP for Observability

# Full stack observability with original source
renacer \
  --source \
  --transpiler-map app.map.json \
  --otlp-endpoint http://localhost:4317 \
  -- ./app

Troubleshooting

Source Map Not Found

Error: Failed to read source map: No such file or directory

Solution:

# Verify file exists
ls -la output.map.json

# Use absolute path if needed
renacer --transpiler-map /absolute/path/to/output.map.json -- ./app

Mappings Not Applied

Warning: No mapping found for output.rs:150

Causes:

  1. Source map incomplete (missing lines)
  2. Source map out of date (code changed after generation)
  3. Generated code modified after transpilation

Solution: Regenerate source map.

Conflicting Mappings

Warning: Multiple mappings for output.rs:100 (using first)

Solution: Check for duplicate entries in source map:

# Validate source map
jq '.mappings[] | select(.generated_line == 100)' output.map.json

Original File Not Found

Warning: Original file not found: input.py

Solution: Ensure original source files are accessible:

# Use absolute paths in source map
# Or ensure files are in working directory

Performance Impact

Transpiler mapping overhead:

  • Loading source map: One-time cost at startup (<10ms for 10K mappings)
  • Lookup per syscall: <1μs (hash map lookup)
  • Total overhead: <0.1% (negligible)

Example: Python → Rust Workflow

Complete example with Depyler:

# 1. Write Python code
cat > app.py << 'EOF'
def read_config(path):
    with open(path) as f:
        return f.read()

def main():
    config = read_config("/etc/app.conf")
    print(f"Config: {config}")

if __name__ == "__main__":
    main()
EOF

# 2. Transpile to Rust with source map
depyler transpile app.py \
  --output app.rs \
  --source-map app.map.json \
  --track-decisions

# 3. Compile with debug symbols
rustc app.rs -g -o app

# 4. Trace with full observability
renacer \
  --source \
  --function-time \
  --transpiler-map app.map.json \
  --otlp-endpoint http://localhost:4317 \
  -- ./app

# Output shows Python source!
openat(AT_FDCWD, "/etc/app.conf", O_RDONLY) = 3
  [app.py:2 in read_config]

read(3, buf, 8192) = 156
  [app.py:3 in read_config]
  💡 Transpiler: buffered_io_optimization

Next Steps