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 (currently1)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 sourcetranspiler_decision: Metadata about transpiler optimizationsoptimization: Name of optimization appliedreasoning: 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:
- Check transpiler map first
- Fall back to DWARF debug info if no mapping found
- 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:
- Source map incomplete (missing lines)
- Source map out of date (code changed after generation)
- 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
- OpenTelemetry Integration - Export transpiler metadata
- Distributed Tracing - Trace across services
- Function Profiling - Profile with original names