use std::collections::BTreeMap;
use crate::cm_tools::tools::tool_param_types::BacktraceAnalyzeArgs;
fn collect_backtrace_frame(
line: &str,
crate_hint: Option<&str>,
frame_hits: &mut Vec<String>,
module_count: &mut BTreeMap<String, usize>,
) {
if !line.contains("::") {
return;
}
if let Some(hint) = crate_hint {
if !line.contains(hint) {
return;
}
} else if is_noise_frame(line) {
return;
}
frame_hits.push(line.to_string());
let module = line
.split("::")
.take(2)
.collect::<Vec<_>>()
.join("::")
.trim()
.to_string();
if !module.is_empty() {
*module_count.entry(module).or_insert(0) += 1;
}
}
fn format_backtrace_analysis(
frame_hits: &[String],
module_count: BTreeMap<String, usize>,
) -> String {
if frame_hits.is_empty() {
return "未识别到可分析的业务调用栈帧。可尝试传入 crate_hint(如你的 crate 名)。"
.to_string();
}
let first = frame_hits.first().cloned().unwrap_or_default();
let mut top_modules = module_count.into_iter().collect::<Vec<_>>();
top_modules.sort_by_key(|b| std::cmp::Reverse(b.1));
let mut out = String::new();
out.push_str("backtrace 分析结果:\n");
out.push_str(&format!("- 首个可疑业务帧: {}\n", first));
out.push_str("- 主要模块命中:\n");
for (name, count) in top_modules.into_iter().take(5) {
out.push_str(&format!(" - {}: {} 次\n", name, count));
}
out.push_str(
"- 建议: 优先从首个可疑业务帧对应函数开始排查参数、unwrap、索引越界和并发共享状态。",
);
out
}
pub fn rust_backtrace_analyze(args_json: &str) -> String {
let parsed = match crate::cm_tools::tools::parse_args_json(args_json) {
Ok(v) => v,
Err(e) => return e,
};
let args: BacktraceAnalyzeArgs = match serde_json::from_value(parsed) {
Ok(a) => a,
Err(e) => return format!("参数解析错误: {e}"),
};
let text = match args.backtrace.trim() {
s if !s.is_empty() => s,
_ => return "错误:缺少 backtrace 参数".to_string(),
};
let crate_hint = args
.crate_hint
.as_deref()
.map(str::trim)
.filter(|s| !s.is_empty());
let mut frame_hits: Vec<String> = Vec::new();
let mut module_count: BTreeMap<String, usize> = BTreeMap::new();
for line in text.lines() {
let l = line.trim();
if l.is_empty() {
continue;
}
collect_backtrace_frame(l, crate_hint, &mut frame_hits, &mut module_count);
}
format_backtrace_analysis(&frame_hits, module_count)
}
fn is_noise_frame(line: &str) -> bool {
let noise = [
"std::",
"core::",
"tokio::",
"alloc::",
"panic_unwind",
"backtrace",
];
noise.iter().any(|n| line.contains(n))
}