Skip to main content

probe_ctree_counts/
probe_ctree_counts.rs

1//! Find the first function whose extracted ctree expression count disagrees with the
2//! Hex-Rays visitor count, then dump its structure. Diagnostic for the ARM64 extraction
3//! discrepancy.
4//!
5//!   `cargo run -p idakit --example probe_ctree_counts -- <db-copy.i64>`
6
7use idakit::prelude::*;
8
9fn dump(tree: &Ctree, node: idakit::decompiler::ctree::NodeRef, depth: usize) {
10    use idakit::decompiler::ctree::NodeRef;
11    let pad = "  ".repeat(depth);
12    let label = match node {
13        NodeRef::Expression(id) => format!("{:?}", tree.kind(id)),
14        NodeRef::Statement(id) => format!("{:?}", tree.statement_kind(id)),
15    };
16    let line: String = label
17        .lines()
18        .next()
19        .unwrap_or("")
20        .chars()
21        .take(90)
22        .collect();
23    println!("{pad}{line}");
24    for c in tree.children(node) {
25        dump(tree, c, depth + 1);
26    }
27}
28
29fn main() -> Result<(), Box<dyn std::error::Error>> {
30    let bin = std::env::args()
31        .nth(1)
32        .expect("usage: probe_ctree_counts <db.i64>");
33
34    Ida::run(move |ida| -> Result<(), Error> {
35        ida.call(move |idb| -> Result<(), Error> {
36            idb.open(&bin).run_auto(false).call()?;
37
38            let eas: Vec<_> = idb
39                .functions()
40                .map(|f| (f.address(), String::from(f.name())))
41                .collect();
42            let mut checked = 0usize;
43            let mut mismatches = 0usize;
44            let mut first_dumped = false;
45            for (address, name) in eas {
46                let Ok(cf) = idb.decompile(address) else {
47                    continue;
48                };
49                let Ok(tree) = cf.ctree() else { continue };
50                checked += 1;
51                let (visitor_total, expected) = cf.expr_extraction_expectation();
52                let extracted = tree.expressions().count() as i32;
53                if extracted != expected {
54                    mismatches += 1;
55                    println!(
56                        "MISMATCH {name} @ {address:#x}: extracted={extracted} expected={expected} \
57                         visitor={visitor_total} (elided empties {})",
58                        visitor_total - expected
59                    );
60                    if !first_dumped {
61                        first_dumped = true;
62                        println!("--- idakit render ---\n{}", tree.to_pseudocode());
63                        println!("--- structure ---");
64                        dump(
65                            &tree,
66                            idakit::decompiler::ctree::NodeRef::Statement(tree.root()),
67                            0,
68                        );
69                    }
70                }
71            }
72            println!("\n[probe] {checked} decompiled, {mismatches} mismatched");
73            idb.close(false);
74            Ok(())
75        })?
76    })??;
77    Ok(())
78}