15f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#!/usr/bin/env python 25f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 35f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)""" 45f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)The Cython debugger 55f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 65f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)The current directory should contain a directory named 'cython_debug', or a 75f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)path to the cython project directory should be given (the parent directory of 85f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)cython_debug). 95f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)Additional gdb args can be provided only if a path to the project directory is 115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)given. 125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)""" 135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import os 155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import sys 165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import glob 175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import tempfile 185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import textwrap 195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import subprocess 205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import optparse 215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)import logging 225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)logger = logging.getLogger(__name__) 245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)def make_command_file(path_to_debug_info, prefix_code='', no_import=False): 265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if not no_import: 275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) pattern = os.path.join(path_to_debug_info, 285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 'cython_debug', 295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 'cython_debug_info_*') 305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) debug_files = glob.glob(pattern) 315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if not debug_files: 335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) sys.exit('%s.\nNo debug files were found in %s. Aborting.' % ( 345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) usage, os.path.abspath(path_to_debug_info))) 355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) fd, tempfilename = tempfile.mkstemp() 375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) f = os.fdopen(fd, 'w') 385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) try: 395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) f.write(prefix_code) 405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) f.write('set breakpoint pending on\n') 415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) f.write("set print pretty on\n") 425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) f.write('python from Cython.Debugger import libcython, libpython\n') 435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if no_import: 455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # don't do this, this overrides file command in .gdbinit 465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) # f.write("file %s\n" % sys.executable) 475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) pass 485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) else: 495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) path = os.path.join(path_to_debug_info, "cython_debug", "interpreter") 505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) interpreter_file = open(path) 515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) try: 525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) interpreter = interpreter_file.read() 535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) finally: 545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) interpreter_file.close() 555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) f.write("file %s\n" % interpreter) 565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) f.write('\n'.join('cy import %s\n' % fn for fn in debug_files)) 575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) f.write(textwrap.dedent('''\ 585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) python 595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) import sys 605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) try: 615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) gdb.lookup_type('PyModuleObject') 625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) except RuntimeError: 635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) sys.stderr.write( 645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 'Python was not compiled with debug symbols (or it was ' 655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 'stripped). Some functionality may not work (properly).\\n') 665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) end 675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) source .cygdbinit 695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ''')) 705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) finally: 715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) f.close() 725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) return tempfilename 745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)usage = "Usage: cygdb [options] [PATH [-- GDB_ARGUMENTS]]" 765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)def main(path_to_debug_info=None, gdb_argv=None, no_import=False): 785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """ 795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) Start the Cython debugger. This tells gdb to import the Cython and Python 805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) extensions (libcython.py and libpython.py) and it enables gdb's pending 815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) breakpoints. 825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) path_to_debug_info is the path to the Cython build directory 845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) gdb_argv is the list of options to gdb 855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) no_import tells cygdb whether it should import debug information 865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) """ 875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) parser = optparse.OptionParser(usage=usage) 885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) parser.add_option("--gdb-executable", 895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) dest="gdb", default='gdb', 905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) help="gdb executable to use [default: gdb]") 915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) parser.add_option("--verbose", "-v", 925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) dest="verbosity", action="count", default=0, 935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) help="Verbose mode. Multiple -v options increase the verbosity") 945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) (options, args) = parser.parse_args() 965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if path_to_debug_info is None: 975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if len(args) > 1: 985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) path_to_debug_info = args[0] 995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) else: 1005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) path_to_debug_info = os.curdir 1015f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1025f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if gdb_argv is None: 1035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) gdb_argv = args[1:] 1045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if path_to_debug_info == '--': 1065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) no_import = True 1075f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1085f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) logging_level = logging.WARN 1095f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if options.verbosity == 1: 1105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) logging_level = logging.INFO 1115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) if options.verbosity == 2: 1125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) logging_level = logging.DEBUG 1135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) logging.basicConfig(level=logging_level) 1145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) logger.info("verbosity = %r", options.verbosity) 1165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) logger.debug("options = %r; args = %r", options, args) 1175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) logger.debug("Done parsing command-line options. path_to_debug_info = %r, gdb_argv = %r", 1185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) path_to_debug_info, gdb_argv) 1195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) 1205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) tempfilename = make_command_file(path_to_debug_info, no_import=no_import) 1215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) logger.info("Launching %s with command file: %s and gdb_argv: %s", 1225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) options.gdb, tempfilename, gdb_argv) 1235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) logger.debug('Command file (%s) contains: """\n%s"""', tempfilename, open(tempfilename).read()) 1245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) logger.info("Spawning %s...", options.gdb) 1255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) p = subprocess.Popen([options.gdb, '-command', tempfilename] + gdb_argv) 1265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) logger.info("Spawned %s (pid %d)", options.gdb, p.pid) 1275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) while True: 1285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) try: 1295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) logger.debug("Waiting for gdb (pid %d) to exit...", p.pid) 1305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) ret = p.wait() 1315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) logger.debug("Wait for gdb (pid %d) to exit is done. Returned: %r", p.pid, ret) 1325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) except KeyboardInterrupt: 1335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) pass 1345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) else: 1355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) break 1365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) logger.debug("Removing temp command file: %s", tempfilename) 1375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) os.remove(tempfilename) 1385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles) logger.debug("Removed temp command file: %s", tempfilename) 139