use criterion::{Criterion, criterion_group, criterion_main};
use debtmap::analyzers::call_graph::debug::{CallGraphDebugger, DebugConfig, DebugFormat};
use debtmap::analyzers::call_graph::validation::CallGraphValidator;
use debtmap::priority::call_graph::{CallGraph, CallType, FunctionCall, FunctionId};
use std::hint::black_box;
use std::path::PathBuf;
fn create_test_graph(size: usize) -> CallGraph {
let mut graph = CallGraph::new();
for i in 0..size {
let func_id = FunctionId::new(
PathBuf::from(format!("file{}.rs", i % 10)),
format!("func_{}", i),
i * 10,
);
graph.add_function(func_id, i == 0, false, (i % 10) as u32, i * 5);
}
for i in 0..size - 1 {
let caller = FunctionId::new(
PathBuf::from(format!("file{}.rs", i % 10)),
format!("func_{}", i),
i * 10,
);
let callee = FunctionId::new(
PathBuf::from(format!("file{}.rs", (i + 1) % 10)),
format!("func_{}", i + 1),
(i + 1) * 10,
);
graph.add_call(FunctionCall {
caller,
callee,
call_type: CallType::Direct,
});
}
graph
}
fn bench_add_function(c: &mut Criterion) {
c.bench_function("add_function", |b| {
b.iter(|| {
let mut graph = CallGraph::new();
for i in 0..100 {
let func_id =
FunctionId::new(PathBuf::from("test.rs"), format!("func_{}", i), i * 10);
graph.add_function(black_box(func_id), false, false, 5, 50);
}
});
});
}
fn bench_add_call(c: &mut Criterion) {
c.bench_function("add_call", |b| {
let mut graph = create_test_graph(100);
b.iter(|| {
let call = FunctionCall {
caller: FunctionId::new(PathBuf::from("test.rs"), "caller".to_string(), 10),
callee: FunctionId::new(PathBuf::from("test.rs"), "callee".to_string(), 20),
call_type: CallType::Direct,
};
graph.add_call(black_box(call));
});
});
}
fn bench_get_callees(c: &mut Criterion) {
let graph = create_test_graph(1000);
let func_id = FunctionId::new(PathBuf::from("file0.rs"), "func_0".to_string(), 0);
c.bench_function("get_callees", |b| {
b.iter(|| {
graph.get_callees(black_box(&func_id));
});
});
}
fn bench_transitive_callees(c: &mut Criterion) {
let graph = create_test_graph(100);
let func_id = FunctionId::new(PathBuf::from("file0.rs"), "func_0".to_string(), 0);
c.bench_function("get_transitive_callees_depth_3", |b| {
b.iter(|| {
graph.get_transitive_callees(black_box(&func_id), 3);
});
});
c.bench_function("get_transitive_callees_depth_10", |b| {
b.iter(|| {
graph.get_transitive_callees(black_box(&func_id), 10);
});
});
}
fn bench_criticality_calculation(c: &mut Criterion) {
let graph = create_test_graph(500);
let func_id = FunctionId::new(PathBuf::from("file5.rs"), "func_50".to_string(), 500);
c.bench_function("calculate_criticality", |b| {
b.iter(|| {
graph.calculate_criticality(black_box(&func_id));
});
});
}
fn bench_delegation_detection(c: &mut Criterion) {
let graph = create_test_graph(200);
let func_id = FunctionId::new(PathBuf::from("file2.rs"), "func_20".to_string(), 200);
c.bench_function("detect_delegation_pattern", |b| {
b.iter(|| {
graph.detect_delegation_pattern(black_box(&func_id));
});
});
}
fn bench_cross_file_resolution(c: &mut Criterion) {
c.bench_function("resolve_cross_file_calls", |b| {
b.iter(|| {
let mut graph = create_test_graph(500);
for i in 0..50 {
let call = FunctionCall {
caller: FunctionId {
file: PathBuf::from(format!("file{}.rs", i % 10)),
name: format!("caller_{}", i),
line: i * 10,
module_path: String::new(),
},
callee: FunctionId {
file: PathBuf::from("unknown.rs"),
name: format!("unresolved_{}", i),
line: 0, module_path: String::new(),
},
call_type: CallType::Direct,
};
graph.add_call(call);
}
graph.resolve_cross_file_calls();
});
});
}
fn bench_debug_mode_overhead(c: &mut Criterion) {
let graph = create_test_graph(500);
c.bench_function("validation_baseline", |b| {
b.iter(|| {
CallGraphValidator::validate(black_box(&graph));
});
});
c.bench_function("validation_with_debug", |b| {
b.iter(|| {
let debug_config = DebugConfig {
show_successes: false,
show_timing: true,
max_candidates_shown: 5,
format: DebugFormat::Text,
filter_functions: None,
};
let mut debugger = CallGraphDebugger::new(debug_config);
debugger.finalize_statistics();
CallGraphValidator::validate(black_box(&graph));
});
});
c.bench_function("debug_report_generation_text", |b| {
b.iter(|| {
let debug_config = DebugConfig {
show_successes: false,
show_timing: true,
max_candidates_shown: 5,
format: DebugFormat::Text,
filter_functions: None,
};
let mut debugger = CallGraphDebugger::new(debug_config);
debugger.finalize_statistics();
let mut output = Vec::new();
let _ = debugger.write_report(&mut output);
});
});
c.bench_function("debug_report_generation_json", |b| {
b.iter(|| {
let debug_config = DebugConfig {
show_successes: false,
show_timing: true,
max_candidates_shown: 5,
format: DebugFormat::Json,
filter_functions: None,
};
let mut debugger = CallGraphDebugger::new(debug_config);
debugger.finalize_statistics();
let mut output = Vec::new();
let _ = debugger.write_report(&mut output);
});
});
}
fn bench_validation_operations(c: &mut Criterion) {
let graph = create_test_graph(1000);
c.bench_function("validate_call_graph_1000_functions", |b| {
b.iter(|| {
CallGraphValidator::validate(black_box(&graph));
});
});
let small_graph = create_test_graph(100);
c.bench_function("validate_call_graph_100_functions", |b| {
b.iter(|| {
CallGraphValidator::validate(black_box(&small_graph));
});
});
}
criterion_group!(
benches,
bench_add_function,
bench_add_call,
bench_get_callees,
bench_transitive_callees,
bench_criticality_calculation,
bench_delegation_detection,
bench_cross_file_resolution,
bench_debug_mode_overhead,
bench_validation_operations
);
criterion_main!(benches);