Python Memory Debugging with Gdb

Python memory debugging with GDB

Yes, you can do this kind of thing:

(gdb) print PyRun_SimpleString("import traceback; traceback.print_stack()")
File "<string>", line 1, in <module>
File "/var/tmp/foo.py", line 2, in <module>
i**2
File "<string>", line 1, in <module>
$1 = 0

It should also be possible to use the pystack command defined in the python gdbinit file, but it's not working for me. It's discussed here if you want to look into it.

Also, if you suspect memory issues, it's worth noting that you can use valgrind with python, if you're prepared to recompile it. The procedure is described here.

How to debug Python memory fault?

You can try Low-level Python debugging with GDB. Probably there is a bug in Python interpreter or in lxml library and it is hard to find it without extra tools.

You can interrupt your script running under gdb when CPU usage goes to 100% and look at stack trace. It will probably help to understand what's going on inside script.

gdb python module read memory content

I guess inferior is what you want:

(gdb) python i = gdb.inferiors()[0]
(gdb) python m = i.read_memory(0x7fffffffe39c, 4) # an int32
(gdb) python print(m.tobytes())
b'\x01\x00\x00\x00'

Memory dump formatted like xxd from gdb

(gdb) define xxd
>dump binary memory dump.bin $arg0 $arg0+$arg1
>shell xxd dump.bin
>end
(gdb) xxd &j 10
0000000: 0000 0000 0000 0000 0000 0000 4d8c a7f7 ............M...
0000010: ff7f 0000 0000 0000 0000 0000 c8d7 ffff ................
0000020: ff7f 0000 0000 0000

Seems easy enough ;-)

You could likely write a Python script (modern GDB versions have embedded Python interpreter) to do the same, and get rid of the need to "shell out".

Update:

Here is a possible Python implementation (save this into xxd.py):

class XXD(gdb.Command):
def __init__(self):
super(XXD, self).__init__("xxd", gdb.COMMAND_USER)

def _PrintLine(self, offset, bytes, size):
print('{:08x}: '.format(offset), end='')
todo = size
while todo >= 4:
print(''.join('{:02x}'.format(b) for b in bytes[0:4]), end='')
todo -= 4
bytes = bytes[3:]
if todo:
print(' ', end='')

# Print any remaining bytes
print(''.join('{:02x}'.format(b) for b in bytes[0:todo]), end='')

print()
return size

def invoke(self, arg, from_tty):
args = arg.split()
if len(args) != 2:
print("xxd: <addr> <count>")
return
size = int(args[1])
addr = gdb.parse_and_eval(args[0])
inferior = gdb.inferiors()[0]
bytes = inferior.read_memory(addr, size).tobytes()

offset = int(addr)
while size > 0:
n = self._PrintLine(offset, bytes, min(len(bytes), 16))
size -= n
offset += n
bytes = bytes[n:]

XXD()

Use it like so:

// Sample program x.c
char foo[] = "abcdefghijklmopqrstuvwxyz";
int main() { return 0; }

gcc -g x.c
gdb -q ./a.out

(gdb) source xxd.py
Temporary breakpoint 1, main () at x.c:3
3 int main() { return 0; }

(gdb) xxd &foo[0] 18
00404030: 61626364 64656667 6768696a 6a6b6c6d
00404040: 7273

gdb dump memory & errors?

While @scott is correct, the answer here was that I didn't account for a snapshot of the memory at the time of the process running.

I had to implement a loop to perform the a comparative analysis of the current memory allocated to the process id found in /proc//mem.

Here is a gist of the total solution.



Related Topics



Leave a reply



Submit