this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

GDB config

+19 -1219
+4 -2
Makefile
··· 1 1 LNFLAGS = -rsiT 2 2 export LN = ln $(LNFLAGS) 3 + 4 + export WGET = wget -Nq --show-progress 5 + 3 6 export PWD = $(shell pwd) 4 - WGET = wget -Nq --show-progress 5 - TARGETS = nvim tmux git conky 7 + TARGETS = nvim tmux git conky gdb 6 8 7 9 all: $(TARGETS) lein 8 10
+3
fish/functions/gdb.fish
··· 1 + function gdb --wrap gdb 2 + command gdb -q $argv 3 + end
+1
gdb/.gitignore
··· 1 + /dashboard
+10
gdb/Makefile
··· 1 + dashboard: 2 + $(WGET) -O$@ http://git.io/.gdbinit 3 + 4 + install: dashboard 5 + $(LN) dashboard ${HOME}/.gdbinit 6 + 7 + clean: 8 + rm ${HOME}/.gdbinit 9 + 10 + .PHONY: dashboard install clean
-1217
gdbinit
··· 1 - python 2 - 3 - # GDB dashboard - Modular visual interface for GDB in Python. 4 - # 5 - # https://github.com/cyrus-and/gdb-dashboard 6 - 7 - import ast 8 - import fcntl 9 - import os 10 - import re 11 - import struct 12 - import termios 13 - 14 - # Common attributes ------------------------------------------------------------ 15 - 16 - class R(): 17 - 18 - @staticmethod 19 - def attributes(): 20 - return { 21 - # miscellaneous 22 - 'ansi': { 23 - 'doc': 'Control the ANSI output of the dashboard.', 24 - 'default': True, 25 - 'type': bool 26 - }, 27 - # prompt 28 - 'prompt': { 29 - 'doc': """Command prompt. 30 - This value is parsed as a Python format string in which `{status}` is expanded 31 - with the substitution of either `prompt_running` or `prompt_not_running` 32 - attributes, according to the target program status. The resulting string must be 33 - a valid GDB prompt, see the command `python print(gdb.prompt.prompt_help())`""", 34 - 'default': '{status}' 35 - }, 36 - 'prompt_running': { 37 - 'doc': """`{status}` when the target program is running. 38 - See the `prompt` attribute. This value is parsed as a Python format string in 39 - which `{pid}` is expanded with the process identifier of the target program.""", 40 - 'default': '\[\e[1;35m\]>>>\[\e[0m\]' 41 - }, 42 - 'prompt_not_running': { 43 - 'doc': '`{status}` when the target program is not running.', 44 - 'default': '\[\e[1;30m\]>>>\[\e[0m\]' 45 - }, 46 - # divider 47 - 'divider_fill_char_primary': { 48 - 'doc': 'Filler around the label for primary dividers', 49 - 'default': '─' 50 - }, 51 - 'divider_fill_char_secondary': { 52 - 'doc': 'Filler around the label for secondary dividers', 53 - 'default': '─' 54 - }, 55 - 'divider_fill_style_primary': { 56 - 'doc': 'Style for `divider_fill_char_primary`', 57 - 'default': '36' 58 - }, 59 - 'divider_fill_style_secondary': { 60 - 'doc': 'Style for `divider_fill_char_secondary`', 61 - 'default': '1;30' 62 - }, 63 - 'divider_label_style_on_primary': { 64 - 'doc': 'Label style for non-empty primary dividers', 65 - 'default': '1;33' 66 - }, 67 - 'divider_label_style_on_secondary': { 68 - 'doc': 'Label style for non-empty secondary dividers', 69 - 'default': '0' 70 - }, 71 - 'divider_label_style_off_primary': { 72 - 'doc': 'Label style for empty primary dividers', 73 - 'default': '33' 74 - }, 75 - 'divider_label_style_off_secondary': { 76 - 'doc': 'Label style for empty secondary dividers', 77 - 'default': '1;30' 78 - }, 79 - 'divider_label_skip': { 80 - 'doc': 'Gap between the aligning border and the label.', 81 - 'default': 3, 82 - 'type': int, 83 - 'check': check_ge_zero 84 - }, 85 - 'divider_label_margin': { 86 - 'doc': 'Number of spaces around the label.', 87 - 'default': 1, 88 - 'type': int, 89 - 'check': check_ge_zero 90 - }, 91 - 'divider_label_align_right': { 92 - 'doc': 'Label alignment flag.', 93 - 'default': False, 94 - 'type': bool 95 - }, 96 - # common styles 97 - 'style_selected_1': { 98 - 'default': '1;32' 99 - }, 100 - 'style_selected_2': { 101 - 'default': '32' 102 - }, 103 - 'style_low': { 104 - 'default': '1;30' 105 - }, 106 - 'style_high': { 107 - 'default': '1;37' 108 - }, 109 - 'style_error': { 110 - 'default': '31' 111 - } 112 - } 113 - 114 - # Common ----------------------------------------------------------------------- 115 - 116 - def run(command): 117 - return gdb.execute(command, to_string=True) 118 - 119 - def ansi(string, style): 120 - if R.ansi: 121 - return '\x1b[{}m{}\x1b[0m'.format(style, string) 122 - else: 123 - return string 124 - 125 - def divider(label='', primary=False, active=True): 126 - width = Dashboard.term_width 127 - if primary: 128 - divider_fill_style = R.divider_fill_style_primary 129 - divider_fill_char = R.divider_fill_char_primary 130 - divider_label_style_on = R.divider_label_style_on_primary 131 - divider_label_style_off = R.divider_label_style_off_primary 132 - else: 133 - divider_fill_style = R.divider_fill_style_secondary 134 - divider_fill_char = R.divider_fill_char_secondary 135 - divider_label_style_on = R.divider_label_style_on_secondary 136 - divider_label_style_off = R.divider_label_style_off_secondary 137 - if label: 138 - if active: 139 - divider_label_style = divider_label_style_on 140 - else: 141 - divider_label_style = divider_label_style_off 142 - skip = R.divider_label_skip 143 - margin = R.divider_label_margin 144 - before = ansi(divider_fill_char * skip, divider_fill_style) 145 - middle = ansi(label, divider_label_style) 146 - after_length = width - len(label) - skip - 2 * margin 147 - after = ansi(divider_fill_char * after_length, divider_fill_style) 148 - if R.divider_label_align_right: 149 - before, after = after, before 150 - return ''.join([before, ' ' * margin, middle, ' ' * margin, after]) 151 - else: 152 - return ansi(divider_fill_char * width, divider_fill_style) 153 - 154 - def check_gt_zero(x): 155 - return x > 0 156 - 157 - def check_ge_zero(x): 158 - return x >= 0 159 - 160 - def to_unsigned(value, size=8): 161 - # values from GDB can be used transparently but are not suitable for 162 - # being printed as unsigned integers, so a conversion is needed 163 - return int(value.cast(gdb.Value(0).type)) % (2 ** (size * 8)) 164 - 165 - def to_string(value): 166 - # attempt to convert an inferior value to string; OK when (Python 3 || 167 - # simple ASCII); otherwise (Python 2.7 && not ASCII) encode the string as 168 - # utf8 169 - try: 170 - value_string = str(value) 171 - except UnicodeEncodeError: 172 - value_string = unicode(value).encode('utf8') 173 - return value_string 174 - 175 - def format_address(address): 176 - pointer_size = gdb.parse_and_eval('$pc').type.sizeof 177 - return ('0x{{:0{}x}}').format(pointer_size * 2).format(address) 178 - 179 - # Dashboard -------------------------------------------------------------------- 180 - 181 - class Dashboard(gdb.Command): 182 - """Redisplay the dashboard.""" 183 - 184 - def __init__(self): 185 - gdb.Command.__init__(self, 'dashboard', 186 - gdb.COMMAND_USER, gdb.COMPLETE_NONE, True) 187 - self.output = None # main terminal 188 - self.enabled = True 189 - # setup subcommands 190 - Dashboard.OutputCommand(self) 191 - Dashboard.EnabledCommand(self) 192 - Dashboard.LayoutCommand(self) 193 - # setup style commands 194 - Dashboard.StyleCommand(self, 'dashboard', R, R.attributes()) 195 - # setup events 196 - gdb.events.cont.connect(lambda _: self.on_continue()) 197 - gdb.events.stop.connect(lambda _: self.on_stop()) 198 - gdb.events.exited.connect(lambda _: self.on_exit()) 199 - 200 - def on_continue(self): 201 - # try to contain the GDB messages is a specified are unless the 202 - # dashboard is printed to a separate file 203 - if self.enabled and self.is_running() and not self.output: 204 - Dashboard.update_term_width() 205 - gdb.write(Dashboard.clear_screen()) 206 - gdb.write(divider('Output/messages', True)) 207 - gdb.write('\n') 208 - gdb.flush() 209 - 210 - def on_stop(self): 211 - # redisplay the dashboard when the target program stops (the screen is 212 - # cleared by on_continue when the dashboard is printed to a separate 213 - # file) 214 - if self.enabled and self.is_running(): 215 - clear = Dashboard.clear_screen() if self.output else '' 216 - self.display(clear, self.build(), '\n') 217 - 218 - def on_exit(self): 219 - pass 220 - 221 - def load_modules(self, modules): 222 - self.modules = [] 223 - for module in modules: 224 - info = Dashboard.ModuleInfo(self, module) 225 - self.modules.append(info) 226 - 227 - def redisplay(self): 228 - # manually redisplay the dashboard 229 - if self.is_running(): 230 - self.display(Dashboard.clear_screen(), self.build(), '') 231 - 232 - def inferior_pid(self): 233 - return gdb.selected_inferior().pid 234 - 235 - def is_running(self): 236 - return self.inferior_pid() != 0 237 - 238 - def build(self): 239 - # fetch the output width 240 - try: 241 - fd = self.output.fileno() if self.output else 1 # main terminal 242 - Dashboard.update_term_width(fd) 243 - except: 244 - # fall back to the main terminal 245 - Dashboard.update_term_width() 246 - # fetch lines 247 - lines = [] 248 - for module in self.modules: 249 - if not module.enabled: 250 - continue 251 - module = module.instance 252 - # active if more than zero lines 253 - module_lines = module.lines() 254 - lines.append(divider(module.label(), True, module_lines)) 255 - lines.extend(module_lines) 256 - if len(lines) == 0: 257 - lines.append(divider('Error', True)) 258 - if len(self.modules) == 0: 259 - lines.append('No module loaded') 260 - else: 261 - lines.append('No module to display (see `help dashboard`)') 262 - lines.append(divider(primary=True)) 263 - # print the dashboard 264 - return '\n'.join(lines) 265 - 266 - def display(self, *data): 267 - # gdb module has both write() and flush() 268 - try: 269 - output = self.output or gdb 270 - for string in data: 271 - output.write(string) 272 - output.flush() 273 - except: 274 - Dashboard.err('Cannot write the dashboard') 275 - 276 - # Utility methods -------------------------------------------------------------- 277 - 278 - @staticmethod 279 - def start(): 280 - # initialize the dashboard 281 - dashboard = Dashboard() 282 - Dashboard.set_custom_prompt(dashboard) 283 - # parse Python inits, load modules then parse GDB inits 284 - Dashboard.parse_inits(True) 285 - modules = Dashboard.get_modules() 286 - dashboard.load_modules(modules) 287 - Dashboard.parse_inits(False) 288 - # GDB overrides 289 - run('set pagination off') 290 - run('alias -a db = dashboard') 291 - 292 - @staticmethod 293 - def update_term_width(fd=1): # defaults to the main terminal 294 - # first 2 shorts (4 byte) of struct winsize 295 - raw = fcntl.ioctl(fd, termios.TIOCGWINSZ, ' ' * 4) 296 - height, width = struct.unpack('hh', raw) 297 - Dashboard.term_width = int(width) 298 - 299 - @staticmethod 300 - def set_custom_prompt(dashboard): 301 - def custom_prompt(_): 302 - # render thread status indicator 303 - if dashboard.is_running(): 304 - pid = dashboard.inferior_pid() 305 - status = R.prompt_running.format(pid=pid) 306 - else: 307 - status = R.prompt_not_running 308 - # build prompt 309 - prompt = R.prompt.format(status=status) 310 - prompt = gdb.prompt.substitute_prompt(prompt) 311 - return prompt + ' ' # force trailing space 312 - gdb.prompt_hook = custom_prompt 313 - 314 - @staticmethod 315 - def parse_inits(python): 316 - for root, dirs, files in os.walk(os.path.expanduser('~/.gdbinit.d/')): 317 - dirs.sort() 318 - for init in sorted(files): 319 - path = os.path.join(root, init) 320 - _, ext = os.path.splitext(path) 321 - # either load Python files or GDB 322 - if python ^ (ext != '.py'): 323 - gdb.execute('source ' + path) 324 - 325 - @staticmethod 326 - def get_modules(): 327 - # scan the scope for modules 328 - modules = [] 329 - for name in globals(): 330 - obj = globals()[name] 331 - try: 332 - if issubclass(obj, Dashboard.Module): 333 - modules.append(obj) 334 - except TypeError: 335 - continue 336 - # sort modules alphabetically 337 - modules.sort(key=lambda x: x.__name__) 338 - return modules 339 - 340 - @staticmethod 341 - def create_command(name, invoke, doc, is_prefix, complete=None): 342 - Class = type('', (gdb.Command,), {'invoke': invoke, '__doc__': doc}) 343 - Class(name, gdb.COMMAND_USER, complete or gdb.COMPLETE_NONE, is_prefix) 344 - 345 - @staticmethod 346 - def err(string): 347 - print(ansi(string, R.style_error)) 348 - 349 - @staticmethod 350 - def complete(word, candidates): 351 - matching = [] 352 - for candidate in candidates: 353 - if candidate.startswith(word): 354 - matching.append(candidate) 355 - return matching 356 - 357 - @staticmethod 358 - def parse_arg(arg): 359 - # encode unicode GDB command arguments as utf8 in Python 2.7 360 - if type(arg) is not str: 361 - arg = arg.encode('utf8') 362 - return arg 363 - 364 - @staticmethod 365 - def clear_screen(): 366 - return '\x1b[H\x1b[2J' 367 - 368 - # Module descriptor ------------------------------------------------------------ 369 - 370 - class ModuleInfo: 371 - 372 - def __init__(self, dashboard, module): 373 - self.name = module.__name__.lower() # from class to module name 374 - self.enabled = True 375 - self.instance = module() 376 - self.doc = self.instance.__doc__ or '(no documentation)' 377 - self.prefix = 'dashboard {}'.format(self.name) 378 - # add GDB commands 379 - self.add_main_command(dashboard) 380 - self.add_style_command(dashboard) 381 - self.add_subcommands(dashboard) 382 - 383 - def add_main_command(self, dashboard): 384 - module = self 385 - def invoke(self, arg, from_tty, info=self): 386 - arg = Dashboard.parse_arg(arg) 387 - if arg == '': 388 - info.enabled ^= True 389 - if dashboard.is_running(): 390 - dashboard.redisplay() 391 - else: 392 - status = 'enabled' if info.enabled else 'disabled' 393 - print('{} module {}'.format(module.name, status)) 394 - else: 395 - Dashboard.err('Wrong argument "{}"'.format(arg)) 396 - doc_brief = 'Configure the {} module.'.format(self.name) 397 - doc_extended = 'Toggle the module visibility.' 398 - doc = '{}\n{}\n\n{}'.format(doc_brief, doc_extended, self.doc) 399 - Dashboard.create_command(self.prefix, invoke, doc, True) 400 - 401 - def add_style_command(self, dashboard): 402 - if 'attributes' in dir(self.instance): 403 - Dashboard.StyleCommand(dashboard, self.prefix, self.instance, 404 - self.instance.attributes()) 405 - 406 - def add_subcommands(self, dashboard): 407 - if 'commands' in dir(self.instance): 408 - for name, command in self.instance.commands().items(): 409 - self.add_subcommand(dashboard, name, command) 410 - 411 - def add_subcommand(self, dashboard, name, command): 412 - action = command['action'] 413 - doc = command['doc'] 414 - complete = command.get('complete') 415 - def invoke(self, arg, from_tty, info=self): 416 - arg = Dashboard.parse_arg(arg) 417 - if info.enabled: 418 - try: 419 - action(arg) 420 - except Exception as e: 421 - Dashboard.err(e) 422 - return 423 - # don't catch redisplay errors 424 - dashboard.redisplay() 425 - else: 426 - Dashboard.err('Module disabled') 427 - prefix = '{} {}'.format(self.prefix, name) 428 - Dashboard.create_command(prefix, invoke, doc, False, complete) 429 - 430 - # GDB commands ----------------------------------------------------------------- 431 - 432 - def invoke(self, arg, from_tty): 433 - arg = Dashboard.parse_arg(arg) 434 - if arg == '': 435 - if self.is_running(): 436 - self.redisplay() 437 - else: 438 - Dashboard.err('Is the target program running?') 439 - else: 440 - Dashboard.err('Wrong argument "{}"'.format(arg)) 441 - 442 - class OutputCommand(gdb.Command): 443 - """Set the dashboard output file/TTY. 444 - The dashboard will be appended to the specified file, which will be created if 445 - it does not exists. If the specified file identifies a terminal then its width 446 - will be used to format the dashboard, otherwise falls back to the width of the 447 - main GDB terminal. Without argument the dashboard will be printed on standard 448 - output (default).""" 449 - 450 - def __init__(self, dashboard): 451 - gdb.Command.__init__(self, 'dashboard -output', 452 - gdb.COMMAND_USER, gdb.COMPLETE_FILENAME) 453 - self.dashboard = dashboard 454 - 455 - def invoke(self, arg, from_tty): 456 - arg = Dashboard.parse_arg(arg) 457 - # close the previous output file, if any 458 - if self.dashboard.output: 459 - self.dashboard.output.close() 460 - # set or open the output file 461 - if arg == '': 462 - self.dashboard.output = None 463 - else: 464 - try: 465 - self.dashboard.output = open(arg, 'w') 466 - except: 467 - Dashboard.err('Cannot open "{}"'.format(arg)) 468 - # redisplay the dashboard in the new output 469 - self.dashboard.redisplay() 470 - 471 - class EnabledCommand(gdb.Command): 472 - """Enable or disable the dashboard [on|off]. 473 - The current status is printed if no argument is present.""" 474 - 475 - def __init__(self, dashboard): 476 - gdb.Command.__init__(self, 'dashboard -enabled', gdb.COMMAND_USER) 477 - self.dashboard = dashboard 478 - 479 - def invoke(self, arg, from_tty): 480 - arg = Dashboard.parse_arg(arg) 481 - if arg == '': 482 - status = 'enabled' if self.dashboard.enabled else 'disabled' 483 - print('The dashboard is {}'.format(status)) 484 - elif arg == 'on': 485 - self.dashboard.enabled = True 486 - self.dashboard.redisplay() 487 - elif arg == 'off': 488 - self.dashboard.enabled = False 489 - else: 490 - msg = 'Wrong argument "{}"; expecting "on" or "off"' 491 - Dashboard.err(msg.format(arg)) 492 - 493 - def complete(self, text, word): 494 - return Dashboard.complete(word, ['on', 'off']) 495 - 496 - class LayoutCommand(gdb.Command): 497 - """Set or show the dashboard layout. 498 - Accepts a space-separated list of directive. Each directive is in the form 499 - "[!]<module>". Modules in the list are placed in the dashboard in the same order 500 - as they appear and those prefixed by "!" are disabled by default. Omitted 501 - modules are hidden and placed at the bottom in alphabetical order. Without 502 - arguments the current layout is shown; enabled and disabled modules are properly 503 - marked.""" 504 - 505 - def __init__(self, dashboard): 506 - gdb.Command.__init__(self, 'dashboard -layout', gdb.COMMAND_USER) 507 - self.dashboard = dashboard 508 - 509 - def invoke(self, arg, from_tty): 510 - arg = Dashboard.parse_arg(arg) 511 - directives = str(arg).split() 512 - if directives: 513 - self.layout(directives) 514 - if from_tty and not self.dashboard.is_running(): 515 - self.show() 516 - else: 517 - self.show() 518 - 519 - def show(self): 520 - for module in self.dashboard.modules: 521 - style = R.style_high if module.enabled else R.style_low 522 - print(ansi(module.name, style)) 523 - 524 - def layout(self, directives): 525 - modules = self.dashboard.modules 526 - # reset visibility 527 - for module in modules: 528 - module.enabled = False 529 - # move and enable the selected modules on top 530 - last = 0 531 - n_enabled = 0 532 - for directive in directives: 533 - # parse next directive 534 - enabled = (directive[0] != '!') 535 - name = directive[not enabled:] 536 - try: 537 - # it may actually start from last, but in this way repeated 538 - # modules can be handler transparently and without error 539 - todo = enumerate(modules[last:], start=last) 540 - index = next(i for i, m in todo if name == m.name) 541 - modules[index].enabled = enabled 542 - modules.insert(last, modules.pop(index)) 543 - last += 1 544 - n_enabled += enabled 545 - except StopIteration: 546 - def find_module(x): 547 - return x.name == name 548 - first_part = modules[:last] 549 - if len(filter(find_module, first_part)) == 0: 550 - Dashboard.err('Cannot find module "{}"'.format(name)) 551 - else: 552 - Dashboard.err('Module "{}" already set'.format(name)) 553 - continue 554 - # redisplay the dashboard 555 - if n_enabled: 556 - self.dashboard.redisplay() 557 - 558 - def complete(self, text, word): 559 - all_modules = (m.name for m in self.dashboard.modules) 560 - return Dashboard.complete(word, all_modules) 561 - 562 - class StyleCommand(gdb.Command): 563 - """Access the stylable attributes. 564 - Without arguments print all the stylable attributes. Subcommands are used to set 565 - or print (when the value is omitted) individual attributes.""" 566 - 567 - def __init__(self, dashboard, prefix, obj, attributes): 568 - self.prefix = prefix + ' -style' 569 - gdb.Command.__init__(self, self.prefix, 570 - gdb.COMMAND_USER, gdb.COMPLETE_NONE, True) 571 - self.dashboard = dashboard 572 - self.obj = obj 573 - self.attributes = attributes 574 - self.add_styles() 575 - 576 - def add_styles(self): 577 - this = self 578 - for name, attribute in self.attributes.items(): 579 - # fetch fields 580 - attr_name = attribute.get('name', name) 581 - attr_type = attribute.get('type', str) 582 - attr_check = attribute.get('check', lambda _: True) 583 - attr_default = attribute['default'] 584 - # set the default value (coerced to the type) 585 - value = attr_type(attr_default) 586 - setattr(self.obj, attr_name, value) 587 - # create the command 588 - def invoke(self, arg, from_tty, name=name, attr_name=attr_name, 589 - attr_type=attr_type, attr_check=attr_check): 590 - new_value = Dashboard.parse_arg(arg) 591 - if new_value == '': 592 - # print the current value 593 - value = getattr(this.obj, attr_name) 594 - print('{} = {!r}'.format(name, value)) 595 - else: 596 - try: 597 - # convert and check the new value 598 - parsed = ast.literal_eval(new_value) 599 - value = attr_type(parsed) 600 - if not attr_check(value): 601 - msg = 'Invalid value "{}" for "{}"' 602 - raise Exception(msg.format(new_value, name)) 603 - except Exception as e: 604 - Dashboard.err(e) 605 - else: 606 - # set and redisplay 607 - setattr(this.obj, attr_name, value) 608 - this.dashboard.redisplay() 609 - prefix = self.prefix + ' ' + name 610 - doc = attribute.get('doc', 'This style is self-documenting') 611 - Dashboard.create_command(prefix, invoke, doc, False) 612 - 613 - def invoke(self, arg, from_tty): 614 - # print all the pairs 615 - for name, attribute in self.attributes.items(): 616 - attr_name = attribute.get('name', name) 617 - value = getattr(self.obj, attr_name) 618 - print('{} = {!r}'.format(name, value)) 619 - 620 - # Base module ------------------------------------------------------------------ 621 - 622 - # just a tag 623 - class Module(): 624 - pass 625 - 626 - # Default modules -------------------------------------------------------------- 627 - 628 - class Source(Dashboard.Module): 629 - """Show the program source code, if available.""" 630 - 631 - def __init__(self): 632 - self.file_name = None 633 - self.source_lines = [] 634 - 635 - def label(self): 636 - return 'Source' 637 - 638 - def lines(self): 639 - # try to fetch the current line (skip if no line information) 640 - sal = gdb.selected_frame().find_sal() 641 - current_line = sal.line 642 - if current_line == 0: 643 - return [] 644 - # reload the source file if changed 645 - file_name = sal.symtab.fullname() 646 - if file_name != self.file_name: 647 - self.file_name = file_name 648 - try: 649 - with open(self.file_name) as source: 650 - self.source_lines = source.readlines() 651 - except: 652 - msg = 'Cannot access "{}"'.format(self.file_name) 653 - return [ansi(msg, R.style_error)] 654 - # compute the line range 655 - start = max(current_line - 1 - self.context, 0) 656 - end = min(current_line - 1 + self.context, len(self.source_lines)) 657 - # return the source code listing 658 - out = [] 659 - number_format = '{{:>{}}}'.format(len(str(end))) 660 - for number, line in enumerate(self.source_lines[start:end], start + 1): 661 - if int(number) == current_line: 662 - line_format = ansi(number_format + ' {}', R.style_selected_1) 663 - else: 664 - line_format = ansi(number_format, R.style_low) + ' {}' 665 - out.append(line_format.format(number, line.rstrip('\n'))) 666 - return out 667 - 668 - def set_context(self, arg): 669 - msg = 'expecting a positive integer' 670 - self.context = parse_value(arg, int, check_ge_zero, msg) 671 - 672 - def attributes(self): 673 - return { 674 - 'context': { 675 - 'doc': 'Number of context lines.', 676 - 'default': 5, 677 - 'type': int, 678 - 'check': check_ge_zero 679 - } 680 - } 681 - 682 - class Assembly(Dashboard.Module): 683 - """Show the disassembled code surrounding the program counter. The 684 - instructions constituting the current statement are marked, if available.""" 685 - 686 - def label(self): 687 - return 'Assembly' 688 - 689 - def lines(self): 690 - line_info = None 691 - frame = gdb.selected_frame() # PC is here 692 - disassemble = frame.architecture().disassemble 693 - try: 694 - # try to fetch the function boundaries using the disassemble command 695 - output = run('disassemble').split('\n') 696 - start = int(re.split('[ :]', output[1][3:], 1)[0], 16) 697 - end = int(re.split('[ :]', output[-3][3:], 1)[0], 16) 698 - asm = disassemble(start, end_pc=end) 699 - # find the location of the PC 700 - pc_index = next(index for index, instr in enumerate(asm) 701 - if instr['addr'] == frame.pc()) 702 - start = max(pc_index - self.context, 0) 703 - end = pc_index + self.context + 1 704 - asm = asm[start:end] 705 - # if there are line information then use it, it may be that 706 - # line_info is not None but line_info.last is None 707 - line_info = gdb.find_pc_line(frame.pc()) 708 - line_info = line_info if line_info.last else None 709 - except gdb.error: 710 - # if it is not possible (stripped binary) start from PC and end 711 - # after twice the context 712 - asm = disassemble(frame.pc(), count=2 * self.context + 1) 713 - # fetch function start if available 714 - func_start = None 715 - if self.show_function and frame.name(): 716 - try: 717 - value = gdb.parse_and_eval(frame.name()).address 718 - func_start = to_unsigned(value) 719 - except gdb.error: 720 - pass # e.g., @plt 721 - # return the machine code 722 - max_length = max(instr['length'] for instr in asm) 723 - inferior = gdb.selected_inferior() 724 - out = [] 725 - for index, instr in enumerate(asm): 726 - addr = instr['addr'] 727 - length = instr['length'] 728 - text = instr['asm'] 729 - addr_str = format_address(addr) 730 - if self.show_opcodes: 731 - # fetch and format opcode 732 - region = inferior.read_memory(addr, length) 733 - opcodes = (' '.join('{:02x}'.format(ord(byte)) 734 - for byte in region)) 735 - opcodes += (max_length - len(region)) * 3 * ' ' + ' ' 736 - else: 737 - opcodes = '' 738 - # compute the offset if available 739 - if self.show_function: 740 - if func_start: 741 - max_offset = len(str(asm[-1]['addr'] - func_start)) 742 - offset = str(addr - func_start).ljust(max_offset) 743 - func_info = '{}+{} '.format(frame.name(), offset) 744 - else: 745 - func_info = '? ' 746 - else: 747 - func_info = '' 748 - format_string = '{} {}{}{}' 749 - if addr == frame.pc(): 750 - addr_str = ansi(addr_str, R.style_selected_1) 751 - opcodes = ansi(opcodes, R.style_selected_1) 752 - func_info = ansi(func_info, R.style_selected_1) 753 - text = ansi(text, R.style_selected_1) 754 - elif line_info and line_info.pc <= addr < line_info.last: 755 - addr_str = ansi(addr_str, R.style_selected_2) 756 - opcodes = ansi(opcodes, R.style_selected_2) 757 - func_info = ansi(func_info, R.style_selected_2) 758 - text = ansi(text, R.style_selected_2) 759 - else: 760 - addr_str = ansi(addr_str, R.style_low) 761 - func_info = ansi(func_info, R.style_low) 762 - out.append(format_string.format(addr_str, opcodes, func_info, text)) 763 - return out 764 - 765 - def attributes(self): 766 - return { 767 - 'context': { 768 - 'doc': 'Number of context instructions.', 769 - 'default': 3, 770 - 'type': int, 771 - 'check': check_ge_zero 772 - }, 773 - 'opcodes': { 774 - 'doc': 'Opcodes visibility flag.', 775 - 'default': False, 776 - 'name': 'show_opcodes', 777 - 'type': bool 778 - }, 779 - 'function': { 780 - 'doc': 'Function information visibility flag.', 781 - 'default': True, 782 - 'name': 'show_function', 783 - 'type': bool 784 - } 785 - } 786 - 787 - class Stack(Dashboard.Module): 788 - """Show the current stack trace including the function name and the file 789 - location, if available. Optionally list the frame arguments and locals too.""" 790 - 791 - def label(self): 792 - return 'Stack' 793 - 794 - def lines(self): 795 - frames = [] 796 - number = 0 797 - selected_index = 0 798 - frame = gdb.newest_frame() 799 - while frame: 800 - frame_lines = [] 801 - # fetch frame info 802 - selected = (frame == gdb.selected_frame()) 803 - if selected: 804 - selected_index = number 805 - style = R.style_selected_1 if selected else R.style_selected_2 806 - frame_id = ansi(str(number), style) 807 - info = Stack.get_pc_line(frame, style) 808 - frame_lines.append('[{}] {}'.format(frame_id, info)) 809 - # fetch frame arguments and locals 810 - decorator = gdb.FrameDecorator.FrameDecorator(frame) 811 - if self.show_arguments: 812 - frame_args = decorator.frame_args() 813 - args_lines = self.fetch_frame_info(frame, frame_args, 'arg') 814 - if args_lines: 815 - frame_lines.extend(args_lines) 816 - else: 817 - frame_lines.append(ansi('(no arguments)', R.style_low)) 818 - if self.show_locals: 819 - frame_locals = decorator.frame_locals() 820 - locals_lines = self.fetch_frame_info(frame, frame_locals, 'loc') 821 - if locals_lines: 822 - frame_lines.extend(locals_lines) 823 - else: 824 - frame_lines.append(ansi('(no locals)', R.style_low)) 825 - # add frame 826 - frames.append(frame_lines) 827 - # next 828 - frame = frame.older() 829 - number += 1 830 - # format the output 831 - if not self.limit or self.limit >= len(frames): 832 - start = 0 833 - end = len(frames) 834 - more = False 835 - else: 836 - start = selected_index 837 - end = min(len(frames), start + self.limit) 838 - more = (len(frames) - start > self.limit) 839 - lines = [] 840 - for frame_lines in frames[start:end]: 841 - lines.extend(frame_lines) 842 - # add the placeholder 843 - if more: 844 - lines.append('[{}]'.format(ansi('+', R.style_selected_2))) 845 - return lines 846 - 847 - def fetch_frame_info(self, frame, data, prefix): 848 - prefix = ansi(prefix, R.style_low) 849 - lines = [] 850 - for elem in data or []: 851 - name = elem.sym 852 - value = to_string(elem.sym.value(frame)) 853 - lines.append('{} {} = {}'.format(prefix, name, value)) 854 - return lines 855 - 856 - @staticmethod 857 - def get_pc_line(frame, style): 858 - frame_pc = ansi(format_address(frame.pc()), style) 859 - info = 'from {}'.format(frame_pc) 860 - if frame.name(): 861 - frame_name = ansi(frame.name(), style) 862 - try: 863 - # try to compute the offset relative to the current function 864 - value = gdb.parse_and_eval(frame.name()).address 865 - # it can be None even if it is part of the "stack" (C++) 866 - if value: 867 - func_start = to_unsigned(value) 868 - offset = frame.pc() - func_start 869 - frame_name += '+' + ansi(str(offset), style) 870 - except gdb.error: 871 - pass # e.g., @plt 872 - info += ' in {}'.format(frame_name) 873 - sal = frame.find_sal() 874 - if sal.symtab: 875 - file_name = ansi(sal.symtab.filename, style) 876 - file_line = ansi(str(sal.line), style) 877 - info += ' at {}:{}'.format(file_name, file_line) 878 - return info 879 - 880 - def attributes(self): 881 - return { 882 - 'limit': { 883 - 'doc': 'Maximum number of displayed frames (0 means no limit).', 884 - 'default': 2, 885 - 'type': int, 886 - 'check': check_ge_zero 887 - }, 888 - 'arguments': { 889 - 'doc': 'Frame arguments visibility flag.', 890 - 'default': True, 891 - 'name': 'show_arguments', 892 - 'type': bool 893 - }, 894 - 'locals': { 895 - 'doc': 'Frame locals visibility flag.', 896 - 'default': False, 897 - 'name': 'show_locals', 898 - 'type': bool 899 - } 900 - } 901 - 902 - class History(Dashboard.Module): 903 - """List the last entries of the value history.""" 904 - 905 - def label(self): 906 - return 'History' 907 - 908 - def lines(self): 909 - out = [] 910 - # fetch last entries 911 - for i in range(-self.limit + 1, 1): 912 - try: 913 - value = to_string(gdb.history(i)) 914 - value_id = ansi('$${}', R.style_low).format(abs(i)) 915 - line = '{} = {}'.format(value_id, value) 916 - out.append(line) 917 - except gdb.error: 918 - continue 919 - return out 920 - 921 - def attributes(self): 922 - return { 923 - 'limit': { 924 - 'doc': 'Maximum number of values to show.', 925 - 'default': 3, 926 - 'type': int, 927 - 'check': check_gt_zero 928 - } 929 - } 930 - 931 - class Memory(Dashboard.Module): 932 - """Allow to inspect memory regions.""" 933 - 934 - @staticmethod 935 - def format_byte(byte): 936 - # `type(byte) is bytes` in Python 3 937 - if byte.isspace(): 938 - return ' ' 939 - elif 0x20 < ord(byte) < 0x7e: 940 - return chr(ord(byte)) 941 - else: 942 - return '.' 943 - 944 - @staticmethod 945 - def parse_as_address(expression): 946 - value = gdb.parse_and_eval(expression) 947 - return to_unsigned(value) 948 - 949 - def __init__(self): 950 - self.row_length = 16 951 - self.table = {} 952 - 953 - def format_memory(self, start, memory): 954 - out = [] 955 - for i in range(0, len(memory), self.row_length): 956 - region = memory[i:i + self.row_length] 957 - pad = self.row_length - len(region) 958 - address = format_address(start + i) 959 - hexa = (' '.join('{:02x}'.format(ord(byte)) for byte in region)) 960 - text = (''.join(Memory.format_byte(byte) for byte in region)) 961 - out.append('{} {}{} {}{}'.format(ansi(address, R.style_low), 962 - hexa, 963 - ansi(pad * ' --', R.style_low), 964 - ansi(text, R.style_high), 965 - ansi(pad * '.', R.style_low))) 966 - return out 967 - 968 - def label(self): 969 - return 'Memory' 970 - 971 - def lines(self): 972 - out = [] 973 - inferior = gdb.selected_inferior() 974 - for address, length in sorted(self.table.items()): 975 - try: 976 - memory = inferior.read_memory(address, length) 977 - out.extend(self.format_memory(address, memory)) 978 - except gdb.error: 979 - msg = 'Cannot access {} bytes starting at {}' 980 - msg = msg.format(length, format_address(address)) 981 - out.append(ansi(msg, R.style_error)) 982 - out.append(divider()) 983 - # drop last divider 984 - if out: 985 - del out[-1] 986 - return out 987 - 988 - def watch(self, arg): 989 - if arg: 990 - address, _, length = arg.partition(' ') 991 - address = Memory.parse_as_address(address) 992 - if length: 993 - length = Memory.parse_as_address(length) 994 - else: 995 - length = self.row_length 996 - self.table[address] = length 997 - else: 998 - raise Exception('Specify an address') 999 - 1000 - def unwatch(self, arg): 1001 - if arg: 1002 - try: 1003 - del self.table[Memory.parse_as_address(arg)] 1004 - except KeyError: 1005 - raise Exception('Memory region not watched') 1006 - else: 1007 - raise Exception('Specify an address') 1008 - 1009 - def clear(self, arg): 1010 - self.table.clear() 1011 - 1012 - def commands(self): 1013 - return { 1014 - 'watch': { 1015 - 'action': self.watch, 1016 - 'doc': 'Watch a memory region by address and length.\n' 1017 - 'The length defaults to 16 byte.', 1018 - 'complete': gdb.COMPLETE_EXPRESSION 1019 - }, 1020 - 'unwatch': { 1021 - 'action': self.unwatch, 1022 - 'doc': 'Stop watching a memory region by address.', 1023 - 'complete': gdb.COMPLETE_EXPRESSION 1024 - }, 1025 - 'clear': { 1026 - 'action': self.clear, 1027 - 'doc': 'Clear all the watched regions.' 1028 - } 1029 - } 1030 - 1031 - class Registers(Dashboard.Module): 1032 - """Show the CPU registers and their values.""" 1033 - 1034 - def __init__(self): 1035 - self.table = {} 1036 - 1037 - def label(self): 1038 - return 'Registers' 1039 - 1040 - def lines(self): 1041 - # fetch registers status 1042 - registers = [] 1043 - for reg_info in run('info registers').strip().split('\n'): 1044 - # fetch register and update the table 1045 - name = reg_info.split(None, 1)[0] 1046 - value = gdb.parse_and_eval('${}'.format(name)) 1047 - string_value = self.format_value(value) 1048 - changed = self.table and (self.table.get(name, '') != string_value) 1049 - self.table[name] = string_value 1050 - registers.append((name, string_value, changed)) 1051 - # split registers in rows and columns, each column is composed of name, 1052 - # space, value and another trailing space which is skipped in the last 1053 - # column (hence term_width + 1) 1054 - max_name = max(len(name) for name, _, _ in registers) 1055 - max_value = max(len(value) for _, value, _ in registers) 1056 - max_width = max_name + max_value + 2 1057 - per_line = int((Dashboard.term_width + 1) / max_width) or 1 1058 - # redistribute extra space among columns 1059 - extra = int((Dashboard.term_width + 1 - 1060 - max_width * per_line) / per_line) 1061 - if per_line == 1: 1062 - # center when there is only one column 1063 - max_name += int(extra / 2) 1064 - max_value += int(extra / 2) 1065 - else: 1066 - max_value += extra 1067 - # format registers info 1068 - partial = [] 1069 - for name, value, changed in registers: 1070 - styled_name = ansi(name.rjust(max_name), R.style_low) 1071 - value_style = R.style_selected_1 if changed else '' 1072 - styled_value = ansi(value.ljust(max_value), value_style) 1073 - partial.append(styled_name + ' ' + styled_value) 1074 - out = [] 1075 - for i in range(0, len(partial), per_line): 1076 - out.append(' '.join(partial[i:i + per_line]).rstrip()) 1077 - return out 1078 - 1079 - def format_value(self, value): 1080 - try: 1081 - if value.type.code in [gdb.TYPE_CODE_INT, gdb.TYPE_CODE_PTR]: 1082 - int_value = to_unsigned(value, value.type.sizeof) 1083 - value_format = '0x{{:0{}x}}'.format(2 * value.type.sizeof) 1084 - return value_format.format(int_value) 1085 - except (gdb.error, ValueError): 1086 - # convert to unsigned but preserve code and flags information 1087 - pass 1088 - return str(value) 1089 - 1090 - class Threads(Dashboard.Module): 1091 - """List the currently available threads.""" 1092 - 1093 - def label(self): 1094 - return 'Threads' 1095 - 1096 - def lines(self): 1097 - out = [] 1098 - selected_thread = gdb.selected_thread() 1099 - selected_frame = gdb.selected_frame() 1100 - for thread in gdb.Inferior.threads(gdb.selected_inferior()): 1101 - is_selected = (thread.ptid == selected_thread.ptid) 1102 - style = R.style_selected_1 if is_selected else R.style_selected_2 1103 - number = ansi(str(thread.num), style) 1104 - tid = ansi(str(thread.ptid[1] or thread.ptid[2]), style) 1105 - info = '[{}] id {}'.format(number, tid) 1106 - if thread.name: 1107 - info += ' name {}'.format(ansi(thread.name, style)) 1108 - # switch thread to fetch frame info 1109 - thread.switch() 1110 - frame = gdb.newest_frame() 1111 - info += ' ' + Stack.get_pc_line(frame, style) 1112 - out.append(info) 1113 - # restore thread and frame 1114 - selected_thread.switch() 1115 - selected_frame.select() 1116 - return out 1117 - 1118 - class Expressions(Dashboard.Module): 1119 - """Watch user expressions.""" 1120 - 1121 - def __init__(self): 1122 - self.number = 1 1123 - self.table = {} 1124 - 1125 - def label(self): 1126 - return 'Expressions' 1127 - 1128 - def lines(self): 1129 - out = [] 1130 - for number, expression in sorted(self.table.items()): 1131 - try: 1132 - value = to_string(gdb.parse_and_eval(expression)) 1133 - except gdb.error as e: 1134 - value = ansi(e, R.style_error) 1135 - number = ansi(number, R.style_selected_2) 1136 - expression = ansi(expression, R.style_low) 1137 - out.append('[{}] {} = {}'.format(number, expression, value)) 1138 - return out 1139 - 1140 - def watch(self, arg): 1141 - if arg: 1142 - self.table[self.number] = arg 1143 - self.number += 1 1144 - else: 1145 - raise Exception('Specify an expression') 1146 - 1147 - def unwatch(self, arg): 1148 - if arg: 1149 - try: 1150 - del self.table[int(arg)] 1151 - except: 1152 - raise Exception('Expression not watched') 1153 - else: 1154 - raise Exception('Specify an identifier') 1155 - 1156 - def clear(self, arg): 1157 - self.table.clear() 1158 - 1159 - def commands(self): 1160 - return { 1161 - 'watch': { 1162 - 'action': self.watch, 1163 - 'doc': 'Watch an expression.', 1164 - 'complete': gdb.COMPLETE_EXPRESSION 1165 - }, 1166 - 'unwatch': { 1167 - 'action': self.unwatch, 1168 - 'doc': 'Stop watching an expression by id.', 1169 - 'complete': gdb.COMPLETE_EXPRESSION 1170 - }, 1171 - 'clear': { 1172 - 'action': self.clear, 1173 - 'doc': 'Clear all the watched expressions.' 1174 - } 1175 - } 1176 - 1177 - end 1178 - 1179 - # Better GDB defaults ---------------------------------------------------------- 1180 - 1181 - set history save 1182 - set confirm off 1183 - set verbose off 1184 - set print pretty on 1185 - set print array off 1186 - set print array-indexes on 1187 - set python print-stack full 1188 - 1189 - # Start ------------------------------------------------------------------------ 1190 - 1191 - python Dashboard.start() 1192 - 1193 - # ------------------------------------------------------------------------------ 1194 - # Copyright (c) 2015 Andrea Cardaci <cyrus.and@gmail.com> 1195 - # 1196 - # Permission is hereby granted, free of charge, to any person obtaining a copy 1197 - # of this software and associated documentation files (the "Software"), to deal 1198 - # in the Software without restriction, including without limitation the rights 1199 - # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 1200 - # copies of the Software, and to permit persons to whom the Software is 1201 - # furnished to do so, subject to the following conditions: 1202 - # 1203 - # The above copyright notice and this permission notice shall be included in all 1204 - # copies or substantial portions of the Software. 1205 - # 1206 - # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 1207 - # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 1208 - # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 1209 - # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 1210 - # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 1211 - # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 1212 - # SOFTWARE. 1213 - # ------------------------------------------------------------------------------ 1214 - # vi:syntax=python 1215 - # Local Variables: 1216 - # mode: python 1217 - # End:
+1
git/ignore
··· 54 54 .rake_tasks 55 55 .meteor/ 56 56 .tags 57 + .gdb_history 57 58 # }}} 58 59 59 60 # vim: foldmethod=marker foldlevel=0 foldenable