Ilia Bider

140 papers A 2B 1C 2Misc 4Journal 23Unranked 94
YearRankTypeTitle / Venue / Authors
2025 J jnl
Commun. Assoc. Inf. Syst.
Jonas Van Riel, Geert Poels, Georgios Koutsopoulos, Rodrigo F. Calhau, Ilia Bider, Erik Perjons, Yves Wautelet, Konstantinos Tsilionis
2025 conf
BIR Workshops
Ilia Bider
2025 conf
RCIS (2)
Toomas Saarsen, Ilia Bider
2025 conf
BIR
Ilia Bider, Martin Henkel, Erik Perjons
2025 conf
ER (Workshops)
Ilia Bider, Erik Perjons
2025 conf
ER (Companion)
Ilia Bider
2024 conf
RCIS (2)
Ilia Bider, Siim Langel
2024 conf
STPIS
Ilia Bider
2024 conf
BIR
Ilia Bider, Erik Perjons
2024 Misc conf
PoEM
Erik Perjons, Ilia Bider, Martin Henkel
2024 conf
BPMDS/EMMSAD@CAiSE
Ilia Bider, Erik Perjons
2024 conf
CBI
Victoria Klyukina, Erik Perjons, Ilia Bider
2023 conf
PoEM Companion
Ilia Bider, Erik Perjons
2023 conf
PoEM Companion
Ilia Bider, Steven Leego, Erik Perjons
2023 conf
BIR
Steven Leego, Ilia Bider
2023 J jnl
Complex Syst. Informatics Model. Q.
Ilia Bider
2023 conf
CENTERIS/ProjMAN/HCist
Ilia Bider
2023 conf
EDEWC
Ilia Bider, Erik Perjons
2023 conf
ER (Workshops)
Ilia Bider, Erik Perjons
2023 conf
BIR Workshops
Giorina Maratsi, Ilia Bider
2022 ch.
Domain-Specific Conceptual Modeling
Ilia Bider, Erik Perjons, Victoria Klyukina
2021 conf
EDOC Workshops
Ilia Bider, Erik Perjons, Dominik Bork
2021 conf
ICEIS (2)
Victoria Klyukina, Ilia Bider, Erik Perjons
2021 conf
BPM (PhD/Demos)
Ilia Bider, Dominik Bork, Erik Perjons
2021 J jnl
Complex Syst. Informatics Model. Q.
Ilia Bider, Erik Perjons
2021 conf
ICEIS (Revised Selected Papers)
Victoria Klyukina, Ilia Bider, Erik Perjons
2021 Misc conf
ICTERI
Ilia Bider, Erik Perjons, Dominik Bork
2021 conf
CBI (2)
Steven Leego, Ilia Bider
2020 J jnl
Complex Syst. Informatics Model. Q.
Ilia Bider, Erik Perjons, Paul Johannesson
2020 B conf
RCIS
Ilia Bider
2020 J jnl
Complex Syst. Informatics Model. Q.
Ilia Bider, Gil Regev, Erik Perjons
2019 conf
CBI (2)
Erik Perjons, Ilia Bider, Paul Johannesson
2019 conf
BPMDS/EMMSAD@CAiSE
Ilia Bider, Arian Chalak
2019 conf
STPIS@ECIS
Ilia Bider, Gil Regev, Erik Perjons
2019 conf
ICEIS (Revised Selected Papers)
Ilia Bider, Azeem Lodhi
2019 conf
WorldCIST (3)
Martin Henkel, Ilia Bider
2019 ed.
STPIS@ECIS
Stewart Kowalski, Peter M. Bednar, Alexander Nolte, Ilia Bider
2019 J jnl
Complex Syst. Informatics Model. Q.
Peter M. Bednar, Ilia Bider
2019 J jnl
Softw. Syst. Model.
Rainer Schmidt, Ilia Bider
2019 conf
BPMDS/EMMSAD@CAiSE
Toomas Saarsen, Ilia Bider, Erik Perjons
2019 conf
ICEIS (2)
Ilia Bider, Azeem Lodhi
2019 conf
BPM (Blockchain and CEE Forum)
Martin Henkel, Georgios Koutsopoulos, Ilia Bider, Erik Perjons
2019 conf
ER Workshops
Ilia Bider, Erik Perjons, Paul Johannesson
2019 conf
ER Forum/Posters/Demos
Martin Henkel, Georgios Koutsopoulos, Ilia Bider, Erik Perjons
2019 conf
VMBO
Ilia Bider, Erik Perjons
2018 conf
BPMDS/EMMSAD@CAiSE
Georgios Koutsopoulos, Ilia Bider
2018 conf
EDOC Workshops
Ilia Bider, Martin Henkel
2018 conf
BIR
Ilia Bider, Erik Perjons
2018 conf
EDOC Workshops
Ilia Bider, Georgios Koutsopoulos
2018 ed.
STPIS@CAiSE
Stewart Kowalski, Peter M. Bednar, Ilia Bider
2018 J jnl
Complex Syst. Informatics Model. Q.
Ilia Bider, Paul Johannesson, Erik Perjons
2018 conf
BIR Workshops
Ilia Bider, Erik Perjons
2018 conf
STPIS@CAiSE
Ilia Bider, Erik Perjons
2018 J jnl
Complex Syst. Informatics Model. Q.
Ilia Bider, Henning Otto, Saga Willysson
2018 conf
EDOC Workshops
Ilia Bider, Victoria Klyukina
2017 J jnl
Softw. Syst. Model.
Ilia Bider, Erik Perjons, Mturi Elias, Paul Johannesson
2017 conf
STPIS@CAiSE
Ilia Bider, Erik Perjons
2017 conf
STPIS@CAiSE
Ilia Bider
2017 ed.
STPIS@CAiSE
Stewart Kowalski, Peter M. Bednar, Ilia Bider
2017 conf
SCME-iStarT@ER
Martin Henkel, Ilia Bider, Erik Perjons, Filip Martensson, Martin Zainali
2017 conf
BPMDS/EMMSAD@CAiSE
Georgios Koutsopoulos, Ilia Bider
2017 J jnl
Complex Syst. Informatics Model. Q.
Ilia Bider, Erik Perjons
2017 conf
RADAR+EMISA@CAiSE
Ilia Bider, Erik Perjons
2016 J jnl
Inf. Syst. E Bus. Manag.
Ilia Bider, Amin Jalali
2016 conf
ICEIS (1)
Ilia Bider, Oscar Söderberg
2016 ed.
BMMDS/EMMSAD
Rainer Schmidt, Wided Guédria, Ilia Bider, Sérgio Guerreiro
2016 conf
EDOC Workshops
Ilia Bider, Amin Jalali
2016 conf
EDOC Workshops
Irina Rychkova, Ilia Bider, Keith Svenson
2016 conf
ICEIS (Revised Selected Papers)
Ilia Bider, Oscar Söderberg
2016 ed.
STPIS@CAiSE
Stewart Kowalski, Peter M. Bednar, Ilia Bider
2016 conf
STPIS@CAiSE
Ilia Bider, Sofia Olsson, Erik Perjons
2016 conf
ER Workshops
Ilia Bider, David Rogers
2015 conf
BMMDS/EMMSAD
Ilia Bider, Amin Jalali, David Söderström
2015 J jnl
Softw. Syst. Model.
Ilia Bider, Erik Perjons
2015 conf
STPIS@CAiSE
Ilia Bider
2015 C conf
ICGSE
Ilia Bider, Henning Otto
2015 ed.
STPIS@CAiSE
Stewart Kowalski, Peter M. Bednar, Ilia Bider, Jeffy Mwakalinga
2015 conf
ER Workshops
Ilia Bider, Martin Henkel, Stewart Kowalski, Erik Perjons
2015 J jnl
Interact. Technol. Smart Educ.
Ilia Bider, Martin Henkel, Stewart Kowalski, Erik Perjons
2015 conf
MCETECH
Ilia Bider, Martin Henkel, Stewart Kowalski, Erik Perjons
2015 conf
Business Process Management Workshops
Ilia Bider
2015 conf
BMMDS/EMMSAD
Magnus Josefsson, Kim Widman, Ilia Bider
2014 conf
BMMDS/EMMSAD
Ilia Bider, Stewart Kowalski
2014 conf
BIR
Ilia Bider
2014 conf
CAiSE Workshops
Martin Henkel, Ilia Bider, Erik Perjons
2014 ed.
BMMDS/EMMSAD
Ilia Bider, Khaled Gaaloul, John Krogstie, Selmin Nurcan, Henderik Alex Proper, Rainer Schmidt, Pnina Soffer
2014 conf
EDOC Workshops
Irina Rychkova, Ilia Bider, Keith D. Swenson
2014 conf
EDOC Workshops
Amin Jalali, Ilia Bider
2014 conf
BMMDS/EMMSAD
Mturi Elias, Ilia Bider, Paul Johannesson
2013 conf
OTM Workshops
Irina Rychkova, Ilia Bider, Keith D. Swenson
2013 conf
OTM Workshops
Ilia Bider, Amin Jalali, Jens Ohlsson
2013 conf
PoEM (Short Papers)
Ilia Bider, Athanasios Karapantelakis, Nirjal Khadka
2013 ed.
BMMDS/EMMSAD
Selmin Nurcan, Henderik Alex Proper, Pnina Soffer, John Krogstie, Rainer Schmidt, Terry A. Halpin, Ilia Bider
2013 conf
BMMDS/EMMSAD
Ilia Bider, Erik Perjons, Zakria Riaz Dar
2013 conf
DESRIST
Ilia Bider, Paul Johannesson, Erik Perjons
2012 C conf
ICIS
Ilia Bider, Paul Johannesson, Erik Perjons, Lena Johansson
2012 conf
Business Process Management Workshops
Ilia Bider, Paul Johannesson, Erik Perjons
2012 ed.
BMMDS/EMMSAD
Ilia Bider, Terry A. Halpin, John Krogstie, Selmin Nurcan, Erik Proper, Rainer Schmidt, Pnina Soffer, Stanislaw Wrycza
2012 conf
BMMDS/EMMSAD
Ilia Bider, Erik Perjons
2012 conf
PoEM (Short Papers)
Ilia Bider, Erik Perjons
2012 J jnl
J. Softw. Evol. Process.
Selmin Nurcan, Rainer Schmidt, Ilia Bider, Gil Regev
2012 conf
Business Process Management Workshops
Ilia Bider
2012 Misc conf
PoEM
Ilia Bider, Erik Perjons, Mturi Elias
2011 ed.
BMMDS/EMMSAD
Terry A. Halpin, Selmin Nurcan, John Krogstie, Pnina Soffer, Erik Proper, Rainer Schmidt, Ilia Bider
2011 A conf
CAiSE
Ilia Bider, Paul Johannesson, Rainer Schmidt
2011 conf
BMMDS/EMMSAD
Ilia Bider
2011 Misc conf
PoEM
Ilia Bider, Gene Bellinger, Erik Perjons
2010 conf
Business Process Management Workshops
Ilia Bider, Paul Johannesson, Erik Perjons
2010 ed.
BMMDS/EMMSAD
Ilia Bider, Terry A. Halpin, John Krogstie, Selmin Nurcan, Erik Proper, Rainer Schmidt, Roland Ukor
2010 conf
BMMDS/EMMSAD
Ilia Bider, Paul Johannesson, Erik Perjons
2009 J jnl
Commun. Assoc. Inf. Syst.
Ilia Bider
2008 J jnl
Int. J. Bus. Process. Integr. Manag.
Ilia Bider, Alexey Striy
2007 J jnl
Softw. Process. Improv. Pract.
Gil Regev, Ilia Bider, Alain Wegmann
2006 conf
PAKM
Ilia Bider, Lena Johansson, Erik Perjons, Alexey Striy
2006 conf
BPMDS
Ilia Bider, Alexey Striy
2005 J jnl
Softw. Process. Improv. Pract.
Tomas Andersson, Ilia Bider, Rogier Svensson
2005 conf
Wissensmanagement (LNCS Volume)
Birger Andersson, Ilia Bider, Erik Perjons
2005 conf
Wissensmanagement
Birger Andersson, Ilia Bider, Erik Perjons
2005 J jnl
Requir. Eng.
Gil Regev, Pnina Soffer, Ilia Bider
2005 J jnl
Softw. Process. Improv. Pract.
Gil Regev, Pnina Soffer, Ilia Bider
2005 J jnl
Bus. Process. Manag. J.
Birger Andersson, Ilia Bider, Paul Johannesson, Erik Perjons
2004 conf
CAiSE Workshops (2)
Ilia Bider
2004 conf
PAKM
Birger Andersson, Ilia Bider, Erik Perjons
2004 conf
CAiSE Workshops (2)
Tomas Andersson, Ilia Bider, Rogier Svensson
2004 conf
CAiSE Workshops (2)
Ilia Bider, Gil Regev, Pnina Soffer
2004 ed.
GBMP
Ilia Bider, Paul Johannesson
2003 conf
CAiSE Workshops
Ilia Bider
2003 conf
CAiSE Workshops
Ian F. Alexander, Ilia Bider, Gil Regev
2002 conf
ICEIS
Ilia Bider
2002 conf
GBPM
Ilia Bider, Paul Johannesson, Erik Perjons
2002 conf
ICEIS
Ilia Bider, Maxim Khomyakov
2002 conf
GBPM
Ilia Bider
2002 A conf
ER
Ilia Bider, Paul Johannesson
2002
Ilia Bider
2000 conf
OOIS
Maxim Khomyakov, Ilia Bider
2000 conf
CRIWG
Ilia Bider, Maxim Khomyakov
2000 J jnl
Autom. Softw. Eng.
Ilia Bider, Maxim Khomyakov, E. Pushchinsky
1998 conf
ECOOP Workshops
Ilia Bider, Maxim Khomyakov
1998 conf
ECOOP Workshops
Ilia Bider, Maxim Khomyakov
1997 conf
ECOOP Workshops
Ilia Bider, Maxim Khomyakov
redb/extractors/decompiler/bninja/analysis/disassembly.py
← Index redb/extractors/decompiler/bninja/analysis/disassembly.py python
import re
import time

import binaryninja
from binaryninja.enums import (
    InstructionTextTokenType,
)

# Support both package and standalone imports
try:
    from ..function_type import FunctionTypeAnalysis
    from ..utils.hashes import calculate_sha256
except ImportError:
    # Fallback to absolute imports (for multiprocessing spawned processes)
    from redb.extractors.decompiler.bninja.function_type import FunctionTypeAnalysis
    from redb.extractors.decompiler.bninja.utils.hashes import calculate_sha256


class DisassemblyAnalysis:
    INVALID_STACK_SIZE = -1

    def __init__(self, arch, function, bv, logger):
        self.arch = arch
        self.function = function
        self.bv = bv
        self.logger = logger
        if self.function is not None and hasattr(self.function, "instructions"):
            self.instructions = self.function.instructions
        else:
            self.instructions = []
        self.errors = []
        return

    def log_error(
        self, message, function_name, address, exception=None, error_location="unknown"
    ):
        """Log an error during processing."""
        error_msg = f"Error in function {function_name} at {address}: {message}"
        if exception:
            error_msg += f" - {str(exception)}"
        self.logger.error(error_msg)

        # Add to errors list
        error = {
            "function_name": function_name,
            "function_address": str(address),
            "error_location": error_location,
            "error_message": message,
            "error_details": str(exception) if exception else "",
            "error_type": type(exception).__name__ if exception else "Unknown",
            "timestamp": int(time.time() * 1000),
        }
        self.errors.append(error)

    def get_json(self):
        try:
            # Build disassembly string and normalized versions
            disassembly_builder = [[], []]  # Address and instruction text

            # Create a dictionary mapping addresses to instruction tokens
            instr_tokens_by_addr = {}
            for instr_tokens, addr in self.instructions:
                instr_tokens_by_addr[addr] = instr_tokens

            addresses = sorted(instr_tokens_by_addr.keys())
            for address in addresses:
                # Original disassembly with addresses
                # instr_tokens, address = instruction
                instr_tokens = instr_tokens_by_addr[address]
                disassembly_builder[0].append(address)
                disassembly_builder[1].append("".join(map(str, instr_tokens)))

            # Join with newlines
            disassembly_str = "\n".join(disassembly_builder[1])
            disassembly_with_addresses = "\n".join(
                f"{hex(address)}: {instr_text}"
                for address, instr_text in zip(
                    disassembly_builder[0], disassembly_builder[1], strict=False
                )
            )

            disassembly_json = {
                "disassembled_function_hash": calculate_sha256(disassembly_str),
                "disassembled_function": disassembly_with_addresses,
                "disassembled_function_no_addresses": disassembly_str,
                "disassembled_function_name": self.function.name,
                "disassembled_function_address": self.function.start,
                "instructions_count": len(instr_tokens_by_addr.keys()),
                "function_type": FunctionTypeAnalysis(self.function)
                .get_function_type()
                .name,
            }

            # Add additional metrics
            type_frequencies = self.collect_instruction_types()
            disassembly_json["instructions_types"] = list(type_frequencies.keys())
            disassembly_json["control_flow_count"] = (
                self.count_control_flow_instructions()
            )
            disassembly_json["memory_access_pattern"] = self.collect_memory_patterns()
            disassembly_json["register_usage"] = self.collect_register_usage()
            disassembly_json["data_references_count"] = self.count_data_references()
            disassembly_json["max_block_size"] = self.compute_max_block_size()
            disassembly_json["num_calls"] = self.compute_num_calls()
            disassembly_json["stack_size"] = self.estimate_stack_size()

            return disassembly_json, self.errors

        except Exception as e:
            self.log_error(
                "Failed to collect instruction types",
                self.function.name,
                self.function.start,
                e,
                "collect_instruction_types",
            )
            raise ValueError(e) from e

    def collect_instruction_types(self):
        """Collect instruction type frequencies from a function."""
        type_frequencies = {}

        try:
            # Iterate through all instructions in the function
            for instruction in self.instructions:
                instr_tokens = instruction[0]  # Get the instruction tokens

                # Extract the mnemonic from the instruction tokens
                mnemonic = None
                for token in instr_tokens:
                    if token.type == InstructionTextTokenType.InstructionToken:
                        mnemonic = token.text
                        break

                if not mnemonic:
                    continue

                # Use normalize_opcode to get standardized opcode
                normalized = self.normalize_opcode(mnemonic)

                # Get category from opcode_categories or use the instruction type directly
                category = self.arch.opcode_categories.get(normalized)
                if category:
                    self._increment_frequency(type_frequencies, category)

        except Exception as e:
            self.log_error(
                "Failed to collect instruction types",
                self.function.name,
                self.function.start,
                e,
                "collect_instruction_types",
            )

        return type_frequencies

    def normalize_opcode(self, opcode):
        return opcode.upper()

    def collect_memory_patterns(self):
        """Collect memory access patterns from a function."""
        patterns = []
        try:
            for instruction in self.instructions:
                instr_tokens = instruction[0]

                # We need to capture memory operands between BeginMemoryOperandToken and EndMemoryOperandToken
                in_memory_operand = False
                memory_operand_text = ""

                for token in instr_tokens:
                    if token.type == InstructionTextTokenType.BeginMemoryOperandToken:
                        in_memory_operand = True
                        memory_operand_text = ""
                    elif token.type == InstructionTextTokenType.EndMemoryOperandToken:
                        in_memory_operand = False

                        # Process the captured memory operand text
                        if memory_operand_text:
                            # Categorize memory access pattern
                            if (
                                "+" in memory_operand_text
                                and "*" in memory_operand_text
                            ):
                                if "MEM_SCALED_INDEX" not in patterns:
                                    patterns.append("MEM_SCALED_INDEX")
                            elif (
                                "+" in memory_operand_text or "-" in memory_operand_text
                            ):
                                if "MEM_BASE_OFFSET" not in patterns:
                                    patterns.append("MEM_BASE_OFFSET")
                            else:
                                if "MEM_DIRECT" not in patterns:
                                    patterns.append("MEM_DIRECT")

                            # Check for stack accesses
                            if any(
                                reg in memory_operand_text
                                for reg in ["SP", "BP", "ESP", "EBP", "RSP", "RBP"]
                            ):
                                if "MEM_STACK" not in patterns:
                                    patterns.append("MEM_STACK")
                            # Check for string operations
                            elif (
                                any(
                                    reg in memory_operand_text
                                    for reg in ["SI", "DI", "ESI", "EDI", "RSI", "RDI"]
                                )
                                and "MEM_STRING" not in patterns
                            ):
                                patterns.append("MEM_STRING")
                    elif in_memory_operand:
                        # Accumulate token text while inside a memory operand
                        memory_operand_text += token.text
        except Exception as e:
            self.log_error(
                "Failed to collect memory patterns",
                self.function.name,
                self.function.start,
                e,
                "collect_memory_patterns",
            )
        return patterns

    def collect_register_usage(self):
        """Collect register usage from a function."""
        registers = []
        try:
            # Define register groups we're interested in tracking
            register_groups = {
                "GPR": [
                    "RAX",
                    "RBX",
                    "RCX",
                    "RDX",
                    "R9",
                    "R10",
                    "R11",
                    "R12",
                    "R13",
                    "R14",
                    "R15",
                    "EAX",
                    "EBX",
                    "ECX",
                    "EDX",
                    "R9D",
                    "R10D",
                    "R11D",
                    "R12D",
                    "R13D",
                    "R14D",
                    "AX",
                    "BX",
                    "CX",
                    "DX",
                ],
                "GPR_INDEX": ["RSI", "RDI", "ESI", "EDI", "SI", "DI"],
                "GPR_STACK": ["RSP", "RBP", "ESP", "EBP", "SP", "BP"],
                "SIMD": ["XMM", "YMM", "ZMM"],
                "FPU": ["ST", "ST0", "ST1", "ST2", "ST3", "ST4", "ST5", "ST6", "ST7"],
                "FLAGS": ["FLAGS", "EFLAGS", "RFLAGS"],
                "CONTROL_REGISTER": ["CR0", "CR2", "CR3", "CR4", "CR8"],
                "DEBUG_REGISTER": ["DR0", "DR1", "DR2", "DR3", "DR6", "DR7"],
            }

            # Extract registers from instructions
            for instruction in self.instructions:
                instr_tokens = instruction[0]
                for token in instr_tokens:
                    if token.type == InstructionTextTokenType.RegisterToken:
                        reg = token.text.upper()
                        # Check which group this register belongs to
                        for group, regs in register_groups.items():
                            # if any(r in reg for r in regs) or any(reg.startswith(r) for r in regs):
                            if any(reg == r or reg.startswith(r) for r in regs):
                                if group not in registers:
                                    registers.append(group)
                                break
        except Exception as e:
            self.log_error(
                "Failed to collect register usage",
                self.function.name,
                self.function.start,
                e,
                "collect_register_usage",
            )
        return registers

    def count_data_references(self):
        """Count the number of data references in a function."""
        count = 0
        try:

            if self.function.mlil is None:
                return 0

            for block in self.function.mlil:
                for instr in block:
                    instr_str = str(instr)
                    logged = False
                    src = None

                    # Check for constant dereferencing or symbolic refs
                    if hasattr(instr, "src"):
                        src = instr.src
                        if isinstance(
                            src,
                            (
                                binaryninja.mediumlevelil.MediumLevelILConstPtr,
                                binaryninja.mediumlevelil.MediumLevelILConst,
                            ),
                        ):
                            count += 1
                            logged = True

                    # Check full string for hardcoded addresses or symbol-like tokens
                    if re.search(r"\b0x[0-9A-Fa-f]{3,}\b", instr_str) and not logged:
                        count += 1
                        logged = True

                    if "_" in instr_str and not logged:
                        count += 1
                        logged = True

                    # Only check for MediumLevelILConstPtr if src exists
                    if src is not None and isinstance(
                        src, binaryninja.mediumlevelil.MediumLevelILConstPtr
                    ):
                        addr = src.constant
                        # Check if address is in data sections
                        segment = self.bv.get_segment_at(addr)
                        if segment and segment.writable:
                            # print(f"[{function.name}] Matched data section reference in: {instr_str}")
                            count += 1
                            logged = True
        except Exception as e:
            self.logger.warning(
                f"Failed to use MLIL for counting data references in {self.function.name} at {self.function.start}: {e}"
            )
        return count

    def compute_max_block_size(self):
        """Compute the maximum basic block size in a function."""
        max_size = 0
        if self.function is None:
            return 0

        for block in self.function.basic_blocks:
            try:
                # Count instructions in this block using the direct length approach
                # This avoids UTF-8 decoding issues entirely
                block_size = block.instruction_count
                max_size = max(max_size, block_size)
            except Exception as e:
                self.log_error(
                    f"[HandledError] computing max block size: {e}",
                    self.function.name,
                    self.function.start,
                    e,
                    "compute_max_block_size",
                )
        return max_size

    def count_control_flow_instructions(self):
        """Count the number of control flow instructions in a function."""
        count = 0
        try:
            for instruction in self.instructions:
                instr_tokens = instruction[0]
                if self.arch.is_control_flow_instruction(instr_tokens):
                    count += 1
        except Exception as e:
            self.log_error(
                "Failed to count control flow instructions",
                self.function.name,
                self.function.start,
                e,
                "count_control_flow_instructions",
            )
        return count

    def compute_num_calls(self) -> int:
        """Compute the number of call instructions in a function."""
        num_calls = 0
        try:
            for instruction in self.instructions:
                instr_tokens = instruction[0]
                # Extract the mnemonic
                for token in instr_tokens:
                    if token.type == InstructionTextTokenType.InstructionToken:
                        if token.text.upper() == "CALL":
                            num_calls += 1
                        break
        except Exception as e:
            self.log_error(
                "Failed to compute number of calls",
                self.function.name,
                self.function.start,
                e,
                "compute_num_calls",
            )
        return num_calls

    def _increment_frequency(self, frequencies, type_name):
        """Increment the frequency count for an instruction type."""
        if type_name in frequencies:
            frequencies[type_name] += 1
        else:
            frequencies[type_name] = 1

    def estimate_stack_size(self):
        """Estimate the stack size used by a function."""
        try:
            # Binary Ninja provides a stack adjustment value for functions
            # Need to convert OffsetWithConfidence to a plain integer
            stack_adjust = self.function.stack_adjustment
            if hasattr(stack_adjust, "value"):  # Handle OffsetWithConfidence objects
                return stack_adjust.value
            return stack_adjust
        except Exception as e:
            self.log_error(
                "Failed to estimate stack size",
                self.function.name,
                self.function.start,
                e,
                "estimate_stack_size",
            )
            return self.INVALID_STACK_SIZE