|
|
|
@ -6,7 +6,7 @@ python |
|
|
|
|
|
|
|
|
|
|
|
# License ---------------------------------------------------------------------- |
|
|
|
# License ---------------------------------------------------------------------- |
|
|
|
|
|
|
|
|
|
|
|
# Copyright (c) 2015-2021 Andrea Cardaci <cyrus.and@gmail.com> |
|
|
|
# Copyright (c) 2015-2025 Andrea Cardaci <cyrus.and@gmail.com> |
|
|
|
# |
|
|
|
# |
|
|
|
# Permission is hereby granted, free of charge, to any person obtaining a copy |
|
|
|
# Permission is hereby granted, free of charge, to any person obtaining a copy |
|
|
|
# of this software and associated documentation files (the "Software"), to deal |
|
|
|
# of this software and associated documentation files (the "Software"), to deal |
|
|
|
@ -103,13 +103,13 @@ prompt, see the command `python print(gdb.prompt.prompt_help())`''', |
|
|
|
|
|
|
|
|
|
|
|
See the `prompt` attribute. This value is used as a Python format string where |
|
|
|
See the `prompt` attribute. This value is used as a Python format string where |
|
|
|
`{pid}` is expanded with the process identifier of the target program.''', |
|
|
|
`{pid}` is expanded with the process identifier of the target program.''', |
|
|
|
'default': '\[\e[1;35m\]>>>\[\e[0m\]' |
|
|
|
'default': r'\[\e[1;35m\]>>>\[\e[0m\]' |
|
|
|
}, |
|
|
|
}, |
|
|
|
'prompt_not_running': { |
|
|
|
'prompt_not_running': { |
|
|
|
'doc': '''Define the value of `{status}` when the target program is running. |
|
|
|
'doc': '''Define the value of `{status}` when the target program is running. |
|
|
|
|
|
|
|
|
|
|
|
See the `prompt` attribute. This value is used as a Python format string.''', |
|
|
|
See the `prompt` attribute. This value is used as a Python format string.''', |
|
|
|
'default': '\[\e[90m\]>>>\[\e[0m\]' |
|
|
|
'default': r'\[\e[90m\]>>>\[\e[0m\]' |
|
|
|
}, |
|
|
|
}, |
|
|
|
# divider |
|
|
|
# divider |
|
|
|
'omit_divider': { |
|
|
|
'omit_divider': { |
|
|
|
@ -192,7 +192,7 @@ See the `prompt` attribute. This value is used as a Python format string.''', |
|
|
|
class Beautifier(): |
|
|
|
class Beautifier(): |
|
|
|
|
|
|
|
|
|
|
|
def __init__(self, hint, tab_size=4): |
|
|
|
def __init__(self, hint, tab_size=4): |
|
|
|
self.tab_spaces = ' ' * tab_size |
|
|
|
self.tab_spaces = ' ' * tab_size if tab_size else None |
|
|
|
self.active = False |
|
|
|
self.active = False |
|
|
|
if not R.ansi or not R.syntax_highlighting: |
|
|
|
if not R.ansi or not R.syntax_highlighting: |
|
|
|
return |
|
|
|
return |
|
|
|
@ -218,7 +218,8 @@ class Beautifier(): |
|
|
|
pass |
|
|
|
pass |
|
|
|
|
|
|
|
|
|
|
|
def process(self, source): |
|
|
|
def process(self, source): |
|
|
|
# convert tabs anyway |
|
|
|
# convert tabs if requested |
|
|
|
|
|
|
|
if self.tab_spaces: |
|
|
|
source = source.replace('\t', self.tab_spaces) |
|
|
|
source = source.replace('\t', self.tab_spaces) |
|
|
|
if self.active: |
|
|
|
if self.active: |
|
|
|
import pygments |
|
|
|
import pygments |
|
|
|
@ -326,6 +327,7 @@ def format_value(value, compact=None): |
|
|
|
def fetch_breakpoints(watchpoints=False, pending=False): |
|
|
|
def fetch_breakpoints(watchpoints=False, pending=False): |
|
|
|
# fetch breakpoints addresses |
|
|
|
# fetch breakpoints addresses |
|
|
|
parsed_breakpoints = dict() |
|
|
|
parsed_breakpoints = dict() |
|
|
|
|
|
|
|
catch_what_regex = re.compile(r'([^,]+".*")?[^,]*') |
|
|
|
for line in run('info breakpoints').split('\n'): |
|
|
|
for line in run('info breakpoints').split('\n'): |
|
|
|
# just keep numbered lines |
|
|
|
# just keep numbered lines |
|
|
|
if not line or not line[0].isdigit(): |
|
|
|
if not line or not line[0].isdigit(): |
|
|
|
@ -341,7 +343,11 @@ def fetch_breakpoints(watchpoints=False, pending=False): |
|
|
|
address = None if is_multiple or is_pending else int(fields[4], 16) |
|
|
|
address = None if is_multiple or is_pending else int(fields[4], 16) |
|
|
|
is_enabled = fields[3] == 'y' |
|
|
|
is_enabled = fields[3] == 'y' |
|
|
|
address_info = address, is_enabled |
|
|
|
address_info = address, is_enabled |
|
|
|
parsed_breakpoints[number] = [address_info], is_pending |
|
|
|
parsed_breakpoints[number] = [address_info], is_pending, '' |
|
|
|
|
|
|
|
elif len(fields) >= 5 and fields[1] == 'catchpoint': |
|
|
|
|
|
|
|
# only take before comma, but ignore commas in quotes |
|
|
|
|
|
|
|
what = catch_what_regex.search(' '.join(fields[4:])).group(0).strip() |
|
|
|
|
|
|
|
parsed_breakpoints[number] = [], False, what |
|
|
|
elif len(fields) >= 3 and number in parsed_breakpoints: |
|
|
|
elif len(fields) >= 3 and number in parsed_breakpoints: |
|
|
|
# add this address to the list of multiple locations |
|
|
|
# add this address to the list of multiple locations |
|
|
|
address = int(fields[2], 16) |
|
|
|
address = int(fields[2], 16) |
|
|
|
@ -350,7 +356,7 @@ def fetch_breakpoints(watchpoints=False, pending=False): |
|
|
|
parsed_breakpoints[number][0].append(address_info) |
|
|
|
parsed_breakpoints[number][0].append(address_info) |
|
|
|
else: |
|
|
|
else: |
|
|
|
# watchpoints |
|
|
|
# watchpoints |
|
|
|
parsed_breakpoints[number] = [], False |
|
|
|
parsed_breakpoints[number] = [], False, '' |
|
|
|
except ValueError: |
|
|
|
except ValueError: |
|
|
|
pass |
|
|
|
pass |
|
|
|
# fetch breakpoints from the API and complement with address and source |
|
|
|
# fetch breakpoints from the API and complement with address and source |
|
|
|
@ -361,7 +367,7 @@ def fetch_breakpoints(watchpoints=False, pending=False): |
|
|
|
# skip internal breakpoints |
|
|
|
# skip internal breakpoints |
|
|
|
if gdb_breakpoint.number < 0: |
|
|
|
if gdb_breakpoint.number < 0: |
|
|
|
continue |
|
|
|
continue |
|
|
|
addresses, is_pending = parsed_breakpoints[gdb_breakpoint.number] |
|
|
|
addresses, is_pending, what = parsed_breakpoints[gdb_breakpoint.number] |
|
|
|
is_pending = getattr(gdb_breakpoint, 'pending', is_pending) |
|
|
|
is_pending = getattr(gdb_breakpoint, 'pending', is_pending) |
|
|
|
if not pending and is_pending: |
|
|
|
if not pending and is_pending: |
|
|
|
continue |
|
|
|
continue |
|
|
|
@ -378,6 +384,7 @@ def fetch_breakpoints(watchpoints=False, pending=False): |
|
|
|
breakpoint['temporary'] = gdb_breakpoint.temporary |
|
|
|
breakpoint['temporary'] = gdb_breakpoint.temporary |
|
|
|
breakpoint['hit_count'] = gdb_breakpoint.hit_count |
|
|
|
breakpoint['hit_count'] = gdb_breakpoint.hit_count |
|
|
|
breakpoint['pending'] = is_pending |
|
|
|
breakpoint['pending'] = is_pending |
|
|
|
|
|
|
|
breakpoint['what'] = what |
|
|
|
# add addresses and source information |
|
|
|
# add addresses and source information |
|
|
|
breakpoint['addresses'] = [] |
|
|
|
breakpoint['addresses'] = [] |
|
|
|
for address, is_enabled in addresses: |
|
|
|
for address, is_enabled in addresses: |
|
|
|
@ -574,6 +581,8 @@ class Dashboard(gdb.Command): |
|
|
|
|
|
|
|
|
|
|
|
@staticmethod |
|
|
|
@staticmethod |
|
|
|
def start(): |
|
|
|
def start(): |
|
|
|
|
|
|
|
# save the instance for customization convenience |
|
|
|
|
|
|
|
global dashboard |
|
|
|
# initialize the dashboard |
|
|
|
# initialize the dashboard |
|
|
|
dashboard = Dashboard() |
|
|
|
dashboard = Dashboard() |
|
|
|
Dashboard.set_custom_prompt(dashboard) |
|
|
|
Dashboard.set_custom_prompt(dashboard) |
|
|
|
@ -603,11 +612,13 @@ class Dashboard(gdb.Command): |
|
|
|
else: |
|
|
|
else: |
|
|
|
import termios |
|
|
|
import termios |
|
|
|
import fcntl |
|
|
|
import fcntl |
|
|
|
# first 2 shorts (4 byte) of struct winsize |
|
|
|
# first 2 shorts of struct winsize |
|
|
|
raw = fcntl.ioctl(fd, termios.TIOCGWINSZ, ' ' * 4) |
|
|
|
winsize_format = 'hhhh' |
|
|
|
height, width = struct.unpack('hh', raw) |
|
|
|
buffer = b'\x00' * struct.calcsize(winsize_format) |
|
|
|
|
|
|
|
buffer = fcntl.ioctl(fd, termios.TIOCGWINSZ, buffer) |
|
|
|
|
|
|
|
height, width, _, _ = struct.unpack(winsize_format, buffer) |
|
|
|
return int(width), int(height) |
|
|
|
return int(width), int(height) |
|
|
|
except (ImportError, OSError): |
|
|
|
except (ImportError, OSError, SystemError): |
|
|
|
# this happens when no curses library is found on windows or when |
|
|
|
# this happens when no curses library is found on windows or when |
|
|
|
# the terminal is not properly configured |
|
|
|
# the terminal is not properly configured |
|
|
|
return 80, 24 # hardcoded fallback value |
|
|
|
return 80, 24 # hardcoded fallback value |
|
|
|
@ -641,7 +652,8 @@ class Dashboard(gdb.Command): |
|
|
|
# process all the init files in order |
|
|
|
# process all the init files in order |
|
|
|
for root, dirs, files in itertools.chain.from_iterable(inits_dirs): |
|
|
|
for root, dirs, files in itertools.chain.from_iterable(inits_dirs): |
|
|
|
dirs.sort() |
|
|
|
dirs.sort() |
|
|
|
for init in sorted(files): |
|
|
|
# skipping dotfiles |
|
|
|
|
|
|
|
for init in sorted(file for file in files if not file.startswith('.')): |
|
|
|
path = os.path.join(root, init) |
|
|
|
path = os.path.join(root, init) |
|
|
|
_, ext = os.path.splitext(path) |
|
|
|
_, ext = os.path.splitext(path) |
|
|
|
# either load Python files or GDB |
|
|
|
# either load Python files or GDB |
|
|
|
@ -665,7 +677,18 @@ class Dashboard(gdb.Command): |
|
|
|
|
|
|
|
|
|
|
|
@staticmethod |
|
|
|
@staticmethod |
|
|
|
def create_command(name, invoke, doc, is_prefix, complete=None): |
|
|
|
def create_command(name, invoke, doc, is_prefix, complete=None): |
|
|
|
Class = type('', (gdb.Command,), {'invoke': invoke, '__doc__': doc}) |
|
|
|
if callable(complete): |
|
|
|
|
|
|
|
Class = type('', (gdb.Command,), { |
|
|
|
|
|
|
|
'__doc__': doc, |
|
|
|
|
|
|
|
'invoke': invoke, |
|
|
|
|
|
|
|
'complete': complete |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
Class(name, gdb.COMMAND_USER, prefix=is_prefix) |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
Class = type('', (gdb.Command,), { |
|
|
|
|
|
|
|
'__doc__': doc, |
|
|
|
|
|
|
|
'invoke': invoke |
|
|
|
|
|
|
|
}) |
|
|
|
Class(name, gdb.COMMAND_USER, complete or gdb.COMPLETE_NONE, is_prefix) |
|
|
|
Class(name, gdb.COMMAND_USER, complete or gdb.COMPLETE_NONE, is_prefix) |
|
|
|
|
|
|
|
|
|
|
|
@staticmethod |
|
|
|
@staticmethod |
|
|
|
@ -688,7 +711,7 @@ class Dashboard(gdb.Command): |
|
|
|
# ANSI: move the cursor to top-left corner and clear the screen |
|
|
|
# ANSI: move the cursor to top-left corner and clear the screen |
|
|
|
# (optionally also clear the scrollback buffer if supported by the |
|
|
|
# (optionally also clear the scrollback buffer if supported by the |
|
|
|
# terminal) |
|
|
|
# terminal) |
|
|
|
return '\x1b[H\x1b[J' + '\x1b[3J' if R.discard_scrollback else '' |
|
|
|
return '\x1b[H\x1b[2J' + ('\x1b[3J' if R.discard_scrollback else '') |
|
|
|
|
|
|
|
|
|
|
|
@staticmethod |
|
|
|
@staticmethod |
|
|
|
def setup_terminal(): |
|
|
|
def setup_terminal(): |
|
|
|
@ -1157,7 +1180,10 @@ class Source(Dashboard.Module): |
|
|
|
self.offset = 0 |
|
|
|
self.offset = 0 |
|
|
|
|
|
|
|
|
|
|
|
def label(self): |
|
|
|
def label(self): |
|
|
|
return 'Source' |
|
|
|
label = 'Source' |
|
|
|
|
|
|
|
if self.show_path and self.file_name: |
|
|
|
|
|
|
|
label += ': {}'.format(self.file_name) |
|
|
|
|
|
|
|
return label |
|
|
|
|
|
|
|
|
|
|
|
def lines(self, term_width, term_height, style_changed): |
|
|
|
def lines(self, term_width, term_height, style_changed): |
|
|
|
# skip if the current thread is not stopped |
|
|
|
# skip if the current thread is not stopped |
|
|
|
@ -1167,6 +1193,7 @@ class Source(Dashboard.Module): |
|
|
|
sal = gdb.selected_frame().find_sal() |
|
|
|
sal = gdb.selected_frame().find_sal() |
|
|
|
current_line = sal.line |
|
|
|
current_line = sal.line |
|
|
|
if current_line == 0: |
|
|
|
if current_line == 0: |
|
|
|
|
|
|
|
self.file_name = None |
|
|
|
return [] |
|
|
|
return [] |
|
|
|
# try to lookup the source file |
|
|
|
# try to lookup the source file |
|
|
|
candidates = [ |
|
|
|
candidates = [ |
|
|
|
@ -1283,6 +1310,12 @@ A value of 0 uses the whole height.''', |
|
|
|
'type': int, |
|
|
|
'type': int, |
|
|
|
'check': check_gt_zero |
|
|
|
'check': check_gt_zero |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
'path': { |
|
|
|
|
|
|
|
'doc': 'Path visibility flag in the module label.', |
|
|
|
|
|
|
|
'default': False, |
|
|
|
|
|
|
|
'name': 'show_path', |
|
|
|
|
|
|
|
'type': bool |
|
|
|
|
|
|
|
}, |
|
|
|
'highlight-line': { |
|
|
|
'highlight-line': { |
|
|
|
'doc': 'Decide whether the whole current line should be highlighted.', |
|
|
|
'doc': 'Decide whether the whole current line should be highlighted.', |
|
|
|
'default': False, |
|
|
|
'default': False, |
|
|
|
@ -1322,7 +1355,7 @@ The instructions constituting the current statement are marked, if available.''' |
|
|
|
flavor = gdb.parameter('disassembly-flavor') |
|
|
|
flavor = gdb.parameter('disassembly-flavor') |
|
|
|
except: |
|
|
|
except: |
|
|
|
flavor = 'att' # not always defined (see #36) |
|
|
|
flavor = 'att' # not always defined (see #36) |
|
|
|
highlighter = Beautifier(flavor) |
|
|
|
highlighter = Beautifier(flavor, tab_size=None) |
|
|
|
# fetch the assembly code |
|
|
|
# fetch the assembly code |
|
|
|
line_info = None |
|
|
|
line_info = None |
|
|
|
frame = gdb.selected_frame() # PC is here |
|
|
|
frame = gdb.selected_frame() # PC is here |
|
|
|
@ -1628,46 +1661,53 @@ Optionally list the frame arguments and locals too.''' |
|
|
|
# skip if the current thread is not stopped |
|
|
|
# skip if the current thread is not stopped |
|
|
|
if not gdb.selected_thread().is_stopped(): |
|
|
|
if not gdb.selected_thread().is_stopped(): |
|
|
|
return [] |
|
|
|
return [] |
|
|
|
# find the selected frame (i.e., the first to display) |
|
|
|
# find the selected frame level (XXX Frame.level() is a recent addition) |
|
|
|
selected_index = 0 |
|
|
|
start_level = 0 |
|
|
|
frame = gdb.newest_frame() |
|
|
|
frame = gdb.newest_frame() |
|
|
|
while frame: |
|
|
|
while frame: |
|
|
|
if frame == gdb.selected_frame(): |
|
|
|
if frame == gdb.selected_frame(): |
|
|
|
break |
|
|
|
break |
|
|
|
frame = frame.older() |
|
|
|
frame = frame.older() |
|
|
|
selected_index += 1 |
|
|
|
start_level += 1 |
|
|
|
# format up to "limit" frames |
|
|
|
# gather the frames |
|
|
|
frames = [] |
|
|
|
|
|
|
|
number = selected_index |
|
|
|
|
|
|
|
more = False |
|
|
|
more = False |
|
|
|
while frame: |
|
|
|
frames = [gdb.selected_frame()] |
|
|
|
# the first is the selected one |
|
|
|
going_down = True |
|
|
|
selected = (len(frames) == 0) |
|
|
|
while True: |
|
|
|
# fetch frame info |
|
|
|
# stack frames limit reached |
|
|
|
style = R.style_selected_1 if selected else R.style_selected_2 |
|
|
|
if len(frames) == self.limit: |
|
|
|
frame_id = ansi(str(number), style) |
|
|
|
|
|
|
|
info = Stack.get_pc_line(frame, style) |
|
|
|
|
|
|
|
frame_lines = [] |
|
|
|
|
|
|
|
frame_lines.append('[{}] {}'.format(frame_id, info)) |
|
|
|
|
|
|
|
# add frame arguments and locals |
|
|
|
|
|
|
|
variables = Variables.format_frame( |
|
|
|
|
|
|
|
frame, self.show_arguments, self.show_locals, self.compact, self.align, self.sort) |
|
|
|
|
|
|
|
frame_lines.extend(variables) |
|
|
|
|
|
|
|
# add frame |
|
|
|
|
|
|
|
frames.append(frame_lines) |
|
|
|
|
|
|
|
# next |
|
|
|
|
|
|
|
frame = frame.older() |
|
|
|
|
|
|
|
number += 1 |
|
|
|
|
|
|
|
# check finished according to the limit |
|
|
|
|
|
|
|
if self.limit and len(frames) == self.limit: |
|
|
|
|
|
|
|
# more frames to show but limited |
|
|
|
|
|
|
|
if frame: |
|
|
|
|
|
|
|
more = True |
|
|
|
more = True |
|
|
|
break |
|
|
|
break |
|
|
|
|
|
|
|
# zigzag the frames starting from the selected one |
|
|
|
|
|
|
|
if going_down: |
|
|
|
|
|
|
|
frame = frames[-1].older() |
|
|
|
|
|
|
|
if frame: |
|
|
|
|
|
|
|
frames.append(frame) |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
frame = frames[0].newer() |
|
|
|
|
|
|
|
if frame: |
|
|
|
|
|
|
|
frames.insert(0, frame) |
|
|
|
|
|
|
|
start_level -= 1 |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
break |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
frame = frames[0].newer() |
|
|
|
|
|
|
|
if frame: |
|
|
|
|
|
|
|
frames.insert(0, frame) |
|
|
|
|
|
|
|
start_level -= 1 |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
frame = frames[-1].older() |
|
|
|
|
|
|
|
if frame: |
|
|
|
|
|
|
|
frames.append(frame) |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
break |
|
|
|
|
|
|
|
# switch direction |
|
|
|
|
|
|
|
going_down = not going_down |
|
|
|
# format the output |
|
|
|
# format the output |
|
|
|
lines = [] |
|
|
|
lines = [] |
|
|
|
for frame_lines in frames: |
|
|
|
for number, frame in enumerate(frames, start=start_level): |
|
|
|
lines.extend(frame_lines) |
|
|
|
selected = frame == gdb.selected_frame() |
|
|
|
|
|
|
|
lines.extend(self.get_frame_lines(number, frame, selected)) |
|
|
|
# add the placeholder |
|
|
|
# add the placeholder |
|
|
|
if more: |
|
|
|
if more: |
|
|
|
lines.append('[{}]'.format(ansi('+', R.style_selected_2))) |
|
|
|
lines.append('[{}]'.format(ansi('+', R.style_selected_2))) |
|
|
|
@ -1710,6 +1750,19 @@ Optionally list the frame arguments and locals too.''' |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_frame_lines(self, number, frame, selected=False): |
|
|
|
|
|
|
|
# fetch frame info |
|
|
|
|
|
|
|
style = R.style_selected_1 if selected else R.style_selected_2 |
|
|
|
|
|
|
|
frame_id = ansi(str(number), style) |
|
|
|
|
|
|
|
info = Stack.get_pc_line(frame, style) |
|
|
|
|
|
|
|
frame_lines = [] |
|
|
|
|
|
|
|
frame_lines.append('[{}] {}'.format(frame_id, info)) |
|
|
|
|
|
|
|
# add frame arguments and locals |
|
|
|
|
|
|
|
variables = Variables.format_frame( |
|
|
|
|
|
|
|
frame, self.show_arguments, self.show_locals, self.compact, self.align, self.sort) |
|
|
|
|
|
|
|
frame_lines.extend(variables) |
|
|
|
|
|
|
|
return frame_lines |
|
|
|
|
|
|
|
|
|
|
|
@staticmethod |
|
|
|
@staticmethod |
|
|
|
def format_line(prefix, line): |
|
|
|
def format_line(prefix, line): |
|
|
|
prefix = ansi(prefix, R.style_low) |
|
|
|
prefix = ansi(prefix, R.style_low) |
|
|
|
@ -1966,6 +2019,10 @@ class Registers(Dashboard.Module): |
|
|
|
changed = self.table and (self.table.get(name, '') != string_value) |
|
|
|
changed = self.table and (self.table.get(name, '') != string_value) |
|
|
|
self.table[name] = string_value |
|
|
|
self.table[name] = string_value |
|
|
|
registers.append((name, string_value, changed)) |
|
|
|
registers.append((name, string_value, changed)) |
|
|
|
|
|
|
|
# handle the empty register list |
|
|
|
|
|
|
|
if not registers: |
|
|
|
|
|
|
|
msg = 'No registers to show (check the "dashboard registers -style list" attribute)' |
|
|
|
|
|
|
|
return [ansi(msg, R.style_error)] |
|
|
|
# compute lengths considering an extra space between and around the |
|
|
|
# compute lengths considering an extra space between and around the |
|
|
|
# entries (hence the +2 and term_width - 1) |
|
|
|
# entries (hence the +2 and term_width - 1) |
|
|
|
max_name = max(len(name) for name, _, _ in registers) |
|
|
|
max_name = max(len(name) for name, _, _ in registers) |
|
|
|
@ -2017,7 +2074,8 @@ class Registers(Dashboard.Module): |
|
|
|
'list': { |
|
|
|
'list': { |
|
|
|
'doc': '''String of space-separated register names to display. |
|
|
|
'doc': '''String of space-separated register names to display. |
|
|
|
|
|
|
|
|
|
|
|
The empty list (default) causes to show all the available registers.''', |
|
|
|
The empty list (default) causes to show all the available registers. For |
|
|
|
|
|
|
|
architectures different from x86 setting this attribute might be mandatory.''', |
|
|
|
'default': '', |
|
|
|
'default': '', |
|
|
|
'name': 'register_list', |
|
|
|
'name': 'register_list', |
|
|
|
} |
|
|
|
} |
|
|
|
@ -2043,7 +2101,7 @@ The empty list (default) causes to show all the available registers.''', |
|
|
|
if len(fields) != 7: |
|
|
|
if len(fields) != 7: |
|
|
|
continue |
|
|
|
continue |
|
|
|
name, _, _, _, _, _, groups = fields |
|
|
|
name, _, _, _, _, _, groups = fields |
|
|
|
if not re.match('\w', name): |
|
|
|
if not re.match(r'\w', name): |
|
|
|
continue |
|
|
|
continue |
|
|
|
for group in groups.split(','): |
|
|
|
for group in groups.split(','): |
|
|
|
if group in (match_groups or ('general',)): |
|
|
|
if group in (match_groups or ('general',)): |
|
|
|
@ -2118,7 +2176,7 @@ class Expressions(Dashboard.Module): |
|
|
|
'''Watch user expressions.''' |
|
|
|
'''Watch user expressions.''' |
|
|
|
|
|
|
|
|
|
|
|
def __init__(self): |
|
|
|
def __init__(self): |
|
|
|
self.table = set() |
|
|
|
self.table = [] |
|
|
|
|
|
|
|
|
|
|
|
def label(self): |
|
|
|
def label(self): |
|
|
|
return 'Expressions' |
|
|
|
return 'Expressions' |
|
|
|
@ -2129,9 +2187,9 @@ class Expressions(Dashboard.Module): |
|
|
|
if self.align: |
|
|
|
if self.align: |
|
|
|
label_width = max(len(expression) for expression in self.table) if self.table else 0 |
|
|
|
label_width = max(len(expression) for expression in self.table) if self.table else 0 |
|
|
|
default_radix = Expressions.get_default_radix() |
|
|
|
default_radix = Expressions.get_default_radix() |
|
|
|
for expression in self.table: |
|
|
|
for number, expression in enumerate(self.table, start=1): |
|
|
|
label = expression |
|
|
|
label = expression |
|
|
|
match = re.match('^/(\d+) +(.+)$', expression) |
|
|
|
match = re.match(r'^/(\d+) +(.+)$', expression) |
|
|
|
try: |
|
|
|
try: |
|
|
|
if match: |
|
|
|
if match: |
|
|
|
radix, expression = match.groups() |
|
|
|
radix, expression = match.groups() |
|
|
|
@ -2142,9 +2200,10 @@ class Expressions(Dashboard.Module): |
|
|
|
finally: |
|
|
|
finally: |
|
|
|
if match: |
|
|
|
if match: |
|
|
|
run('set output-radix {}'.format(default_radix)) |
|
|
|
run('set output-radix {}'.format(default_radix)) |
|
|
|
|
|
|
|
number = ansi(str(number), R.style_selected_2) |
|
|
|
label = ansi(expression, R.style_high) + ' ' * (label_width - len(expression)) |
|
|
|
label = ansi(expression, R.style_high) + ' ' * (label_width - len(expression)) |
|
|
|
equal = ansi('=', R.style_low) |
|
|
|
equal = ansi('=', R.style_low) |
|
|
|
out.append('{} {} {}'.format(label, equal, value)) |
|
|
|
out.append('[{}] {} {} {}'.format(number, label, equal, value)) |
|
|
|
return out |
|
|
|
return out |
|
|
|
|
|
|
|
|
|
|
|
def commands(self): |
|
|
|
def commands(self): |
|
|
|
@ -2156,8 +2215,7 @@ class Expressions(Dashboard.Module): |
|
|
|
}, |
|
|
|
}, |
|
|
|
'unwatch': { |
|
|
|
'unwatch': { |
|
|
|
'action': self.unwatch, |
|
|
|
'action': self.unwatch, |
|
|
|
'doc': 'Stop watching an expression.', |
|
|
|
'doc': 'Stop watching an expression by index.' |
|
|
|
'complete': gdb.COMPLETE_EXPRESSION |
|
|
|
|
|
|
|
}, |
|
|
|
}, |
|
|
|
'clear': { |
|
|
|
'clear': { |
|
|
|
'action': self.clear, |
|
|
|
'action': self.clear, |
|
|
|
@ -2176,15 +2234,22 @@ class Expressions(Dashboard.Module): |
|
|
|
|
|
|
|
|
|
|
|
def watch(self, arg): |
|
|
|
def watch(self, arg): |
|
|
|
if arg: |
|
|
|
if arg: |
|
|
|
self.table.add(arg) |
|
|
|
if arg not in self.table: |
|
|
|
|
|
|
|
self.table.append(arg) |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
raise Exception('Expression already watched') |
|
|
|
else: |
|
|
|
else: |
|
|
|
raise Exception('Specify an expression') |
|
|
|
raise Exception('Specify an expression') |
|
|
|
|
|
|
|
|
|
|
|
def unwatch(self, arg): |
|
|
|
def unwatch(self, arg): |
|
|
|
if arg: |
|
|
|
if arg: |
|
|
|
try: |
|
|
|
try: |
|
|
|
self.table.remove(arg) |
|
|
|
number = int(arg) - 1 |
|
|
|
except: |
|
|
|
except: |
|
|
|
|
|
|
|
number = -1 |
|
|
|
|
|
|
|
if 0 <= number < len(self.table): |
|
|
|
|
|
|
|
self.table.pop(number) |
|
|
|
|
|
|
|
else: |
|
|
|
raise Exception('Expression not watched') |
|
|
|
raise Exception('Expression not watched') |
|
|
|
else: |
|
|
|
else: |
|
|
|
raise Exception('Specify an expression') |
|
|
|
raise Exception('Specify an expression') |
|
|
|
@ -2199,9 +2264,12 @@ class Expressions(Dashboard.Module): |
|
|
|
except RuntimeError: |
|
|
|
except RuntimeError: |
|
|
|
# XXX this is a fix for GDB <8.1.x see #161 |
|
|
|
# XXX this is a fix for GDB <8.1.x see #161 |
|
|
|
message = run('show output-radix') |
|
|
|
message = run('show output-radix') |
|
|
|
match = re.match('^Default output radix for printing of values is (\d+)\.$', message) |
|
|
|
match = re.match(r'^Default output radix for printing of values is (\d+)\.$', message) |
|
|
|
return match.groups()[0] if match else 10 # fallback |
|
|
|
return match.groups()[0] if match else 10 # fallback |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# XXX workaround to support BP_BREAKPOINT in older GDB versions |
|
|
|
|
|
|
|
setattr(gdb, 'BP_CATCHPOINT', getattr(gdb, 'BP_CATCHPOINT', 26)) |
|
|
|
|
|
|
|
|
|
|
|
class Breakpoints(Dashboard.Module): |
|
|
|
class Breakpoints(Dashboard.Module): |
|
|
|
'''Display the breakpoints list.''' |
|
|
|
'''Display the breakpoints list.''' |
|
|
|
|
|
|
|
|
|
|
|
@ -2210,7 +2278,8 @@ class Breakpoints(Dashboard.Module): |
|
|
|
gdb.BP_WATCHPOINT: 'watch', |
|
|
|
gdb.BP_WATCHPOINT: 'watch', |
|
|
|
gdb.BP_HARDWARE_WATCHPOINT: 'write watch', |
|
|
|
gdb.BP_HARDWARE_WATCHPOINT: 'write watch', |
|
|
|
gdb.BP_READ_WATCHPOINT: 'read watch', |
|
|
|
gdb.BP_READ_WATCHPOINT: 'read watch', |
|
|
|
gdb.BP_ACCESS_WATCHPOINT: 'access watch' |
|
|
|
gdb.BP_ACCESS_WATCHPOINT: 'access watch', |
|
|
|
|
|
|
|
gdb.BP_CATCHPOINT: 'catch' |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
def label(self): |
|
|
|
def label(self): |
|
|
|
@ -2260,6 +2329,9 @@ class Breakpoints(Dashboard.Module): |
|
|
|
# format user location |
|
|
|
# format user location |
|
|
|
location = breakpoint['location'] |
|
|
|
location = breakpoint['location'] |
|
|
|
line += ' for {}'.format(ansi(location, style)) |
|
|
|
line += ' for {}'.format(ansi(location, style)) |
|
|
|
|
|
|
|
elif breakpoint['type'] == gdb.BP_CATCHPOINT: |
|
|
|
|
|
|
|
what = breakpoint['what'] |
|
|
|
|
|
|
|
line += ' {}'.format(ansi(what, style)) |
|
|
|
else: |
|
|
|
else: |
|
|
|
# format user expression |
|
|
|
# format user expression |
|
|
|
expression = breakpoint['expression'] |
|
|
|
expression = breakpoint['expression'] |
|
|
|
@ -2304,6 +2376,11 @@ set python print-stack full |
|
|
|
|
|
|
|
|
|
|
|
python Dashboard.start() |
|
|
|
python Dashboard.start() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Fixes ------------------------------------------------------------------------ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# workaround for the GDB readline issue, see #325 |
|
|
|
|
|
|
|
python import sys; sys.modules['readline'] = None |
|
|
|
|
|
|
|
|
|
|
|
# File variables --------------------------------------------------------------- |
|
|
|
# File variables --------------------------------------------------------------- |
|
|
|
|
|
|
|
|
|
|
|
# vim: filetype=python |
|
|
|
# vim: filetype=python |
|
|
|
|