Skip to content

Commit 19fbc30

Browse files
usage without regressed program & [lldb] attach using pid (#42)
1 parent 9dc88b5 commit 19fbc30

File tree

5 files changed

+149
-68
lines changed

5 files changed

+149
-68
lines changed

debuggers/gdb/gdb_mi_driver.py

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import os
22
import json
33

4-
from debuggers.gdb.idd_gdb_controller import create_IDDGdbController, terminate_all_IDDGdbController
4+
from debuggers.gdb.idd_gdb_controller import create_IDDGdbController, terminate_all_IDDGdbController, IDDGdbController
55
from driver import Driver
66

77
from debuggers.gdb.utils import parse_gdb_line
@@ -18,24 +18,11 @@ class GDBMiDebugger(Driver):
1818

1919
def __init__(self, base_args, base_script_file_path, regression_args, regression_script_file_path,
2020
base_pid=None, regression_pid=None):
21-
self.base_gdb_instance = create_IDDGdbController(base_script_file_path)
22-
self.regressed_gdb_instance = create_IDDGdbController(regression_script_file_path)
21+
self.base_gdb_instance = create_IDDGdbController(base_args, base_pid, base_script_file_path)
22+
self.regressed_gdb_instance = create_IDDGdbController(regression_args, regression_pid, regression_script_file_path)
2323

2424
self.gdb_instances = { 'base': self.base_gdb_instance, 'regressed': self.regressed_gdb_instance }
2525

26-
if base_pid is None:
27-
self.run_single_raw_command('file ' + base_args, 'base')
28-
else:
29-
self.run_single_raw_command('attach ' + base_pid, 'base')
30-
31-
if regression_pid is None:
32-
self.run_single_raw_command('file ' + regression_args, 'regressed')
33-
else:
34-
self.run_single_raw_command('attach ' + regression_pid, 'regressed')
35-
36-
dirname = os.path.dirname(__file__)
37-
self.run_parallel_raw_command("source " + os.path.join(dirname, "gdb_commands.py"))
38-
3926
def run_parallel_command(self, command):
4027
# start both execution in parallel
4128
self.base_gdb_instance.send(((" {command}\n".format(command = command),), {"timeout_sec": 60}))

debuggers/gdb/idd_gdb_controller.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1+
import os
12
import logging
23
import subprocess
4+
import json
35

46
from driver import IDDParallelTerminate
7+
from debuggers.gdb.utils import parse_gdb_line
8+
59
from pygdbmi.gdbcontroller import GdbController
610
from pygdbmi.IoManager import IoManager
711
from pygdbmi.constants import (
@@ -19,9 +23,17 @@
1923
class IDDGdbController(GdbController):
2024
script_file_path = None
2125

22-
def __init__(self, script_file_path = None):
26+
def __init__(self, base_args="", base_pid=None, script_file_path = None):
2327
self.script_file_path = script_file_path
2428
super().__init__( None, DEFAULT_TIME_TO_CHECK_FOR_ADDITIONAL_OUTPUT_SEC)
29+
30+
if base_args != "":
31+
self.run_single_command('file ' + base_args, 'base')
32+
elif base_pid != None:
33+
self.run_single_command('attach ' + base_pid, 'base')
34+
35+
dirname = os.path.dirname(__file__)
36+
self.run_single_command("source " + os.path.join(dirname, "gdb_commands.py"))
2537

2638
def spawn_new_gdb_subprocess(self) -> int:
2739
if self.gdb_process:
@@ -54,6 +66,38 @@ def spawn_new_gdb_subprocess(self) -> int:
5466
self.time_to_check_for_additional_output_sec,
5567
)
5668
return self.gdb_process.pid
69+
70+
def parse_command_output(self, raw_result):
71+
response = []
72+
for item in raw_result:
73+
if item['type'] == 'console':
74+
input_string = str(item['payload'])
75+
processed_output = parse_gdb_line(input_string)
76+
response.append(processed_output)
77+
return response
78+
79+
def parse_special_command_output(self, raw_result):
80+
for item in raw_result:
81+
if item['type'] == 'console':
82+
input_string = str(item['payload'])
83+
processed_output = parse_gdb_line(input_string)
84+
try:
85+
parsed_dict = json.loads(processed_output)
86+
except json.JSONDecodeError:
87+
parsed_dict = processed_output
88+
89+
if parsed_dict:
90+
return parsed_dict
91+
92+
def get_state(self, *_):
93+
return self.parse_special_command_output(self.write("pstate"))
94+
95+
96+
def run_single_command(self, command, *_):
97+
return self.parse_command_output(self.write(command))
98+
99+
def terminate(self):
100+
return
57101

58102

59103

debuggers/lldb/lldb_driver.py

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,36 +21,39 @@ class LLDBDebugger:
2121

2222
lldb_instances = None
2323

24-
def __init__(self, args):
24+
def __init__(self, exe="", pid=None):
2525
self.lldb_instance = lldb.SBDebugger.Create()
2626
self.lldb_instance.SetAsync(False)
2727
self.lldb_instance.SetUseColor(False)
28-
29-
error = lldb.SBError()
30-
target = self.lldb_instance.CreateTarget(args, "x86_64", "host", True, error)
31-
if not error.Success():
32-
raise Exception(error.GetCString())
3328

3429
self.command_interpreter = self.lldb_instance.GetCommandInterpreter()
3530

36-
launch_info = lldb.SBLaunchInfo(None)
37-
launch_info.SetExecutableFile (target.GetExecutable(), True)
31+
if exe != "":
32+
error = lldb.SBError()
33+
target = self.lldb_instance.CreateTarget(exe, "x86_64", "host", True, error)
34+
if not error.Success():
35+
raise Exception(error.GetCString())
36+
37+
launch_info = lldb.SBLaunchInfo(None)
38+
launch_info.SetExecutableFile (target.GetExecutable(), True)
39+
elif pid is not None:
40+
self.run_single_command("attach -p " + str(pid))
3841

3942
dirname = os.path.dirname(__file__)
4043
self.run_single_command("command script import " + os.path.join(dirname, "lldb_commands.py"))
4144

4245
self.is_initted = True
4346

44-
def run_single_command(self, command):
47+
def run_single_command(self, command, *_):
4548
command_result = lldb.SBCommandReturnObject()
4649
self.command_interpreter.HandleCommand(command, command_result)
47-
50+
4851
if command_result.Succeeded():
49-
return command_result.GetOutput()
52+
return command_result.GetOutput().split("\n")
5053
else:
51-
return command_result.GetError()
54+
return command_result.GetError().split("\n")
5255

53-
def get_state(self):
56+
def get_state(self, *_):
5457
return {
5558
'stack_frames': self.get_current_stack_frames(),
5659
'locals': self.get_current_local_vars(None),
@@ -68,7 +71,7 @@ def get_current_args(self):
6871
target = self.lldb_instance.GetTargetAtIndex(0)
6972
args = get_args_as_list(target)
7073
return args
71-
74+
7275
def get_current_local_vars(self, filters):
7376
target = self.lldb_instance.GetTargetAtIndex(0)
7477
target_locals = get_local_vars_as_list(target)
@@ -90,13 +93,13 @@ def get_current_calls(self):
9093
target = self.lldb_instance.GetTargetAtIndex(0)
9194
calls = get_call_instructions(target)
9295
return calls
93-
96+
9497
def terminate(self):
95-
pass
98+
return
9699

97100
@staticmethod
98101
def run(lldb_args, pipe):
99-
lldb = LLDBDebugger(lldb_args)
102+
lldb = LLDBDebugger(*lldb_args)
100103
while True:
101104
args, kwargs = pipe.recv()
102105
if isinstance(args, IDDParallelTerminate) or isinstance(kwargs, IDDParallelTerminate):
@@ -111,9 +114,9 @@ def run(lldb_args, pipe):
111114

112115

113116
class LLDBParallelDebugger(Driver):
114-
def __init__(self, base_args, regression_args):
115-
self.base_pipe = create_LLDBDebugger_for_parallel(base_args)
116-
self.regressed_pipe = create_LLDBDebugger_for_parallel(regression_args)
117+
def __init__(self, base_args="", base_pid=None, regression_args="", regression_pid=None):
118+
self.base_pipe = create_LLDBDebugger_for_parallel(base_args, base_pid)
119+
self.regressed_pipe = create_LLDBDebugger_for_parallel(regression_args, regression_pid)
117120

118121
def get_state(self, target=None):
119122
if target == "base":
@@ -134,18 +137,18 @@ def get_state(self, target=None):
134137
def run_single_command(self, command, target):
135138
if target == "base":
136139
self.base_pipe.send(((command,), {}))
137-
return self.base_pipe.recv().split("\n")
140+
return self.base_pipe.recv()
138141
if target == "regressed":
139142
self.regressed_pipe.send(((command,), {}))
140-
return self.regressed_pipe.recv().split("\n")
143+
return self.regressed_pipe.recv()
141144

142145
def run_parallel_command(self, command):
143146
self.base_pipe.send(((command,), {}))
144147
self.regressed_pipe.send(((command,), {}))
145148

146149
return {
147-
"base": self.base_pipe.recv().split("\n"),
148-
"regressed": self.regressed_pipe.recv().split("\n"),
150+
"base": self.base_pipe.recv(),
151+
"regressed": self.regressed_pipe.recv(),
149152
}
150153

151154
def terminate(self):
@@ -158,7 +161,7 @@ def terminate_all_IDDGdbController():
158161
for process, _ in processes:
159162
process.join()
160163

161-
def create_LLDBDebugger_for_parallel(args):
164+
def create_LLDBDebugger_for_parallel(*args):
162165
global processes
163166

164167
parent_conn, child_conn = Pipe()

idd.py

Lines changed: 56 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,9 @@ class DiffDebug(App):
4949
base_command_bar = Input(placeholder="Enter your base command here...", name="base_command_bar", id="base-command-bar")
5050
regressed_command_bar = Input(placeholder="Enter your regression command here...", name="regressed_command_bar", id="regressed-command-bar")
5151

52-
def __init__(self, disable_asm=False, disable_registers=False):
52+
def __init__(self, disable_asm=False, disable_registers=False, only_base=False):
5353
super().__init__()
54+
self.only_base = only_base
5455
self.disable_asm = disable_asm
5556
self.disable_registers = disable_registers
5657
self.common_history = [""]
@@ -216,6 +217,33 @@ async def set_pregisters_command_result(self, state) -> None:
216217

217218
def compose(self) -> ComposeResult:
218219
"""Compose the layout of the application."""
220+
if self.only_base:
221+
with Vertical():
222+
yield Header()
223+
with Horizontal(classes="base_only_row1"):
224+
yield self.diff_frames1
225+
with Horizontal(classes="base_only_row2"):
226+
with Horizontal():
227+
yield self.diff_locals1
228+
yield self.diff_args1
229+
if not self.disable_registers and not self.disable_asm:
230+
with Vertical():
231+
with Horizontal():
232+
yield self.diff_reg1
233+
with Horizontal():
234+
yield self.diff_asm1
235+
elif not self.disable_asm:
236+
with Vertical():
237+
yield self.diff_asm1
238+
elif not self.disable_registers:
239+
with Vertical():
240+
yield self.diff_reg1
241+
with Horizontal(classes="base_only_row3"):
242+
yield self.diff_area1
243+
with Horizontal(classes="base_only_row4"):
244+
yield self.base_command_bar
245+
return
246+
219247
with Vertical():
220248
yield Header()
221249

@@ -307,6 +335,9 @@ async def execute_debugger_command(self, event: Input.Changed) -> None:
307335
self.parallel_command_bar.value = ""
308336

309337
elif event.control.id == 'base-command-bar':
338+
if self.only_base and (self.base_command_bar.value == "exit" or self.base_command_bar.value == "quit"):
339+
Debugger.terminate()
340+
exit(0)
310341
if self.base_command_bar.value != "":
311342
result = Debugger.run_single_command(self.base_command_bar.value, "base")
312343
self.diff_area1.append([self.base_command_bar.value])
@@ -348,6 +379,9 @@ async def execute_debugger_command(self, event: Input.Changed) -> None:
348379
self.regressed_command_bar.value = ""
349380

350381
async def on_key(self, event: events.Key) -> None:
382+
if self.focused is None:
383+
return
384+
351385
if self.focused.id == "parallel-command-bar":
352386
if event.key == "up":
353387
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:
383417
Debugger = None
384418

385419
parser = argparse.ArgumentParser(description='Diff Debug for simple debugging!')
420+
421+
base_arg_group = parser.add_mutually_exclusive_group()
422+
regressed_arg_group = parser.add_mutually_exclusive_group()
423+
386424
parser.add_argument('-c','--comparator', help='Choose a comparator', default='gdb')
387-
parser.add_argument('-ba','--base-args', help='Base executable args', default="", nargs='+')
388-
parser.add_argument('-bpid','--base-processid', help='Base process ID', default=None)
425+
base_arg_group.add_argument('-ba','--base-args', help='Base executable args', default="", nargs='+')
426+
base_arg_group.add_argument('-bpid','--base-processid', help='Base process ID', default=None)
389427
parser.add_argument('-bs','--base-script-path', help='Base preliminary script file path', default=None, nargs='+')
390-
parser.add_argument('-ra','--regression-args', help='Regression executable args', default="", nargs='+')
391-
parser.add_argument('-rpid','--regression-processid', help='Regression process ID', default=None)
428+
regressed_arg_group.add_argument('-ra','--regression-args', help='Regression executable args', default="", nargs='+')
429+
regressed_arg_group.add_argument('-rpid','--regression-processid', help='Regression process ID', default=None)
392430
parser.add_argument('-rs','--regression-script-path', help='Regression preliminary script file path', default=None, nargs='+')
393431
parser.add_argument('-r','--remote_host', help='The host of the remote server', default='localhost')
394432
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:
405443
ra = ' '.join(args['regression_args'])
406444
rpid = args['regression_processid']
407445
rs = ' '.join(args['regression_script_path']) if args["regression_script_path"] is not None else None
446+
base_only = False
408447

409448
if comparator == 'gdb':
410-
from debuggers.gdb.gdb_mi_driver import GDBMiDebugger
449+
from debuggers.gdb.gdb_mi_driver import GDBMiDebugger, IDDGdbController
411450

412-
if ba != "" and bpid is not None:
413-
raise Exception("Both executable and process ID given for base. This is not possible")
414-
if ra != "" and rpid is not None:
415-
raise Exception("Both executable and process ID given for regression. This is not possible")
416-
417-
if ba == "":
418-
if ra == "":
419-
Debugger = GDBMiDebugger(ba, bs, ra, rs, base_pid=bpid, regression_pid=rpid)
420-
else:
421-
Debugger = GDBMiDebugger(ba, bs, ra, rs, base_pid=bpid)
451+
if ra == "" and rpid is None:
452+
Debugger = IDDGdbController(ba, bpid, bs)
453+
base_only = True
422454
else:
423-
if ra == "":
424-
Debugger = GDBMiDebugger(ba, bs, ra, rs, regression_pid=rpid)
425-
else:
426-
Debugger = GDBMiDebugger(ba, bs, ra, rs)
455+
Debugger = GDBMiDebugger(ba, bs, ra, rs, base_pid=bpid, regression_pid=rpid)
427456

428457
elif comparator == 'lldb':
429-
from debuggers.lldb.lldb_driver import LLDBParallelDebugger
458+
from debuggers.lldb.lldb_driver import LLDBParallelDebugger, LLDBDebugger
430459

431-
if ba == "" or ra == "":
432-
raise Exception("LLDB can only be used by launching executable and executable is not provided")
433-
Debugger = LLDBParallelDebugger(ba, ra)
460+
if ra == "" and rpid is None:
461+
Debugger = LLDBDebugger(ba, bpid)
462+
base_only = True
463+
else:
464+
Debugger = LLDBParallelDebugger(ba, bpid, ra, rpid)
434465
else:
435466
sys.exit("Invalid comparator set")
436467

437468
disable_registers = args["disable_registers"]
438469
disable_assembly = args["disable_assembly"]
439-
dd = DiffDebug(disable_assembly, disable_registers)
470+
dd = DiffDebug(disable_assembly, disable_registers, base_only)
440471
dd.run()

layout.tcss

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,25 @@ TextScrollView {
2020
}
2121

2222
.row3, .row5 {
23-
height: 6%;
23+
height: 5%;
2424
}
2525

2626
.row4 {
2727
height: 40%;
2828
}
29+
30+
.base_only_row1 {
31+
height: 20%
32+
}
33+
34+
.base_only_row2 {
35+
height: 30%
36+
}
37+
38+
.base_only_row3 {
39+
height: 44%
40+
}
41+
42+
.base_only_row4 {
43+
height: 6%
44+
}

0 commit comments

Comments
 (0)