diff --git a/debuggers/gdb/gdb_mi_driver.py b/debuggers/gdb/gdb_mi_driver.py index b476ce3..2260a24 100755 --- a/debuggers/gdb/gdb_mi_driver.py +++ b/debuggers/gdb/gdb_mi_driver.py @@ -1,7 +1,7 @@ import os import json -from debuggers.gdb.idd_gdb_controller import create_IDDGdbController, terminate_all_IDDGdbController +from debuggers.gdb.idd_gdb_controller import create_IDDGdbController, terminate_all_IDDGdbController, IDDGdbController from driver import Driver from debuggers.gdb.utils import parse_gdb_line @@ -18,24 +18,11 @@ class GDBMiDebugger(Driver): def __init__(self, base_args, base_script_file_path, regression_args, regression_script_file_path, base_pid=None, regression_pid=None): - self.base_gdb_instance = create_IDDGdbController(base_script_file_path) - self.regressed_gdb_instance = create_IDDGdbController(regression_script_file_path) + self.base_gdb_instance = create_IDDGdbController(base_args, base_pid, base_script_file_path) + self.regressed_gdb_instance = create_IDDGdbController(regression_args, regression_pid, regression_script_file_path) self.gdb_instances = { 'base': self.base_gdb_instance, 'regressed': self.regressed_gdb_instance } - if base_pid is None: - self.run_single_raw_command('file ' + base_args, 'base') - else: - self.run_single_raw_command('attach ' + base_pid, 'base') - - if regression_pid is None: - self.run_single_raw_command('file ' + regression_args, 'regressed') - else: - self.run_single_raw_command('attach ' + regression_pid, 'regressed') - - dirname = os.path.dirname(__file__) - self.run_parallel_raw_command("source " + os.path.join(dirname, "gdb_commands.py")) - def run_parallel_command(self, command): # start both execution in parallel self.base_gdb_instance.send(((" {command}\n".format(command = command),), {"timeout_sec": 60})) diff --git a/debuggers/gdb/idd_gdb_controller.py b/debuggers/gdb/idd_gdb_controller.py index 94f65a1..8fda0ca 100644 --- a/debuggers/gdb/idd_gdb_controller.py +++ b/debuggers/gdb/idd_gdb_controller.py @@ -1,7 +1,11 @@ +import os import logging import subprocess +import json from driver import IDDParallelTerminate +from debuggers.gdb.utils import parse_gdb_line + from pygdbmi.gdbcontroller import GdbController from pygdbmi.IoManager import IoManager from pygdbmi.constants import ( @@ -19,9 +23,17 @@ class IDDGdbController(GdbController): script_file_path = None - def __init__(self, script_file_path = None): + def __init__(self, base_args="", base_pid=None, script_file_path = None): self.script_file_path = script_file_path super().__init__( None, DEFAULT_TIME_TO_CHECK_FOR_ADDITIONAL_OUTPUT_SEC) + + if base_args != "": + self.run_single_command('file ' + base_args, 'base') + elif base_pid != None: + self.run_single_command('attach ' + base_pid, 'base') + + dirname = os.path.dirname(__file__) + self.run_single_command("source " + os.path.join(dirname, "gdb_commands.py")) def spawn_new_gdb_subprocess(self) -> int: if self.gdb_process: @@ -54,6 +66,38 @@ def spawn_new_gdb_subprocess(self) -> int: self.time_to_check_for_additional_output_sec, ) return self.gdb_process.pid + + def parse_command_output(self, raw_result): + response = [] + for item in raw_result: + if item['type'] == 'console': + input_string = str(item['payload']) + processed_output = parse_gdb_line(input_string) + response.append(processed_output) + return response + + def parse_special_command_output(self, raw_result): + for item in raw_result: + if item['type'] == 'console': + input_string = str(item['payload']) + processed_output = parse_gdb_line(input_string) + try: + parsed_dict = json.loads(processed_output) + except json.JSONDecodeError: + parsed_dict = processed_output + + if parsed_dict: + return parsed_dict + + def get_state(self, *_): + return self.parse_special_command_output(self.write("pstate")) + + + def run_single_command(self, command, *_): + return self.parse_command_output(self.write(command)) + + def terminate(self): + return diff --git a/debuggers/lldb/lldb_driver.py b/debuggers/lldb/lldb_driver.py index 3d1d543..8a8d2be 100755 --- a/debuggers/lldb/lldb_driver.py +++ b/debuggers/lldb/lldb_driver.py @@ -21,36 +21,39 @@ class LLDBDebugger: lldb_instances = None - def __init__(self, args): + def __init__(self, exe="", pid=None): self.lldb_instance = lldb.SBDebugger.Create() self.lldb_instance.SetAsync(False) self.lldb_instance.SetUseColor(False) - - error = lldb.SBError() - target = self.lldb_instance.CreateTarget(args, "x86_64", "host", True, error) - if not error.Success(): - raise Exception(error.GetCString()) self.command_interpreter = self.lldb_instance.GetCommandInterpreter() - launch_info = lldb.SBLaunchInfo(None) - launch_info.SetExecutableFile (target.GetExecutable(), True) + if exe != "": + error = lldb.SBError() + target = self.lldb_instance.CreateTarget(exe, "x86_64", "host", True, error) + if not error.Success(): + raise Exception(error.GetCString()) + + launch_info = lldb.SBLaunchInfo(None) + launch_info.SetExecutableFile (target.GetExecutable(), True) + elif pid is not None: + self.run_single_command("attach -p " + str(pid)) dirname = os.path.dirname(__file__) self.run_single_command("command script import " + os.path.join(dirname, "lldb_commands.py")) self.is_initted = True - def run_single_command(self, command): + def run_single_command(self, command, *_): command_result = lldb.SBCommandReturnObject() self.command_interpreter.HandleCommand(command, command_result) - + if command_result.Succeeded(): - return command_result.GetOutput() + return command_result.GetOutput().split("\n") else: - return command_result.GetError() + return command_result.GetError().split("\n") - def get_state(self): + def get_state(self, *_): return { 'stack_frames': self.get_current_stack_frames(), 'locals': self.get_current_local_vars(None), @@ -68,7 +71,7 @@ def get_current_args(self): target = self.lldb_instance.GetTargetAtIndex(0) args = get_args_as_list(target) return args - + def get_current_local_vars(self, filters): target = self.lldb_instance.GetTargetAtIndex(0) target_locals = get_local_vars_as_list(target) @@ -90,13 +93,13 @@ def get_current_calls(self): target = self.lldb_instance.GetTargetAtIndex(0) calls = get_call_instructions(target) return calls - + def terminate(self): - pass + return @staticmethod def run(lldb_args, pipe): - lldb = LLDBDebugger(lldb_args) + lldb = LLDBDebugger(*lldb_args) while True: args, kwargs = pipe.recv() if isinstance(args, IDDParallelTerminate) or isinstance(kwargs, IDDParallelTerminate): @@ -111,9 +114,9 @@ def run(lldb_args, pipe): class LLDBParallelDebugger(Driver): - def __init__(self, base_args, regression_args): - self.base_pipe = create_LLDBDebugger_for_parallel(base_args) - self.regressed_pipe = create_LLDBDebugger_for_parallel(regression_args) + def __init__(self, base_args="", base_pid=None, regression_args="", regression_pid=None): + self.base_pipe = create_LLDBDebugger_for_parallel(base_args, base_pid) + self.regressed_pipe = create_LLDBDebugger_for_parallel(regression_args, regression_pid) def get_state(self, target=None): if target == "base": @@ -134,18 +137,18 @@ def get_state(self, target=None): def run_single_command(self, command, target): if target == "base": self.base_pipe.send(((command,), {})) - return self.base_pipe.recv().split("\n") + return self.base_pipe.recv() if target == "regressed": self.regressed_pipe.send(((command,), {})) - return self.regressed_pipe.recv().split("\n") + return self.regressed_pipe.recv() def run_parallel_command(self, command): self.base_pipe.send(((command,), {})) self.regressed_pipe.send(((command,), {})) return { - "base": self.base_pipe.recv().split("\n"), - "regressed": self.regressed_pipe.recv().split("\n"), + "base": self.base_pipe.recv(), + "regressed": self.regressed_pipe.recv(), } def terminate(self): @@ -158,7 +161,7 @@ def terminate_all_IDDGdbController(): for process, _ in processes: process.join() -def create_LLDBDebugger_for_parallel(args): +def create_LLDBDebugger_for_parallel(*args): global processes parent_conn, child_conn = Pipe() diff --git a/idd.py b/idd.py index a8a14af..5d7d6f8 100755 --- a/idd.py +++ b/idd.py @@ -49,8 +49,9 @@ class DiffDebug(App): base_command_bar = Input(placeholder="Enter your base command here...", name="base_command_bar", id="base-command-bar") regressed_command_bar = Input(placeholder="Enter your regression command here...", name="regressed_command_bar", id="regressed-command-bar") - def __init__(self, disable_asm=False, disable_registers=False): + def __init__(self, disable_asm=False, disable_registers=False, only_base=False): super().__init__() + self.only_base = only_base self.disable_asm = disable_asm self.disable_registers = disable_registers self.common_history = [""] @@ -216,6 +217,33 @@ async def set_pregisters_command_result(self, state) -> None: def compose(self) -> ComposeResult: """Compose the layout of the application.""" + if self.only_base: + with Vertical(): + yield Header() + with Horizontal(classes="base_only_row1"): + yield self.diff_frames1 + with Horizontal(classes="base_only_row2"): + with Horizontal(): + yield self.diff_locals1 + yield self.diff_args1 + if not self.disable_registers and not self.disable_asm: + with Vertical(): + with Horizontal(): + yield self.diff_reg1 + with Horizontal(): + yield self.diff_asm1 + elif not self.disable_asm: + with Vertical(): + yield self.diff_asm1 + elif not self.disable_registers: + with Vertical(): + yield self.diff_reg1 + with Horizontal(classes="base_only_row3"): + yield self.diff_area1 + with Horizontal(classes="base_only_row4"): + yield self.base_command_bar + return + with Vertical(): yield Header() @@ -307,6 +335,9 @@ async def execute_debugger_command(self, event: Input.Changed) -> None: self.parallel_command_bar.value = "" elif event.control.id == 'base-command-bar': + if self.only_base and (self.base_command_bar.value == "exit" or self.base_command_bar.value == "quit"): + Debugger.terminate() + exit(0) if self.base_command_bar.value != "": result = Debugger.run_single_command(self.base_command_bar.value, "base") self.diff_area1.append([self.base_command_bar.value]) @@ -348,6 +379,9 @@ async def execute_debugger_command(self, event: Input.Changed) -> None: self.regressed_command_bar.value = "" async def on_key(self, event: events.Key) -> None: + if self.focused is None: + return + if self.focused.id == "parallel-command-bar": if event.key == "up": self.common_history_index = (self.common_history_index - 1) % len(self.common_history) @@ -383,12 +417,16 @@ async def on_key(self, event: events.Key) -> None: Debugger = None parser = argparse.ArgumentParser(description='Diff Debug for simple debugging!') + + base_arg_group = parser.add_mutually_exclusive_group() + regressed_arg_group = parser.add_mutually_exclusive_group() + parser.add_argument('-c','--comparator', help='Choose a comparator', default='gdb') - parser.add_argument('-ba','--base-args', help='Base executable args', default="", nargs='+') - parser.add_argument('-bpid','--base-processid', help='Base process ID', default=None) + base_arg_group.add_argument('-ba','--base-args', help='Base executable args', default="", nargs='+') + base_arg_group.add_argument('-bpid','--base-processid', help='Base process ID', default=None) parser.add_argument('-bs','--base-script-path', help='Base preliminary script file path', default=None, nargs='+') - parser.add_argument('-ra','--regression-args', help='Regression executable args', default="", nargs='+') - parser.add_argument('-rpid','--regression-processid', help='Regression process ID', default=None) + regressed_arg_group.add_argument('-ra','--regression-args', help='Regression executable args', default="", nargs='+') + regressed_arg_group.add_argument('-rpid','--regression-processid', help='Regression process ID', default=None) parser.add_argument('-rs','--regression-script-path', help='Regression preliminary script file path', default=None, nargs='+') parser.add_argument('-r','--remote_host', help='The host of the remote server', default='localhost') parser.add_argument('-p','--platform', help='The platform of the remote server: macosx, linux', default='linux') @@ -405,36 +443,29 @@ async def on_key(self, event: events.Key) -> None: ra = ' '.join(args['regression_args']) rpid = args['regression_processid'] rs = ' '.join(args['regression_script_path']) if args["regression_script_path"] is not None else None + base_only = False if comparator == 'gdb': - from debuggers.gdb.gdb_mi_driver import GDBMiDebugger + from debuggers.gdb.gdb_mi_driver import GDBMiDebugger, IDDGdbController - if ba != "" and bpid is not None: - raise Exception("Both executable and process ID given for base. This is not possible") - if ra != "" and rpid is not None: - raise Exception("Both executable and process ID given for regression. This is not possible") - - if ba == "": - if ra == "": - Debugger = GDBMiDebugger(ba, bs, ra, rs, base_pid=bpid, regression_pid=rpid) - else: - Debugger = GDBMiDebugger(ba, bs, ra, rs, base_pid=bpid) + if ra == "" and rpid is None: + Debugger = IDDGdbController(ba, bpid, bs) + base_only = True else: - if ra == "": - Debugger = GDBMiDebugger(ba, bs, ra, rs, regression_pid=rpid) - else: - Debugger = GDBMiDebugger(ba, bs, ra, rs) + Debugger = GDBMiDebugger(ba, bs, ra, rs, base_pid=bpid, regression_pid=rpid) elif comparator == 'lldb': - from debuggers.lldb.lldb_driver import LLDBParallelDebugger + from debuggers.lldb.lldb_driver import LLDBParallelDebugger, LLDBDebugger - if ba == "" or ra == "": - raise Exception("LLDB can only be used by launching executable and executable is not provided") - Debugger = LLDBParallelDebugger(ba, ra) + if ra == "" and rpid is None: + Debugger = LLDBDebugger(ba, bpid) + base_only = True + else: + Debugger = LLDBParallelDebugger(ba, bpid, ra, rpid) else: sys.exit("Invalid comparator set") disable_registers = args["disable_registers"] disable_assembly = args["disable_assembly"] - dd = DiffDebug(disable_assembly, disable_registers) + dd = DiffDebug(disable_assembly, disable_registers, base_only) dd.run() diff --git a/layout.tcss b/layout.tcss index 0ebb173..a1ca1dc 100644 --- a/layout.tcss +++ b/layout.tcss @@ -20,9 +20,25 @@ TextScrollView { } .row3, .row5 { - height: 6%; + height: 5%; } .row4 { height: 40%; } + +.base_only_row1 { + height: 20% +} + +.base_only_row2 { + height: 30% +} + +.base_only_row3 { + height: 44% +} + +.base_only_row4 { + height: 6% +}