Skip to content

Commit

Permalink
caller: add cfg-stats flag
Browse files Browse the repository at this point in the history
  • Loading branch information
Valentin Obst committed Oct 16, 2024
1 parent 0b260ce commit 41eac1c
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 3 deletions.
1 change: 1 addition & 0 deletions src/caller/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ rust-version = "1.81"
[dependencies]
clap = { version = "4.0.32", features = ["derive"] }
cwe_checker_lib = { path = "../cwe_checker_lib" }
serde = {version = "1.0", features = ["derive", "rc"]}
serde_json = "1.0"
directories = "5.0.1"
anyhow = "1.0"
85 changes: 85 additions & 0 deletions src/caller/src/cfg_stats.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use cwe_checker_lib::analysis::graph::{
call::{CallGraph, CgNode},
intraprocedural_cfg::IntraproceduralCfg,
};
use cwe_checker_lib::intermediate_representation::{Program, Tid};

use std::collections::HashMap;

use serde::Serialize;

#[derive(Serialize)]
pub struct CfgStats<'a> {
internal_fns: HashMap<String, IntFnCfgStats>,
external_fns: HashMap<String, ExtFnCfgStats<'a>>,
}

#[derive(Serialize, Debug, PartialEq, Eq)]
struct IntFnCfgStats {
cyclomatic_complexity: u32,
flattening_score: u32,
num_bb: u64,
num_insn: u64,
}

impl IntFnCfgStats {
fn new(cfg: &IntraproceduralCfg) -> Self {
Self {
cyclomatic_complexity: cfg.cyclomatic_complexity(),
flattening_score: cfg.flattening_score(),
num_bb: cfg.num_blocks(),
num_insn: cfg.num_insn(),
}
}
}

#[derive(Serialize, Debug, PartialEq, Eq)]
struct ExtFnCfgStats<'a> {
/// Number of call sites where this function may be called.
num_cs: u64,
/// Name of the function.
name: &'a str,
}

impl<'a> ExtFnCfgStats<'a> {
fn new(p: &'a Program, cg: &CallGraph<'_>, f: &Tid) -> Self {
Self {
num_cs: cg.callers(f).map(|(_, edge)| edge.num_cs()).sum(),
name: &p.extern_symbols.get(f).unwrap().name,
}
}
}

impl<'a> CfgStats<'a> {
/// Computes some CFG statistics of the program `p`.
pub fn new(p: &'a Program) -> Self {
let cg = CallGraph::new_with_full_cfgs(p);

let mut internal_fns = HashMap::new();
let mut external_fns = HashMap::new();

for f in cg.nodes() {
match f {
CgNode::Function(term, cfg) => {
assert_eq!(
internal_fns.insert(term.tid.to_string(), IntFnCfgStats::new(cfg)),
None,
"Function TIDs are not unique."
);
}
CgNode::ExtFunction(tid) => {
assert_eq!(
external_fns.insert(tid.to_string(), ExtFnCfgStats::new(p, &cg, tid)),
None,
"Function TIDs are not unique."
);
}
}
}

Self {
internal_fns,
external_fns,
}
}
}
20 changes: 17 additions & 3 deletions src/caller/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//! This crate defines the command line interface for the cwe_checker.
//! General documentation about the cwe_checker is contained in the [`cwe_checker_lib`] crate.
//! Command line interface for the cwe_checker.
//!
//! General documentation about the cwe_checker is contained in the
//! [`cwe_checker_lib`] crate.
extern crate cwe_checker_lib; // Needed for the docstring-link to work

Expand All @@ -19,6 +21,8 @@ use std::convert::From;
use std::path::PathBuf;
use std::ops::Deref;

mod cfg_stats;

#[derive(ValueEnum, Clone, Debug, Copy)]
/// Selects which kind of debug output is displayed.
pub enum CliDebugMode {
Expand Down Expand Up @@ -166,6 +170,10 @@ struct CmdlineArgs {
/// of invoking Ghidra.
#[arg(long, hide(true))]
pcode_raw: Option<String>,

/// Print some statistics and metrics of the IR-program's CFG and exit.
#[arg(long, hide(true))]
cfg_stats: bool,
}

impl From<&CmdlineArgs> for debug::Settings {
Expand Down Expand Up @@ -241,10 +249,16 @@ fn run_with_ghidra(args: &CmdlineArgs) -> Result<(), Error> {

if debug_settings.should_debug(debug::Stage::CallGraph) {
// TODO: Move once call graph is used somewhere else.
let cg = graph::call::CallGraph::new(&project.program.term);
let cg = graph::call::CallGraph::new(&project.program);
debug_settings.print_compact_json(&cg, debug::Stage::CallGraph);
}

if args.cfg_stats {
let cfg_stats = cfg_stats::CfgStats::new(&project.program);
println!("{:#}", serde_json::to_value(cfg_stats)?);
return Ok(());
}

// Filter the modules to be executed.
if let Some(ref partial_module_list) = args.partial {
filter_modules_for_partial_run(&mut modules, partial_module_list);
Expand Down

0 comments on commit 41eac1c

Please sign in to comment.