Skip to content

Add code complexity metrics for Function - #8607

Open
UvuvDev wants to merge 2 commits into
devfrom
feature/code-complexity-metric
Open

UvuvDev wants to merge 2 commits into
devfrom
feature/code-complexity-metric

Conversation

@UvuvDev

@UvuvDev UvuvDev commented Oct 1, 2026 •

Copy link
Copy Markdown
Member

Adds a set of independent complexity metrics (cyclomatic, instruction count, token count, branch density, Halstead volume, nesting depth, cognitive complexity, a weighted composite, fan-out, code reference count, and transitive complexity) computable per-function via Function.get_complexity()/GetComplexity(), plus two example scripts demonstrating single-function and whole-binary reports.

I would really love to have some input here. This is ideally a discussion rather than a plain merge. I vibe coded all the metrics, so although they are mostly accurate there may be other metrics that I am not aware of.

The benefits of a PR like this are that we can have actual metrics to give to scripts and agents about the content of a function, which I think is especially applicable to RouteLLM style AI routing. If token efficiency is what we are going for we should support it as best we can. Routing small simple functions to a cheap local model and the complex ones to 6 Astra xhigh will save a ton on something like this firmware image with 76,000 functions:

image

I am in the process of building a C++ backend right now to speed up the code. It is performant enough for most use cases. It can still take 10 minutes for something like this modem firmware, but then again analysis took me 30 :)

https://github.com/UvuvDev/Code-Complexity-Plugin

I made a plugin for a UI end for testing and I can't lie, this really helps me. It gives me so much more information about the decompilation which I think is necessary. If I have a 76,000 function decompilation, why can't I sort by code references? Ghidra has had this for years and it was one of the things I use most often. Why can't I sort by branch density, and instruction count (which they don't have right now)? When I go into a firmware image, the first thing I am looking for is a printf (usually one of the top 5 in references), and the second thing I am looking for is main: for me to find main, it is just sorting for the highest call depth rather than going through an absurd amount of code. I think for customers like malware analysts, this has been less of a problem because the binary's are so much smaller. This was a 100 MB firmware image: it isn't uncommon for people to work on something this size.

I have talked to Jordan and he said we have too many UI widgets as is. I see his perspective, but I am curious if anyone has an idea of how we can integrate this somehow with what we have. My default would be the symbols sidebar, but there is not enough horizontal space to really incorporate that. Maybe triage view...?

UvuvDev and others added 2 commits September 29, 2026 17:49
Adds a set of independent complexity metrics (cyclomatic, instruction
count, token count, branch density, Halstead volume, nesting depth,
cognitive complexity, a weighted composite, fan-out, code reference
count, and transitive complexity) computable per-function via
Function.get_complexity()/GetComplexity(), plus two example scripts
demonstrating single-function and whole-binary reports.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…indings

Function::GetComplexity/GetComplexityMetricNames now call through new
BNGetFunctionComplexity/BNGetFunctionComplexityMetricNames C API
functions instead of each language binding reimplementing the metric
engine independently - the core is now the single canonical C/C++
implementation. Also adds "code_references" (count of code
cross-references to a function - the complement of fan_out) to the
core implementation, and adds Rust bindings
(Function::complexity/complexity_metric_names) that go through the
same C API.

The standalone complexity.cpp in this repo is removed since its logic
moved into the core; the C++ wrapper in function.cpp now validates the
metric name client-side before calling into the core, since the C ABI
boundary can't let an unrecognized-metric exception cross into other
language bindings (notably Rust).

Validated directly: a full incremental build of the real Binary Ninja
core links successfully with the new BNGetFunctionComplexity/
BNGetFunctionComplexityMetricNames symbols correctly exported: nm
confirms both. The updated C++ wrapper in function.cpp syntax-checks
cleanly against the new declarations, and `cargo check` on the Rust
crate passes with bindgen picking up the new C API automatically.

Corresponding core changes (core/complexity.cpp, a small addition to
core/function.h) live in the separate, private binaryninja core repo
and aren't part of this PR - flagging here since this PR's C++/Rust
code won't link without them.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@UvuvDev UvuvDev self-assigned this Oct 1, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant