diff --git a/chb/app/CHVersion.py b/chb/app/CHVersion.py index a8ccbabf..b6c76b23 100644 --- a/chb/app/CHVersion.py +++ b/chb/app/CHVersion.py @@ -1 +1 @@ -chbversion: str = "0.3.0-20250811" +chbversion: str = "0.3.0-20250812" diff --git a/chb/cmdline/AnalysisManager.py b/chb/cmdline/AnalysisManager.py index 1aa1d8ee..8a2fd65b 100644 --- a/chb/cmdline/AnalysisManager.py +++ b/chb/cmdline/AnalysisManager.py @@ -289,6 +289,7 @@ def analyze( extract: bool = False, verbose: bool = False, collectdiagnostics: bool = False, + failonfunctionfailure: bool = False, ignore_stable: bool = False, save_asm: bool = False, construct_all_functions: bool = False, @@ -307,6 +308,7 @@ def analyze( verbose=verbose, construct_all_functions=construct_all_functions, collectdiagnostics=collectdiagnostics, + failonfunctionfailure=failonfunctionfailure, preamble_cutoff=preamble_cutoff) return result @@ -446,6 +448,7 @@ def _analyze_until_stable( verbose: bool = False, construct_all_functions: bool = False, collectdiagnostics: bool = False, + failonfunctionfailure: bool = False, preamble_cutoff: int = 12) -> int: cwd = os.getcwd() os.chdir(self.path) # temporary change in directory @@ -500,6 +503,8 @@ def _analyze_until_stable( cmd.extend(["-lineq_block_cutoff", str(self.lineq_block_cutoff)]) if self.include_arm_extension_registers: cmd.append("-arm_extension_registers") + if failonfunctionfailure: + cmd.append("-fail_on_function_failure") cmd.extend(["-analyze", self.filename]) jarcmd = ["jar", "cf", functionsjarfile, "-C", analysisdir, "functions"] diff --git a/chb/cmdline/chkx b/chb/cmdline/chkx index 0678108a..c17b7e29 100755 --- a/chb/cmdline/chkx +++ b/chb/cmdline/chkx @@ -390,6 +390,10 @@ def parse() -> argparse.Namespace: "--collect_diagnostics", action="store_true", help="save diagnostics in a separate diagnostics log file") + analyzecmd.add_argument( + "--fail_on_function_failure", + action="store_true", + help="fail immediately if the analysis of one function fails") analyzecmd.add_argument( "--thumb", nargs="*", @@ -1381,6 +1385,10 @@ def parse() -> argparse.Namespace: "--collect_diagnostics", action="store_true", help="save diagnostic information in diagnostics log") + relationalprepare.add_argument( + "--fail_on_function_failure", + action="store_true", + help="fail immediately if the analysis of one function fails") relationalprepare.add_argument( "--hints", nargs="*", diff --git a/chb/cmdline/commandutil.py b/chb/cmdline/commandutil.py index ce93ab68..cd1a10d9 100644 --- a/chb/cmdline/commandutil.py +++ b/chb/cmdline/commandutil.py @@ -396,6 +396,7 @@ def analyzecmd(args: argparse.Namespace) -> NoReturn: doextract: bool = args.extract verbose: bool = args.verbose collectdiagnostics: bool = args.collect_diagnostics + failonfunctionfailure: bool = args.fail_on_function_failure save_asm: bool = args.save_asm save_asm_cfg_info: bool = args.save_asm_cfg_info thumb: List[str] = args.thumb @@ -425,6 +426,8 @@ def analyzecmd(args: argparse.Namespace) -> NoReturn: logfilename: Optional[str] = args.logfilename logfilemode: str = args.logfilemode + failonfunctionfailure = failonfunctionfailure or len(fns_include) == 1 + if not os.path.isfile(Config().chx86_analyze): print_error( "CodeHawk analyzer executable not found.\n" @@ -586,6 +589,7 @@ def analyzecmd(args: argparse.Namespace) -> NoReturn: save_asm=save_asm, construct_all_functions=construct_all_functions, collectdiagnostics=collectdiagnostics, + failonfunctionfailure=failonfunctionfailure, preamble_cutoff=preamble_cutoff) except subprocess.CalledProcessError as e: print_error( diff --git a/chb/cmdline/relationalcmds.py b/chb/cmdline/relationalcmds.py index 5b262764..ad816a7a 100644 --- a/chb/cmdline/relationalcmds.py +++ b/chb/cmdline/relationalcmds.py @@ -154,6 +154,7 @@ def relational_prepare_command(args: argparse.Namespace) -> NoReturn: xprint: bool = not args.json xssa: bool = args.ssa collectdiagnostics = args.collect_diagnostics + failonfunctionfailure = args.fail_on_function_failure xconstruct_all_functions: bool = args.construct_all_functions loglevel: str = args.loglevel logfilename: Optional[str] = args.logfilename @@ -311,6 +312,7 @@ def relational_prepare_command(args: argparse.Namespace) -> NoReturn: iterations=10, save_asm=xsave_asm, collectdiagnostics=collectdiagnostics, + failonfunctionfailure=failonfunctionfailure, construct_all_functions=xconstruct_all_functions) except subprocess.CalledProcessError as e: print(e.output)