Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Debugging: stepping through Python script using gdb?

Let's say we have the following mega-simple Python script:

print "Initializing".... a=10 print "Variable value is %d" % (a) print "All done!" 

... and say, I'd like to debug this script by placing a breakpoint at line a=10, and then stepping through the script.

Now, I'd like to use gdb for this, because I'd like to debug Python bindings that may come as a part of a shared object (.so) library - hence, I'd ideally place a breakpoint on a Python code line, and then "step into" the C part of the shared object... (Note that DebuggingWithGdb - PythonInfo Wiki doesn't really explicitly state that this is possible)

The problem is: gdb on its own cannot really recognize breakpoints, placed on a Python script line:

$ gdb python GNU gdb (GDB) 7.3.50.20110806-cvs  ... Reading symbols from /usr/bin/python...(no debugging symbols found)...done. (gdb) b test.py:3 No symbol table is loaded.  Use the "file" command. Make breakpoint pending on future shared library load? (y or [n]) y  Breakpoint 1 (test.py:3) pending. (gdb) run test.py Starting program: /usr/bin/python test.py ... 

... and while the entire Python script does run within gdb, the breakpoint is simply never reached.

So - is what I want to do, at all possible with gdb; and if not, what other alternatives would I have for something similar?

like image 453
sdaau Avatar asked Sep 14 '11 07:09

sdaau


People also ask

How do you debug a Python script step by step?

Starting Python Debugger To start debugging within the program just insert import pdb, pdb. set_trace() commands. Run your script normally and execution will stop where we have introduced a breakpoint. So basically we are hard coding a breakpoint on a line below where we call set_trace().

Is GDB written in Python?

Additionally, GDB commands and convenience functions which are written in Python and are located in the data-directory /python/gdb/command or data-directory /python/gdb/function directories are automatically imported when GDB starts.


1 Answers

Very interesting question. Here's my approach. Create signal_test.py:

import os import signal  PID = os.getpid()  def do_nothing(*args):     pass  def foo():     print "Initializing..."     a=10     os.kill(PID, signal.SIGUSR1)     print "Variable value is %d" % (a)     print "All done!"  signal.signal(signal.SIGUSR1, do_nothing)  foo() 

Then you can run it under gdb:

$ gdb --args python signal_test.py GNU gdb (GDB) Red Hat Enterprise Linux (7.0.1-37.el5_7.1) Copyright (C) 2009 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.  Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-redhat-linux-gnu". For bug reporting instructions, please see: <http://www.gnu.org/software/gdb/bugs/>... Reading symbols from /net/gs/vol3/software/modules-sw/python/2.7/Linux/RHEL5/x86_64/bin/python...done. 

And when you run it, it will go until you reach the call to kill():

(gdb) run Starting program: /net/gs/vol3/software/modules-sw/python/2.7/Linux/RHEL5/x86_64/bin/python signal_test.py warning: no loadable sections found in added symbol-file system-supplied DSO at 0x2aaaaaaab000 [Thread debugging using libthread_db enabled] Initializing...  Program received signal SIGUSR1, User defined signal 1. 0x0000003d340306f7 in kill () from /lib64/libc.so.6 

You can then look at a backtrace:

(gdb) backtrace #0  0x0000003d340306f7 in kill () from /lib64/libc.so.6 #1  0x00000000004d82dd in posix_kill (self=<value optimized out>, args=<value optimized out>)     at ./Modules/posixmodule.c:4047 #2  0x000000000049b574 in call_function (f=0x8aca30, throwflag=<value optimized out>)     at Python/ceval.c:4012 #3  PyEval_EvalFrameEx (f=0x8aca30, throwflag=<value optimized out>) at Python/ceval.c:2665 #4  0x000000000049c5cd in call_function (f=0x8ac560, throwflag=<value optimized out>)     at Python/ceval.c:4098 #5  PyEval_EvalFrameEx (f=0x8ac560, throwflag=<value optimized out>) at Python/ceval.c:2665 #6  0x000000000049d3bb in PyEval_EvalCodeEx (co=0x2aaaae224f30, globals=<value optimized out>,      locals=<value optimized out>, args=0x0, argcount=0, kws=0x0, kwcount=0, defs=0x0, defcount=0,      closure=0x0) at Python/ceval.c:3252 #7  0x000000000049d432 in PyEval_EvalCode (co=0x1a48, globals=0xa, locals=0x0) at Python/ceval.c:666 #8  0x00000000004bf321 in run_mod (fp=0x89ad60, filename=0x7fffffffb5b4 "signal_test.py",      start=<value optimized out>, globals=0x7e4680, locals=0x7e4680, closeit=1, flags=0x7fffffffaee0)     at Python/pythonrun.c:1346 #9  PyRun_FileExFlags (fp=0x89ad60, filename=0x7fffffffb5b4 "signal_test.py",      start=<value optimized out>, globals=0x7e4680, locals=0x7e4680, closeit=1, flags=0x7fffffffaee0)     at Python/pythonrun.c:1332 #10 0x00000000004bf5d8 in PyRun_SimpleFileExFlags (fp=<value optimized out>,      filename=0x7fffffffb5b4 "signal_test.py", closeit=1, flags=0x7fffffffaee0)     at Python/pythonrun.c:936 #11 0x00000000004148cc in Py_Main (argc=<value optimized out>, argv=<value optimized out>)     at Modules/main.c:599 #12 0x0000003d3401d994 in __libc_start_main () from /lib64/libc.so.6 #13 0x0000000000413b19 in _start () 

If you continue on, the rest of the program will run normally.

(gdb) continue Continuing. Variable value is 10 All done!  Program exited normally. 

You can, instead, step through in the appropriate frame until you reach the statement you're interested in. You're probably going to want to run a debugging Python for this to make much sense.

like image 96
Michael Hoffman Avatar answered Oct 06 '22 15:10

Michael Hoffman