1""" 2GDB extension that adds Cython support. 3""" 4 5from __future__ import print_function 6 7try: 8 input = raw_input 9except NameError: 10 pass 11 12import sys 13import textwrap 14import traceback 15import functools 16import itertools 17import collections 18 19import gdb 20 21try: # python 2 22 UNICODE = unicode 23 BYTES = str 24except NameError: # python 3 25 UNICODE = str 26 BYTES = bytes 27 28try: 29 from lxml import etree 30 have_lxml = True 31except ImportError: 32 have_lxml = False 33 try: 34 # Python 2.5 35 from xml.etree import cElementTree as etree 36 except ImportError: 37 try: 38 # Python 2.5 39 from xml.etree import ElementTree as etree 40 except ImportError: 41 try: 42 # normal cElementTree install 43 import cElementTree as etree 44 except ImportError: 45 # normal ElementTree install 46 import elementtree.ElementTree as etree 47 48try: 49 import pygments.lexers 50 import pygments.formatters 51except ImportError: 52 pygments = None 53 sys.stderr.write("Install pygments for colorized source code.\n") 54 55if hasattr(gdb, 'string_to_argv'): 56 from gdb import string_to_argv 57else: 58 from shlex import split as string_to_argv 59 60from Cython.Debugger import libpython 61 62# C or Python type 63CObject = 'CObject' 64PythonObject = 'PythonObject' 65 66_data_types = dict(CObject=CObject, PythonObject=PythonObject) 67_filesystemencoding = sys.getfilesystemencoding() or 'UTF-8' 68 69 70# decorators 71 72def dont_suppress_errors(function): 73 "*sigh*, readline" 74 @functools.wraps(function) 75 def wrapper(*args, **kwargs): 76 try: 77 return function(*args, **kwargs) 78 except Exception: 79 traceback.print_exc() 80 raise 81 82 return wrapper 83 84 85def default_selected_gdb_frame(err=True): 86 def decorator(function): 87 @functools.wraps(function) 88 def wrapper(self, frame=None, *args, **kwargs): 89 try: 90 frame = frame or gdb.selected_frame() 91 except RuntimeError: 92 raise gdb.GdbError("No frame is currently selected.") 93 94 if err and frame.name() is None: 95 raise NoFunctionNameInFrameError() 96 97 return function(self, frame, *args, **kwargs) 98 return wrapper 99 return decorator 100 101 102def require_cython_frame(function): 103 @functools.wraps(function) 104 @require_running_program 105 def wrapper(self, *args, **kwargs): 106 frame = kwargs.get('frame') or gdb.selected_frame() 107 if not self.is_cython_function(frame): 108 raise gdb.GdbError('Selected frame does not correspond with a ' 109 'Cython function we know about.') 110 return function(self, *args, **kwargs) 111 return wrapper 112 113 114def dispatch_on_frame(c_command, python_command=None): 115 def decorator(function): 116 @functools.wraps(function) 117 def wrapper(self, *args, **kwargs): 118 is_cy = self.is_cython_function() 119 is_py = self.is_python_function() 120 121 if is_cy or (is_py and not python_command): 122 function(self, *args, **kwargs) 123 elif is_py: 124 gdb.execute(python_command) 125 elif self.is_relevant_function(): 126 gdb.execute(c_command) 127 else: 128 raise gdb.GdbError("Not a function cygdb knows about. " 129 "Use the normal GDB commands instead.") 130 131 return wrapper 132 return decorator 133 134 135def require_running_program(function): 136 @functools.wraps(function) 137 def wrapper(*args, **kwargs): 138 try: 139 gdb.selected_frame() 140 except RuntimeError: 141 raise gdb.GdbError("No frame is currently selected.") 142 143 return function(*args, **kwargs) 144 return wrapper 145 146 147def gdb_function_value_to_unicode(function): 148 @functools.wraps(function) 149 def wrapper(self, string, *args, **kwargs): 150 if isinstance(string, gdb.Value): 151 string = string.string() 152 153 return function(self, string, *args, **kwargs) 154 return wrapper 155 156 157# Classes that represent the debug information 158# Don't rename the parameters of these classes, they come directly from the XML 159 160class CythonModule(object): 161 def __init__(self, module_name, filename, c_filename): 162 self.name = module_name 163 self.filename = filename 164 self.c_filename = c_filename 165 self.globals = {} 166 # {cython_lineno: min(c_linenos)} 167 self.lineno_cy2c = {} 168 # {c_lineno: cython_lineno} 169 self.lineno_c2cy = {} 170 self.functions = {} 171 172 173class CythonVariable(object): 174 175 def __init__(self, name, cname, qualified_name, type, lineno): 176 self.name = name 177 self.cname = cname 178 self.qualified_name = qualified_name 179 self.type = type 180 self.lineno = int(lineno) 181 182 183class CythonFunction(CythonVariable): 184 def __init__(self, 185 module, 186 name, 187 cname, 188 pf_cname, 189 qualified_name, 190 lineno, 191 type=CObject, 192 is_initmodule_function="False"): 193 super(CythonFunction, self).__init__(name, 194 cname, 195 qualified_name, 196 type, 197 lineno) 198 self.module = module 199 self.pf_cname = pf_cname 200 self.is_initmodule_function = is_initmodule_function == "True" 201 self.locals = {} 202 self.arguments = [] 203 self.step_into_functions = set() 204 205 206# General purpose classes 207 208class CythonBase(object): 209 210 @default_selected_gdb_frame(err=False) 211 def is_cython_function(self, frame): 212 return frame.name() in self.cy.functions_by_cname 213 214 @default_selected_gdb_frame(err=False) 215 def is_python_function(self, frame): 216 """ 217 Tells if a frame is associated with a Python function. 218 If we can't read the Python frame information, don't regard it as such. 219 """ 220 if frame.name() == 'PyEval_EvalFrameEx': 221 pyframe = libpython.Frame(frame).get_pyop() 222 return pyframe and not pyframe.is_optimized_out() 223 return False 224 225 @default_selected_gdb_frame() 226 def get_c_function_name(self, frame): 227 return frame.name() 228 229 @default_selected_gdb_frame() 230 def get_c_lineno(self, frame): 231 return frame.find_sal().line 232 233 @default_selected_gdb_frame() 234 def get_cython_function(self, frame): 235 result = self.cy.functions_by_cname.get(frame.name()) 236 if result is None: 237 raise NoCythonFunctionInFrameError() 238 239 return result 240 241 @default_selected_gdb_frame() 242 def get_cython_lineno(self, frame): 243 """ 244 Get the current Cython line number. Returns 0 if there is no 245 correspondence between the C and Cython code. 246 """ 247 cyfunc = self.get_cython_function(frame) 248 return cyfunc.module.lineno_c2cy.get(self.get_c_lineno(frame), 0) 249 250 @default_selected_gdb_frame() 251 def get_source_desc(self, frame): 252 filename = lineno = lexer = None 253 if self.is_cython_function(frame): 254 filename = self.get_cython_function(frame).module.filename 255 lineno = self.get_cython_lineno(frame) 256 if pygments: 257 lexer = pygments.lexers.CythonLexer(stripall=False) 258 elif self.is_python_function(frame): 259 pyframeobject = libpython.Frame(frame).get_pyop() 260 261 if not pyframeobject: 262 raise gdb.GdbError( 263 'Unable to read information on python frame') 264 265 filename = pyframeobject.filename() 266 lineno = pyframeobject.current_line_num() 267 268 if pygments: 269 lexer = pygments.lexers.PythonLexer(stripall=False) 270 else: 271 symbol_and_line_obj = frame.find_sal() 272 if not symbol_and_line_obj or not symbol_and_line_obj.symtab: 273 filename = None 274 lineno = 0 275 else: 276 filename = symbol_and_line_obj.symtab.fullname() 277 lineno = symbol_and_line_obj.line 278 if pygments: 279 lexer = pygments.lexers.CLexer(stripall=False) 280 281 return SourceFileDescriptor(filename, lexer), lineno 282 283 @default_selected_gdb_frame() 284 def get_source_line(self, frame): 285 source_desc, lineno = self.get_source_desc() 286 return source_desc.get_source(lineno) 287 288 @default_selected_gdb_frame() 289 def is_relevant_function(self, frame): 290 """ 291 returns whether we care about a frame on the user-level when debugging 292 Cython code 293 """ 294 name = frame.name() 295 older_frame = frame.older() 296 if self.is_cython_function(frame) or self.is_python_function(frame): 297 return True 298 elif older_frame and self.is_cython_function(older_frame): 299 # check for direct C function call from a Cython function 300 cython_func = self.get_cython_function(older_frame) 301 return name in cython_func.step_into_functions 302 303 return False 304 305 @default_selected_gdb_frame(err=False) 306 def print_stackframe(self, frame, index, is_c=False): 307 """ 308 Print a C, Cython or Python stack frame and the line of source code 309 if available. 310 """ 311 # do this to prevent the require_cython_frame decorator from 312 # raising GdbError when calling self.cy.cy_cvalue.invoke() 313 selected_frame = gdb.selected_frame() 314 frame.select() 315 316 try: 317 source_desc, lineno = self.get_source_desc(frame) 318 except NoFunctionNameInFrameError: 319 print('#%-2d Unknown Frame (compile with -g)' % index) 320 return 321 322 if not is_c and self.is_python_function(frame): 323 pyframe = libpython.Frame(frame).get_pyop() 324 if pyframe is None or pyframe.is_optimized_out(): 325 # print this python function as a C function 326 return self.print_stackframe(frame, index, is_c=True) 327 328 func_name = pyframe.co_name 329 func_cname = 'PyEval_EvalFrameEx' 330 func_args = [] 331 elif self.is_cython_function(frame): 332 cyfunc = self.get_cython_function(frame) 333 f = lambda arg: self.cy.cy_cvalue.invoke(arg, frame=frame) 334 335 func_name = cyfunc.name 336 func_cname = cyfunc.cname 337 func_args = [] # [(arg, f(arg)) for arg in cyfunc.arguments] 338 else: 339 source_desc, lineno = self.get_source_desc(frame) 340 func_name = frame.name() 341 func_cname = func_name 342 func_args = [] 343 344 try: 345 gdb_value = gdb.parse_and_eval(func_cname) 346 except RuntimeError: 347 func_address = 0 348 else: 349 func_address = gdb_value.address 350 if not isinstance(func_address, int): 351 # Seriously? Why is the address not an int? 352 if not isinstance(func_address, (str, bytes)): 353 func_address = str(func_address) 354 func_address = int(func_address.split()[0], 0) 355 356 a = ', '.join('%s=%s' % (name, val) for name, val in func_args) 357 sys.stdout.write('#%-2d 0x%016x in %s(%s)' % (index, func_address, func_name, a)) 358 359 if source_desc.filename is not None: 360 sys.stdout.write(' at %s:%s' % (source_desc.filename, lineno)) 361 362 sys.stdout.write('\n') 363 364 try: 365 sys.stdout.write(' ' + source_desc.get_source(lineno)) 366 except gdb.GdbError: 367 pass 368 369 selected_frame.select() 370 371 def get_remote_cython_globals_dict(self): 372 m = gdb.parse_and_eval('__pyx_m') 373 374 try: 375 PyModuleObject = gdb.lookup_type('PyModuleObject') 376 except RuntimeError: 377 raise gdb.GdbError(textwrap.dedent("""\ 378 Unable to lookup type PyModuleObject, did you compile python 379 with debugging support (-g)?""")) 380 381 m = m.cast(PyModuleObject.pointer()) 382 return m['md_dict'] 383 384 385 def get_cython_globals_dict(self): 386 """ 387 Get the Cython globals dict where the remote names are turned into 388 local strings. 389 """ 390 remote_dict = self.get_remote_cython_globals_dict() 391 pyobject_dict = libpython.PyObjectPtr.from_pyobject_ptr(remote_dict) 392 393 result = {} 394 seen = set() 395 for k, v in pyobject_dict.items(): 396 result[k.proxyval(seen)] = v 397 398 return result 399 400 def print_gdb_value(self, name, value, max_name_length=None, prefix=''): 401 if libpython.pretty_printer_lookup(value): 402 typename = '' 403 else: 404 typename = '(%s) ' % (value.type,) 405 406 if max_name_length is None: 407 print('%s%s = %s%s' % (prefix, name, typename, value)) 408 else: 409 print('%s%-*s = %s%s' % (prefix, max_name_length, name, typename, value)) 410 411 def is_initialized(self, cython_func, local_name): 412 cyvar = cython_func.locals[local_name] 413 cur_lineno = self.get_cython_lineno() 414 415 if '->' in cyvar.cname: 416 # Closed over free variable 417 if cur_lineno > cython_func.lineno: 418 if cyvar.type == PythonObject: 419 return int(gdb.parse_and_eval(cyvar.cname)) 420 return True 421 return False 422 423 return cur_lineno > cyvar.lineno 424 425 426class SourceFileDescriptor(object): 427 def __init__(self, filename, lexer, formatter=None): 428 self.filename = filename 429 self.lexer = lexer 430 self.formatter = formatter 431 432 def valid(self): 433 return self.filename is not None 434 435 def lex(self, code): 436 if pygments and self.lexer and parameters.colorize_code: 437 bg = parameters.terminal_background.value 438 if self.formatter is None: 439 formatter = pygments.formatters.TerminalFormatter(bg=bg) 440 else: 441 formatter = self.formatter 442 443 return pygments.highlight(code, self.lexer, formatter) 444 445 return code 446 447 def _get_source(self, start, stop, lex_source, mark_line, lex_entire): 448 with open(self.filename) as f: 449 # to provide "correct" colouring, the entire code needs to be 450 # lexed. However, this makes a lot of things terribly slow, so 451 # we decide not to. Besides, it's unlikely to matter. 452 453 if lex_source and lex_entire: 454 f = self.lex(f.read()).splitlines() 455 456 slice = itertools.islice(f, start - 1, stop - 1) 457 458 for idx, line in enumerate(slice): 459 if start + idx == mark_line: 460 prefix = '>' 461 else: 462 prefix = ' ' 463 464 if lex_source and not lex_entire: 465 line = self.lex(line) 466 467 yield '%s %4d %s' % (prefix, start + idx, line.rstrip()) 468 469 def get_source(self, start, stop=None, lex_source=True, mark_line=0, 470 lex_entire=False): 471 exc = gdb.GdbError('Unable to retrieve source code') 472 473 if not self.filename: 474 raise exc 475 476 start = max(start, 1) 477 if stop is None: 478 stop = start + 1 479 480 try: 481 return '\n'.join( 482 self._get_source(start, stop, lex_source, mark_line, lex_entire)) 483 except IOError: 484 raise exc 485 486 487# Errors 488 489class CyGDBError(gdb.GdbError): 490 """ 491 Base class for Cython-command related errors 492 """ 493 494 def __init__(self, *args): 495 args = args or (self.msg,) 496 super(CyGDBError, self).__init__(*args) 497 498 499class NoCythonFunctionInFrameError(CyGDBError): 500 """ 501 raised when the user requests the current cython function, which is 502 unavailable 503 """ 504 msg = "Current function is a function cygdb doesn't know about" 505 506 507class NoFunctionNameInFrameError(NoCythonFunctionInFrameError): 508 """ 509 raised when the name of the C function could not be determined 510 in the current C stack frame 511 """ 512 msg = ('C function name could not be determined in the current C stack ' 513 'frame') 514 515 516# Parameters 517 518class CythonParameter(gdb.Parameter): 519 """ 520 Base class for cython parameters 521 """ 522 523 def __init__(self, name, command_class, parameter_class, default=None): 524 self.show_doc = self.set_doc = self.__class__.__doc__ 525 super(CythonParameter, self).__init__(name, command_class, 526 parameter_class) 527 if default is not None: 528 self.value = default 529 530 def __bool__(self): 531 return bool(self.value) 532 533 __nonzero__ = __bool__ # Python 2 534 535 536 537class CompleteUnqualifiedFunctionNames(CythonParameter): 538 """ 539 Have 'cy break' complete unqualified function or method names. 540 """ 541 542 543class ColorizeSourceCode(CythonParameter): 544 """ 545 Tell cygdb whether to colorize source code. 546 """ 547 548 549class TerminalBackground(CythonParameter): 550 """ 551 Tell cygdb about the user's terminal background (light or dark). 552 """ 553 554 555class CythonParameters(object): 556 """ 557 Simple container class that might get more functionality in the distant 558 future (mostly to remind us that we're dealing with parameters). 559 """ 560 561 def __init__(self): 562 self.complete_unqualified = CompleteUnqualifiedFunctionNames( 563 'cy_complete_unqualified', 564 gdb.COMMAND_BREAKPOINTS, 565 gdb.PARAM_BOOLEAN, 566 True) 567 self.colorize_code = ColorizeSourceCode( 568 'cy_colorize_code', 569 gdb.COMMAND_FILES, 570 gdb.PARAM_BOOLEAN, 571 True) 572 self.terminal_background = TerminalBackground( 573 'cy_terminal_background_color', 574 gdb.COMMAND_FILES, 575 gdb.PARAM_STRING, 576 "dark") 577 578parameters = CythonParameters() 579 580 581# Commands 582 583class CythonCommand(gdb.Command, CythonBase): 584 """ 585 Base class for Cython commands 586 """ 587 588 command_class = gdb.COMMAND_NONE 589 590 @classmethod 591 def _register(cls, clsname, args, kwargs): 592 if not hasattr(cls, 'completer_class'): 593 return cls(clsname, cls.command_class, *args, **kwargs) 594 else: 595 return cls(clsname, cls.command_class, cls.completer_class, 596 *args, **kwargs) 597 598 @classmethod 599 def register(cls, *args, **kwargs): 600 alias = getattr(cls, 'alias', None) 601 if alias: 602 cls._register(cls.alias, args, kwargs) 603 604 return cls._register(cls.name, args, kwargs) 605 606 607class CyCy(CythonCommand): 608 """ 609 Invoke a Cython command. Available commands are: 610 611 cy import 612 cy break 613 cy step 614 cy next 615 cy run 616 cy cont 617 cy finish 618 cy up 619 cy down 620 cy select 621 cy bt / cy backtrace 622 cy list 623 cy print 624 cy set 625 cy locals 626 cy globals 627 cy exec 628 """ 629 630 name = 'cy' 631 command_class = gdb.COMMAND_NONE 632 completer_class = gdb.COMPLETE_COMMAND 633 634 def __init__(self, name, command_class, completer_class): 635 # keep the signature 2.5 compatible (i.e. do not use f(*a, k=v) 636 super(CythonCommand, self).__init__(name, command_class, 637 completer_class, prefix=True) 638 639 commands = dict( 640 # GDB commands 641 import_ = CyImport.register(), 642 break_ = CyBreak.register(), 643 step = CyStep.register(), 644 next = CyNext.register(), 645 run = CyRun.register(), 646 cont = CyCont.register(), 647 finish = CyFinish.register(), 648 up = CyUp.register(), 649 down = CyDown.register(), 650 select = CySelect.register(), 651 bt = CyBacktrace.register(), 652 list = CyList.register(), 653 print_ = CyPrint.register(), 654 locals = CyLocals.register(), 655 globals = CyGlobals.register(), 656 exec_ = libpython.FixGdbCommand('cy exec', '-cy-exec'), 657 _exec = CyExec.register(), 658 set = CySet.register(), 659 660 # GDB functions 661 cy_cname = CyCName('cy_cname'), 662 cy_cvalue = CyCValue('cy_cvalue'), 663 cy_lineno = CyLine('cy_lineno'), 664 cy_eval = CyEval('cy_eval'), 665 ) 666 667 for command_name, command in commands.items(): 668 command.cy = self 669 setattr(self, command_name, command) 670 671 self.cy = self 672 673 # Cython module namespace 674 self.cython_namespace = {} 675 676 # maps (unique) qualified function names (e.g. 677 # cythonmodule.ClassName.method_name) to the CythonFunction object 678 self.functions_by_qualified_name = {} 679 680 # unique cnames of Cython functions 681 self.functions_by_cname = {} 682 683 # map function names like method_name to a list of all such 684 # CythonFunction objects 685 self.functions_by_name = collections.defaultdict(list) 686 687 688class CyImport(CythonCommand): 689 """ 690 Import debug information outputted by the Cython compiler 691 Example: cy import FILE... 692 """ 693 694 name = 'cy import' 695 command_class = gdb.COMMAND_STATUS 696 completer_class = gdb.COMPLETE_FILENAME 697 698 def invoke(self, args, from_tty): 699 if isinstance(args, BYTES): 700 args = args.decode(_filesystemencoding) 701 for arg in string_to_argv(args): 702 try: 703 f = open(arg) 704 except OSError as e: 705 raise gdb.GdbError('Unable to open file %r: %s' % (args, e.args[1])) 706 707 t = etree.parse(f) 708 709 for module in t.getroot(): 710 cython_module = CythonModule(**module.attrib) 711 self.cy.cython_namespace[cython_module.name] = cython_module 712 713 for variable in module.find('Globals'): 714 d = variable.attrib 715 cython_module.globals[d['name']] = CythonVariable(**d) 716 717 for function in module.find('Functions'): 718 cython_function = CythonFunction(module=cython_module, 719 **function.attrib) 720 721 # update the global function mappings 722 name = cython_function.name 723 qname = cython_function.qualified_name 724 725 self.cy.functions_by_name[name].append(cython_function) 726 self.cy.functions_by_qualified_name[ 727 cython_function.qualified_name] = cython_function 728 self.cy.functions_by_cname[ 729 cython_function.cname] = cython_function 730 731 d = cython_module.functions[qname] = cython_function 732 733 for local in function.find('Locals'): 734 d = local.attrib 735 cython_function.locals[d['name']] = CythonVariable(**d) 736 737 for step_into_func in function.find('StepIntoFunctions'): 738 d = step_into_func.attrib 739 cython_function.step_into_functions.add(d['name']) 740 741 cython_function.arguments.extend( 742 funcarg.tag for funcarg in function.find('Arguments')) 743 744 for marker in module.find('LineNumberMapping'): 745 cython_lineno = int(marker.attrib['cython_lineno']) 746 c_linenos = list(map(int, marker.attrib['c_linenos'].split())) 747 cython_module.lineno_cy2c[cython_lineno] = min(c_linenos) 748 for c_lineno in c_linenos: 749 cython_module.lineno_c2cy[c_lineno] = cython_lineno 750 751 752class CyBreak(CythonCommand): 753 """ 754 Set a breakpoint for Cython code using Cython qualified name notation, e.g.: 755 756 cy break cython_modulename.ClassName.method_name... 757 758 or normal notation: 759 760 cy break function_or_method_name... 761 762 or for a line number: 763 764 cy break cython_module:lineno... 765 766 Set a Python breakpoint: 767 Break on any function or method named 'func' in module 'modname' 768 769 cy break -p modname.func... 770 771 Break on any function or method named 'func' 772 773 cy break -p func... 774 """ 775 776 name = 'cy break' 777 command_class = gdb.COMMAND_BREAKPOINTS 778 779 def _break_pyx(self, name): 780 modulename, _, lineno = name.partition(':') 781 lineno = int(lineno) 782 if modulename: 783 cython_module = self.cy.cython_namespace[modulename] 784 else: 785 cython_module = self.get_cython_function().module 786 787 if lineno in cython_module.lineno_cy2c: 788 c_lineno = cython_module.lineno_cy2c[lineno] 789 breakpoint = '%s:%s' % (cython_module.c_filename, c_lineno) 790 gdb.execute('break ' + breakpoint) 791 else: 792 raise gdb.GdbError("Not a valid line number. " 793 "Does it contain actual code?") 794 795 def _break_funcname(self, funcname): 796 func = self.cy.functions_by_qualified_name.get(funcname) 797 798 if func and func.is_initmodule_function: 799 func = None 800 801 break_funcs = [func] 802 803 if not func: 804 funcs = self.cy.functions_by_name.get(funcname) or [] 805 funcs = [f for f in funcs if not f.is_initmodule_function] 806 807 if not funcs: 808 gdb.execute('break ' + funcname) 809 return 810 811 if len(funcs) > 1: 812 # multiple functions, let the user pick one 813 print('There are multiple such functions:') 814 for idx, func in enumerate(funcs): 815 print('%3d) %s' % (idx, func.qualified_name)) 816 817 while True: 818 try: 819 result = input( 820 "Select a function, press 'a' for all " 821 "functions or press 'q' or '^D' to quit: ") 822 except EOFError: 823 return 824 else: 825 if result.lower() == 'q': 826 return 827 elif result.lower() == 'a': 828 break_funcs = funcs 829 break 830 elif (result.isdigit() and 831 0 <= int(result) < len(funcs)): 832 break_funcs = [funcs[int(result)]] 833 break 834 else: 835 print('Not understood...') 836 else: 837 break_funcs = [funcs[0]] 838 839 for func in break_funcs: 840 gdb.execute('break %s' % func.cname) 841 if func.pf_cname: 842 gdb.execute('break %s' % func.pf_cname) 843 844 def invoke(self, function_names, from_tty): 845 if isinstance(function_names, BYTES): 846 function_names = function_names.decode(_filesystemencoding) 847 argv = string_to_argv(function_names) 848 if function_names.startswith('-p'): 849 argv = argv[1:] 850 python_breakpoints = True 851 else: 852 python_breakpoints = False 853 854 for funcname in argv: 855 if python_breakpoints: 856 gdb.execute('py-break %s' % funcname) 857 elif ':' in funcname: 858 self._break_pyx(funcname) 859 else: 860 self._break_funcname(funcname) 861 862 @dont_suppress_errors 863 def complete(self, text, word): 864 # Filter init-module functions (breakpoints can be set using 865 # modulename:linenumber). 866 names = [n for n, L in self.cy.functions_by_name.items() 867 if any(not f.is_initmodule_function for f in L)] 868 qnames = [n for n, f in self.cy.functions_by_qualified_name.items() 869 if not f.is_initmodule_function] 870 871 if parameters.complete_unqualified: 872 all_names = itertools.chain(qnames, names) 873 else: 874 all_names = qnames 875 876 words = text.strip().split() 877 if not words or '.' not in words[-1]: 878 # complete unqualified 879 seen = set(text[:-len(word)].split()) 880 return [n for n in all_names 881 if n.startswith(word) and n not in seen] 882 883 # complete qualified name 884 lastword = words[-1] 885 compl = [n for n in qnames if n.startswith(lastword)] 886 887 if len(lastword) > len(word): 888 # readline sees something (e.g. a '.') as a word boundary, so don't 889 # "recomplete" this prefix 890 strip_prefix_length = len(lastword) - len(word) 891 compl = [n[strip_prefix_length:] for n in compl] 892 893 return compl 894 895 896class CythonInfo(CythonBase, libpython.PythonInfo): 897 """ 898 Implementation of the interface dictated by libpython.LanguageInfo. 899 """ 900 901 def lineno(self, frame): 902 # Take care of the Python and Cython levels. We need to care for both 903 # as we can't simply dispatch to 'py-step', since that would work for 904 # stepping through Python code, but it would not step back into Cython- 905 # related code. The C level should be dispatched to the 'step' command. 906 if self.is_cython_function(frame): 907 return self.get_cython_lineno(frame) 908 return super(CythonInfo, self).lineno(frame) 909 910 def get_source_line(self, frame): 911 try: 912 line = super(CythonInfo, self).get_source_line(frame) 913 except gdb.GdbError: 914 return None 915 else: 916 return line.strip() or None 917 918 def exc_info(self, frame): 919 if self.is_python_function: 920 return super(CythonInfo, self).exc_info(frame) 921 922 def runtime_break_functions(self): 923 if self.is_cython_function(): 924 return self.get_cython_function().step_into_functions 925 return () 926 927 def static_break_functions(self): 928 result = ['PyEval_EvalFrameEx'] 929 result.extend(self.cy.functions_by_cname) 930 return result 931 932 933class CythonExecutionControlCommand(CythonCommand, 934 libpython.ExecutionControlCommandBase): 935 936 @classmethod 937 def register(cls): 938 return cls(cls.name, cython_info) 939 940 941class CyStep(CythonExecutionControlCommand, libpython.PythonStepperMixin): 942 "Step through Cython, Python or C code." 943 944 name = 'cy -step' 945 stepinto = True 946 947 def invoke(self, args, from_tty): 948 if self.is_python_function(): 949 self.python_step(self.stepinto) 950 elif not self.is_cython_function(): 951 if self.stepinto: 952 command = 'step' 953 else: 954 command = 'next' 955 956 self.finish_executing(gdb.execute(command, to_string=True)) 957 else: 958 self.step(stepinto=self.stepinto) 959 960 961class CyNext(CyStep): 962 "Step-over Cython, Python or C code." 963 964 name = 'cy -next' 965 stepinto = False 966 967 968class CyRun(CythonExecutionControlCommand): 969 """ 970 Run a Cython program. This is like the 'run' command, except that it 971 displays Cython or Python source lines as well 972 """ 973 974 name = 'cy run' 975 976 invoke = CythonExecutionControlCommand.run 977 978 979class CyCont(CythonExecutionControlCommand): 980 """ 981 Continue a Cython program. This is like the 'run' command, except that it 982 displays Cython or Python source lines as well. 983 """ 984 985 name = 'cy cont' 986 invoke = CythonExecutionControlCommand.cont 987 988 989class CyFinish(CythonExecutionControlCommand): 990 """ 991 Execute until the function returns. 992 """ 993 name = 'cy finish' 994 995 invoke = CythonExecutionControlCommand.finish 996 997 998class CyUp(CythonCommand): 999 """ 1000 Go up a Cython, Python or relevant C frame. 1001 """ 1002 name = 'cy up' 1003 _command = 'up' 1004 1005 def invoke(self, *args): 1006 try: 1007 gdb.execute(self._command, to_string=True) 1008 while not self.is_relevant_function(gdb.selected_frame()): 1009 gdb.execute(self._command, to_string=True) 1010 except RuntimeError as e: 1011 raise gdb.GdbError(*e.args) 1012 1013 frame = gdb.selected_frame() 1014 index = 0 1015 while frame: 1016 frame = frame.older() 1017 index += 1 1018 1019 self.print_stackframe(index=index - 1) 1020 1021 1022class CyDown(CyUp): 1023 """ 1024 Go down a Cython, Python or relevant C frame. 1025 """ 1026 1027 name = 'cy down' 1028 _command = 'down' 1029 1030 1031class CySelect(CythonCommand): 1032 """ 1033 Select a frame. Use frame numbers as listed in `cy backtrace`. 1034 This command is useful because `cy backtrace` prints a reversed backtrace. 1035 """ 1036 1037 name = 'cy select' 1038 1039 def invoke(self, stackno, from_tty): 1040 try: 1041 stackno = int(stackno) 1042 except ValueError: 1043 raise gdb.GdbError("Not a valid number: %r" % (stackno,)) 1044 1045 frame = gdb.selected_frame() 1046 while frame.newer(): 1047 frame = frame.newer() 1048 1049 stackdepth = libpython.stackdepth(frame) 1050 1051 try: 1052 gdb.execute('select %d' % (stackdepth - stackno - 1,)) 1053 except RuntimeError as e: 1054 raise gdb.GdbError(*e.args) 1055 1056 1057class CyBacktrace(CythonCommand): 1058 'Print the Cython stack' 1059 1060 name = 'cy bt' 1061 alias = 'cy backtrace' 1062 command_class = gdb.COMMAND_STACK 1063 completer_class = gdb.COMPLETE_NONE 1064 1065 @require_running_program 1066 def invoke(self, args, from_tty): 1067 # get the first frame 1068 frame = gdb.selected_frame() 1069 while frame.older(): 1070 frame = frame.older() 1071 1072 print_all = args == '-a' 1073 1074 index = 0 1075 while frame: 1076 try: 1077 is_relevant = self.is_relevant_function(frame) 1078 except CyGDBError: 1079 is_relevant = False 1080 1081 if print_all or is_relevant: 1082 self.print_stackframe(frame, index) 1083 1084 index += 1 1085 frame = frame.newer() 1086 1087 1088class CyList(CythonCommand): 1089 """ 1090 List Cython source code. To disable to customize colouring see the cy_* 1091 parameters. 1092 """ 1093 1094 name = 'cy list' 1095 command_class = gdb.COMMAND_FILES 1096 completer_class = gdb.COMPLETE_NONE 1097 1098 # @dispatch_on_frame(c_command='list') 1099 def invoke(self, _, from_tty): 1100 sd, lineno = self.get_source_desc() 1101 source = sd.get_source(lineno - 5, lineno + 5, mark_line=lineno, 1102 lex_entire=True) 1103 print(source) 1104 1105 1106class CyPrint(CythonCommand): 1107 """ 1108 Print a Cython variable using 'cy-print x' or 'cy-print module.function.x' 1109 """ 1110 1111 name = 'cy print' 1112 command_class = gdb.COMMAND_DATA 1113 1114 def invoke(self, name, from_tty, max_name_length=None): 1115 if self.is_python_function(): 1116 return gdb.execute('py-print ' + name) 1117 elif self.is_cython_function(): 1118 value = self.cy.cy_cvalue.invoke(name.lstrip('*')) 1119 for c in name: 1120 if c == '*': 1121 value = value.dereference() 1122 else: 1123 break 1124 1125 self.print_gdb_value(name, value, max_name_length) 1126 else: 1127 gdb.execute('print ' + name) 1128 1129 def complete(self): 1130 if self.is_cython_function(): 1131 f = self.get_cython_function() 1132 return list(itertools.chain(f.locals, f.globals)) 1133 else: 1134 return [] 1135 1136 1137sortkey = lambda item: item[0].lower() 1138 1139 1140class CyLocals(CythonCommand): 1141 """ 1142 List the locals from the current Cython frame. 1143 """ 1144 1145 name = 'cy locals' 1146 command_class = gdb.COMMAND_STACK 1147 completer_class = gdb.COMPLETE_NONE 1148 1149 @dispatch_on_frame(c_command='info locals', python_command='py-locals') 1150 def invoke(self, args, from_tty): 1151 cython_function = self.get_cython_function() 1152 1153 if cython_function.is_initmodule_function: 1154 self.cy.globals.invoke(args, from_tty) 1155 return 1156 1157 local_cython_vars = cython_function.locals 1158 max_name_length = len(max(local_cython_vars, key=len)) 1159 for name, cyvar in sorted(local_cython_vars.items(), key=sortkey): 1160 if self.is_initialized(self.get_cython_function(), cyvar.name): 1161 value = gdb.parse_and_eval(cyvar.cname) 1162 if not value.is_optimized_out: 1163 self.print_gdb_value(cyvar.name, value, 1164 max_name_length, '') 1165 1166 1167class CyGlobals(CyLocals): 1168 """ 1169 List the globals from the current Cython module. 1170 """ 1171 1172 name = 'cy globals' 1173 command_class = gdb.COMMAND_STACK 1174 completer_class = gdb.COMPLETE_NONE 1175 1176 @dispatch_on_frame(c_command='info variables', python_command='py-globals') 1177 def invoke(self, args, from_tty): 1178 global_python_dict = self.get_cython_globals_dict() 1179 module_globals = self.get_cython_function().module.globals 1180 1181 max_globals_len = 0 1182 max_globals_dict_len = 0 1183 if module_globals: 1184 max_globals_len = len(max(module_globals, key=len)) 1185 if global_python_dict: 1186 max_globals_dict_len = len(max(global_python_dict)) 1187 1188 max_name_length = max(max_globals_len, max_globals_dict_len) 1189 1190 seen = set() 1191 print('Python globals:') 1192 for k, v in sorted(global_python_dict.items(), key=sortkey): 1193 v = v.get_truncated_repr(libpython.MAX_OUTPUT_LEN) 1194 seen.add(k) 1195 print(' %-*s = %s' % (max_name_length, k, v)) 1196 1197 print('C globals:') 1198 for name, cyvar in sorted(module_globals.items(), key=sortkey): 1199 if name not in seen: 1200 try: 1201 value = gdb.parse_and_eval(cyvar.cname) 1202 except RuntimeError: 1203 pass 1204 else: 1205 if not value.is_optimized_out: 1206 self.print_gdb_value(cyvar.name, value, 1207 max_name_length, ' ') 1208 1209 1210class EvaluateOrExecuteCodeMixin(object): 1211 """ 1212 Evaluate or execute Python code in a Cython or Python frame. The 'evalcode' 1213 method evaluations Python code, prints a traceback if an exception went 1214 uncaught, and returns any return value as a gdb.Value (NULL on exception). 1215 """ 1216 1217 def _fill_locals_dict(self, executor, local_dict_pointer): 1218 "Fill a remotely allocated dict with values from the Cython C stack" 1219 cython_func = self.get_cython_function() 1220 1221 for name, cyvar in cython_func.locals.items(): 1222 if cyvar.type == PythonObject and self.is_initialized(cython_func, name): 1223 try: 1224 val = gdb.parse_and_eval(cyvar.cname) 1225 except RuntimeError: 1226 continue 1227 else: 1228 if val.is_optimized_out: 1229 continue 1230 1231 pystringp = executor.alloc_pystring(name) 1232 code = ''' 1233 (PyObject *) PyDict_SetItem( 1234 (PyObject *) %d, 1235 (PyObject *) %d, 1236 (PyObject *) %s) 1237 ''' % (local_dict_pointer, pystringp, cyvar.cname) 1238 1239 try: 1240 if gdb.parse_and_eval(code) < 0: 1241 gdb.parse_and_eval('PyErr_Print()') 1242 raise gdb.GdbError("Unable to execute Python code.") 1243 finally: 1244 # PyDict_SetItem doesn't steal our reference 1245 executor.xdecref(pystringp) 1246 1247 def _find_first_cython_or_python_frame(self): 1248 frame = gdb.selected_frame() 1249 while frame: 1250 if (self.is_cython_function(frame) or 1251 self.is_python_function(frame)): 1252 frame.select() 1253 return frame 1254 1255 frame = frame.older() 1256 1257 raise gdb.GdbError("There is no Cython or Python frame on the stack.") 1258 1259 def _evalcode_cython(self, executor, code, input_type): 1260 with libpython.FetchAndRestoreError(): 1261 # get the dict of Cython globals and construct a dict in the 1262 # inferior with Cython locals 1263 global_dict = gdb.parse_and_eval( 1264 '(PyObject *) PyModule_GetDict(__pyx_m)') 1265 local_dict = gdb.parse_and_eval('(PyObject *) PyDict_New()') 1266 1267 try: 1268 self._fill_locals_dict(executor, 1269 libpython.pointervalue(local_dict)) 1270 result = executor.evalcode(code, input_type, global_dict, 1271 local_dict) 1272 finally: 1273 executor.xdecref(libpython.pointervalue(local_dict)) 1274 1275 return result 1276 1277 def evalcode(self, code, input_type): 1278 """ 1279 Evaluate `code` in a Python or Cython stack frame using the given 1280 `input_type`. 1281 """ 1282 frame = self._find_first_cython_or_python_frame() 1283 executor = libpython.PythonCodeExecutor() 1284 if self.is_python_function(frame): 1285 return libpython._evalcode_python(executor, code, input_type) 1286 return self._evalcode_cython(executor, code, input_type) 1287 1288 1289class CyExec(CythonCommand, libpython.PyExec, EvaluateOrExecuteCodeMixin): 1290 """ 1291 Execute Python code in the nearest Python or Cython frame. 1292 """ 1293 1294 name = '-cy-exec' 1295 command_class = gdb.COMMAND_STACK 1296 completer_class = gdb.COMPLETE_NONE 1297 1298 def invoke(self, expr, from_tty): 1299 expr, input_type = self.readcode(expr) 1300 executor = libpython.PythonCodeExecutor() 1301 executor.xdecref(self.evalcode(expr, executor.Py_single_input)) 1302 1303 1304class CySet(CythonCommand): 1305 """ 1306 Set a Cython variable to a certain value 1307 1308 cy set my_cython_c_variable = 10 1309 cy set my_cython_py_variable = $cy_eval("{'doner': 'kebab'}") 1310 1311 This is equivalent to 1312 1313 set $cy_value("my_cython_variable") = 10 1314 """ 1315 1316 name = 'cy set' 1317 command_class = gdb.COMMAND_DATA 1318 completer_class = gdb.COMPLETE_NONE 1319 1320 @require_cython_frame 1321 def invoke(self, expr, from_tty): 1322 name_and_expr = expr.split('=', 1) 1323 if len(name_and_expr) != 2: 1324 raise gdb.GdbError("Invalid expression. Use 'cy set var = expr'.") 1325 1326 varname, expr = name_and_expr 1327 cname = self.cy.cy_cname.invoke(varname.strip()) 1328 gdb.execute("set %s = %s" % (cname, expr)) 1329 1330 1331# Functions 1332 1333class CyCName(gdb.Function, CythonBase): 1334 """ 1335 Get the C name of a Cython variable in the current context. 1336 Examples: 1337 1338 print $cy_cname("function") 1339 print $cy_cname("Class.method") 1340 print $cy_cname("module.function") 1341 """ 1342 1343 @require_cython_frame 1344 @gdb_function_value_to_unicode 1345 def invoke(self, cyname, frame=None): 1346 frame = frame or gdb.selected_frame() 1347 cname = None 1348 1349 if self.is_cython_function(frame): 1350 cython_function = self.get_cython_function(frame) 1351 if cyname in cython_function.locals: 1352 cname = cython_function.locals[cyname].cname 1353 elif cyname in cython_function.module.globals: 1354 cname = cython_function.module.globals[cyname].cname 1355 else: 1356 qname = '%s.%s' % (cython_function.module.name, cyname) 1357 if qname in cython_function.module.functions: 1358 cname = cython_function.module.functions[qname].cname 1359 1360 if not cname: 1361 cname = self.cy.functions_by_qualified_name.get(cyname) 1362 1363 if not cname: 1364 raise gdb.GdbError('No such Cython variable: %s' % cyname) 1365 1366 return cname 1367 1368 1369class CyCValue(CyCName): 1370 """ 1371 Get the value of a Cython variable. 1372 """ 1373 1374 @require_cython_frame 1375 @gdb_function_value_to_unicode 1376 def invoke(self, cyname, frame=None): 1377 globals_dict = self.get_cython_globals_dict() 1378 cython_function = self.get_cython_function(frame) 1379 1380 if self.is_initialized(cython_function, cyname): 1381 cname = super(CyCValue, self).invoke(cyname, frame=frame) 1382 return gdb.parse_and_eval(cname) 1383 elif cyname in globals_dict: 1384 return globals_dict[cyname]._gdbval 1385 else: 1386 raise gdb.GdbError("Variable %s is not initialized." % cyname) 1387 1388 1389class CyLine(gdb.Function, CythonBase): 1390 """ 1391 Get the current Cython line. 1392 """ 1393 1394 @require_cython_frame 1395 def invoke(self): 1396 return self.get_cython_lineno() 1397 1398 1399class CyEval(gdb.Function, CythonBase, EvaluateOrExecuteCodeMixin): 1400 """ 1401 Evaluate Python code in the nearest Python or Cython frame and return 1402 """ 1403 1404 @gdb_function_value_to_unicode 1405 def invoke(self, python_expression): 1406 input_type = libpython.PythonCodeExecutor.Py_eval_input 1407 return self.evalcode(python_expression, input_type) 1408 1409 1410cython_info = CythonInfo() 1411cy = CyCy.register() 1412cython_info.cy = cy 1413 1414 1415def register_defines(): 1416 libpython.source_gdb_script(textwrap.dedent("""\ 1417 define cy step 1418 cy -step 1419 end 1420 1421 define cy next 1422 cy -next 1423 end 1424 1425 document cy step 1426 %s 1427 end 1428 1429 document cy next 1430 %s 1431 end 1432 """) % (CyStep.__doc__, CyNext.__doc__)) 1433 1434register_defines() 1435