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