Skip to content

Commit

Permalink
usage without regressed program & [lldb] attach using pid (#42)
Browse files Browse the repository at this point in the history
  • Loading branch information
Vipul-Cariappa authored Nov 27, 2024
1 parent 9dc88b5 commit 19fbc30
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 68 deletions.
19 changes: 3 additions & 16 deletions debuggers/gdb/gdb_mi_driver.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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}))
Expand Down
46 changes: 45 additions & 1 deletion debuggers/gdb/idd_gdb_controller.py
Original file line number Diff line number Diff line change
@@ -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 (
Expand All @@ -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:
Expand Down Expand Up @@ -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



Expand Down
53 changes: 28 additions & 25 deletions debuggers/lldb/lldb_driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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)
Expand All @@ -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):
Expand All @@ -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":
Expand All @@ -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):
Expand All @@ -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()
Expand Down
81 changes: 56 additions & 25 deletions idd.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [""]
Expand Down Expand Up @@ -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()

Expand Down Expand Up @@ -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])
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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')
Expand All @@ -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()
18 changes: 17 additions & 1 deletion layout.tcss
Original file line number Diff line number Diff line change
Expand Up @@ -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%
}

0 comments on commit 19fbc30

Please sign in to comment.