I was told to fix a bug in a legacy application.
I can reproduce a bug, but I have no clue at which python source code line the error does get executed.
I can see the relevant failure with strace
: A file gets opened, which should not get opened.
I would like to make the relevant open() linux-syscall raise an Exception in the python interpreter. My goal: I want to see the stacktrace to be able to fix the bug.
This way I could avoid the time consuming stepping through a lot of lines with a debugger.
The same with other words: if the syscall gets executed, which results in the strace output of open("/somefile", O_RDONLY) = 4
the python interpreter should exit with an traceback.
Has anybody a solution?
Please leave a comment if you don't understand what I am looking for.
We can do a patch on open
before import modules, here is an example:
in test.py
:
def func():
with open('test', 'w') as f:
pass
in test2.py
:
try:
import __builtin__ # for python2
except ImportError:
import builtins as __builtin__ #for python3
import copy
import traceback
orig_open = copy.copy(__builtin__.open)
def myopen(*args):
traceback.print_stack()
return orig_open(*args)
__builtin__.open = myopen
from test import func # Note that we import the module after patching on open()
func()
and when func()
is called in test2.py
, call stack will be printed:
$ python test2.py
File "test2.py", line 19, in <module>
func()
File "/tmp/test.py", line 4, in func
with open('test', 'w') as f:
File "test2.py", line 12, in myopen
traceback.print_stack()
You can run python under gdb, set a (conditional) breakpoint on the open()
syscall (or, rather, the stub function in libc through which it is called), and, when the breakpoint is hit, send a SIGINT
signal to the python process and let it continue, whereupon the execution of the python script should be interrupted with desired stack trace.
The shell script below automates that procedure.
Usage:
stack_trace_on_open
filename
-- python
script.py
[
script args
]
stack_trace_on_open:
#!/usr/bin/env bash
myname="$(basename "$0")"
if [[ $# -lt 4 || "$2" != '--' ]]
then
echo >&2 "Usage: $myname <filename> -- python <script.py> [script args ...]"
exit 1
fi
fname=$1
python_exe="$3"
shift 3
gdb -q "$python_exe" <<END
set breakpoint pending on
break open
condition 1 strcmp(\$rdi,"$fname") == 0
run "$@"
signal 2
cont
quit
END
Demonstration:
$ cat test.py
import ctypes
clib = ctypes.CDLL(None)
fd = clib.open("/dev/urandom", 0)
clib.close(fd)
$ ./stack_trace_on_open /dev/urandom -- python test.py
Reading symbols from python...(no debugging symbols found)...done.
(gdb) (gdb) Function "open" not defined.
Breakpoint 1 (open) pending.
(gdb) (gdb) Starting program: /usr/bin/python "test.py"
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 1, open64 () at ../sysdeps/unix/syscall-template.S:84
84 ../sysdeps/unix/syscall-template.S: No such file or directory.
(gdb) Continuing with signal SIGINT.
Breakpoint 1, open64 () at ../sysdeps/unix/syscall-template.S:84
84 in ../sysdeps/unix/syscall-template.S
(gdb) Continuing.
Traceback (most recent call last): # <--------
File "test.py", line 4, in <module> # <--------
fd = clib.open("/dev/urandom", 0) # <--------
KeyboardInterrupt
[Inferior 1 (process 14248) exited with code 01]
(gdb)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With