From 24fc03a146e8d454699269deb90e9c88ae2a6089 Mon Sep 17 00:00:00 2001 From: Netanel Cohen Date: Thu, 23 May 2024 14:02:51 +0300 Subject: [PATCH] hilda_client: seperate configurable values into their own namespace --- hilda/hilda_client.py | 68 ++++++++++++++++--------------------- hilda/objective_c_class.py | 2 ++ hilda/objective_c_symbol.py | 2 ++ hilda/symbols_jar.py | 9 +++-- 4 files changed, 41 insertions(+), 40 deletions(-) diff --git a/hilda/hilda_client.py b/hilda/hilda_client.py index 3246d1c..c0b39d2 100644 --- a/hilda/hilda_client.py +++ b/hilda/hilda_client.py @@ -91,6 +91,33 @@ def fbp(line, local_ns=None): SerializableSymbol = namedtuple('SerializableSymbol', 'address type_ filename') +@dataclass +class Configs: + """ Configuration settings for evaluation and monitoring. """ + evaluation_unwind_on_error: bool = field(default=False, + metadata={'doc': 'Whether to unwind on error during evaluation.'}) + evaluation_ignore_breakpoints: bool = field(default=False, + metadata={'doc': 'Whether to ignore breakpoints during evaluation.'}) + nsobject_exclusion: bool = field(default=False, metadata={ + 'doc': 'Whether to exclude NSObject during evaluation - reduce ipython autocomplete results.'}) + objc_verbose_monitor: bool = field(default=False, metadata={ + 'doc': 'When set to True, using monitor() will automatically print objc methods arguments.'}) + + def __repr__(self): + return self.__str__() + + def __str__(self): + config_str = 'Configuration settings:\n' + max_len = max(len(field_name) for field_name in self.__dataclass_fields__) + 2 + + for field_name, field_info in self.__dataclass_fields__.items(): + value = getattr(self, field_name) + doc = field_info.metadata.get('doc', 'No docstring available') + config_str += f'\t{field_name.ljust(max_len)}: {str(value).ljust(5)} | {doc}\n' + + return config_str + + class HildaClient: Breakpoint = namedtuple('Breakpoint', 'address options forced callback') @@ -108,13 +135,7 @@ def __init__(self, debugger: lldb.SBDebugger): self.registers = Registers(self) self.arch = self.target.GetTriple().split('-')[0] self.ui_manager = UiManager(self) - # should unwind the stack on errors. change this to False in order to debug self-made calls - # within hilda - self._evaluation_unwind_on_error = True - - # should ignore breakpoints while evaluation - self._evaluation_ignore_breakpoints = True - + self.configs = Configs() self._dynamic_env_loaded = False self._symbols_loaded = False @@ -821,9 +842,9 @@ def evaluate_expression(self, expression) -> Symbol: formatted_expression = str(expression) options = lldb.SBExpressionOptions() - options.SetIgnoreBreakpoints(self._evaluation_ignore_breakpoints) + options.SetIgnoreBreakpoints(self.configs.evaluation_ignore_breakpoints) options.SetTryAllThreads(True) - options.SetUnwindOnError(self._evaluation_unwind_on_error) + options.SetUnwindOnError(self.configs.evaluation_unwind_on_error) e = self.frame.EvaluateExpression(formatted_expression, options) @@ -847,35 +868,6 @@ def import_module(self, filename: str, name: Optional[str] = None) -> Any: spec.loader.exec_module(m) return m - def set_evaluation_unwind(self, value: bool): - """ - Set whether LLDB will attempt to unwind the stack whenever an expression evaluation error occurs. - - Use unwind() to restore when an error is raised in this case. - """ - self._evaluation_unwind_on_error = value - - def get_evaluation_unwind(self) -> bool: - """ - Get evaluation unwind state. - - When this value is True, LLDB will attempt unwinding the stack on evaluation errors. - Otherwise, the stack frame will remain the same on errors to help you investigate the error. - """ - return self._evaluation_unwind_on_error - - def set_evaluation_ignore_breakpoints(self, value: bool): - """ - Set whether to ignore breakpoints while evaluating expressions - """ - self._evaluation_ignore_breakpoints = value - - def get_evaluation_ignore_breakpoints(self) -> bool: - """ - Get evaluation "ignore-breakpoints" state. - """ - return self._evaluation_ignore_breakpoints - def unwind(self) -> bool: """ Unwind the stack (useful when get_evaluation_unwind() == False) """ return self.thread.UnwindInnermostExpression().Success() diff --git a/hilda/objective_c_class.py b/hilda/objective_c_class.py index 200a91c..b991f68 100644 --- a/hilda/objective_c_class.py +++ b/hilda/objective_c_class.py @@ -268,6 +268,8 @@ def __dir__(self): result.add(method.name.replace(':', '_')) for sup in self.iter_supers(): + if self._client.configs.nsobject_exclusion and sup.name == 'NSObject': + continue for method in sup.methods: if method.is_class: result.add(method.name.replace(':', '_')) diff --git a/hilda/objective_c_symbol.py b/hilda/objective_c_symbol.py index 481a131..73e4abd 100644 --- a/hilda/objective_c_symbol.py +++ b/hilda/objective_c_symbol.py @@ -175,6 +175,8 @@ def __dir__(self): result.add(method.name.replace(':', '_')) for sup in self.class_.iter_supers(): + if self._client.configs.nsobject_exclusion and sup.name == 'NSObject': + continue for method in sup.methods: result.add(method.name.replace(':', '_')) diff --git a/hilda/symbols_jar.py b/hilda/symbols_jar.py index eea23c1..32f2952 100644 --- a/hilda/symbols_jar.py +++ b/hilda/symbols_jar.py @@ -139,10 +139,15 @@ def monitor(self, **args): :param args: given arguments for monitor command """ for name, address in self.items(): + args_c = args.copy() if name == '_client': continue - name = args.get('name', name) - address.monitor(name=name, **args) + if self.__dict__['_client'].configs.objc_verbose_monitor: + arg_count = name.count(':') + if arg_count > 0: + args_c['regs'] = {f'x{i + 2}': 'po' for i in range(arg_count)} + name = args_c.get('name', name) + address.monitor(name=name, **args_c) def startswith(self, exp, case_sensitive=True): """