I would like to be able to set a breakpoint in GDB, and have it run to that point - and in the process, print out lines it has "stepped through".
Here is an example, based on this simple file with a main
and a function, and two breakpoints for each:
$ cat > test.c <<EOF #include "stdio.h" int count=0; void doFunction(void) { // two steps forward count += 2; // one step back count--; } int main(void) { // some pointless init commands; count = 1; count += 2; count = 0; //main loop while(1) { doFunction(); printf("%d\n", count); } } EOF $ gcc -g -Wall test.c -o test.exe $ chmod +x test.exe $ gdb -se test.exe ... Reading symbols from /path/to/test.exe...done. (gdb) b main Breakpoint 1 at 0x80483ec: file test.c, line 14. (gdb) b doFunction Breakpoint 2 at 0x80483c7: file test.c, line 7.
To start the session, I need to run (r
) the program, which will then stop at first breakpoint (main
):
(gdb) r Starting program: /path/to/test.exe Breakpoint 1, main () at test.c:14 14 count = 1; (gdb)
At this point - I can, for instance, hit continue (c
); and the process will run through, not outputing anything, and break at the requested line:
(gdb) c Continuing. Breakpoint 2, doFunction () at test.c:7 7 count += 2; (gdb)
On the other hand, instead of continue - I can go line by line, either by using step (s
) or next (n
); for instance:
14 count = 1; (gdb) n 15 count += 2; (gdb) s 16 count = 0; (gdb) s 19 doFunction(); (gdb) s Breakpoint 2, doFunction () at test.c:7 7 count += 2; (gdb) s 9 count--; (gdb) s 10 } (gdb) s main () at test.c:20 20 printf("%d\n", count); (gdb) s ... (gdb) s _IO_vfprintf_internal (s=Cannot access memory at address 0xe5853361 ) at vfprintf.c:210 210 vfprintf.c: No such file or directory. in vfprintf.c (gdb) s 245 in vfprintf.c (gdb) s 210 in vfprintf.c (gdb) n 245 in vfprintf.c ... (gdb) n 2006 in vfprintf.c (gdb) n __printf (format=0x80484f0 "%d\n") at printf.c:39 39 printf.c: No such file or directory. in printf.c (gdb) n main () at test.c:21 21 } (gdb) n 19 doFunction(); (gdb) n Breakpoint 2, doFunction () at test.c:7 7 count += 2; (gdb)
Anyways, I am aware that I can keep Enter pressed, and the last entered command (step or next) will repeat (left a bit longer session in the second case, to show that 'next' remains on same level, 'step' steps inside the functions being called). However, as it can be seen, depending on whether step or next runs, it may take a while until a result is reached - and so, I don't want to sit for 10 minutes with my hand stuck on the Enter button :)
So, my question is - can I somehow instruct gdb
to run to 'breakpoint 2' without further user intervention - while printing out the lines it goes through, as if step (or next) was pressed?
Setting breakpoints A breakpoint is like a stop sign in your code -- whenever gdb gets to a breakpoint it halts execution of your program and allows you to examine it. To set breakpoints, type "break [filename]:[linenumber]". For example, if you wanted to set a breakpoint at line 55 of main.
The rbreak command can be used to set breakpoints in all the functions in a program, like this: (gdb) rbreak . Print a table of all breakpoints, watchpoints, and catchpoints set and not deleted, with the following columns for each breakpoint: Breakpoint Numbers.
This command continues the debuggee past the current line until the specified source line in the current stack frame. Use this command to avoid single stepping through a loop more than once, or to skip over the execution of uninteresting code.
If GDB creates a software watchpoint, it can only watch the value of an expression in a single thread. If you are confident that the expression can only change due to the current thread's activity (and if you are also confident that no other thread can become current), then you can use software watchpoints as usual.
Well, this wasn't easy - but I think I somewhat got it :) I went through a bunch of failed attempts (posted here); relevant code is below.
Basically, the problem in a "next/step until breakpoint" is how to determine whether you're "on" a breakpoint or not, if the debugger is stopped (at a step). Note also I use GDB 7.2-1ubuntu11 (current for Ubuntu 11.04). So, it went like this:
gdb.execute("CMDSTR", toString=True)
command - which is seemingly exactly what is needed to capture the output: "By default, any output produced by command is sent to gdb's standard output. If the to_string parameter is True, then output will be collected by gdb.execute and returned as a string[1]"! gdb.execute
in the recommended manner; failed here - because of this: subprocess.Popen
the GDB program, while replacing its stdin and stdout; and then proceed controlling GDB from there (pygdb-sub.py) - that failed too... (apparently, because I didn't redirect stdin/out right)source
) which would internally fork into a pty whenever gdb.execute
should be called, so as to capture its output (pygdb-fork.gdb,pygdb-fork.py)... This almost worked - as there are strings returned; however GDB notices something ain't right: "[tcsetpgrp failed in terminal_inferior: Operation not permitted]", and the subsequent return strings don't seem to change.And finally, the approach that worked is: temporarily redirecting the GDB output from a gdb.execute
to a logfile in RAM (Linux: /dev/shm
); and then reading it back, parsing it and printing it from python - python also handles a simple while loop that steps until a breakpoint is reached.
The irony is - most of these bugs, that caused this solution via redirecting the logfile, are actually recently fixed in SVN; meaning those will propagate to the distros in the near future, and one will be able to use gdb.execute("CMDSTR", toString=True)
directly :/ Yet, as I cannot risk building GDB from source right now (and possibly bumping into possible new incompatibilites), this is good enough for me also :)
Here are the relevant files (partially also in pygdb-fork.gdb,pygdb-fork.py):
pygdb-logg.gdb
is:
# gdb script: pygdb-logg.gdb # easier interface for pygdb-logg.py stuff # from within gdb: (gdb) source -v pygdb-logg.gdb # from cdmline: gdb -x pygdb-logg.gdb -se test.exe # first, "include" the python file: source -v pygdb-logg.py # define shorthand for nextUntilBreakpoint(): define nub python nextUntilBreakpoint() end # set up breakpoints for test.exe: b main b doFunction # go to main breakpoint run
pygdb-logg.py
is:
# gdb will 'recognize' this as python # upon 'source pygdb-logg.py' # however, from gdb functions still have # to be called like: # (gdb) python print logExecCapture("bt") import sys import gdb import os def logExecCapture(instr): # /dev/shm - save file in RAM ltxname="/dev/shm/c.log" gdb.execute("set logging file "+ltxname) # lpfname gdb.execute("set logging redirect on") gdb.execute("set logging overwrite on") gdb.execute("set logging on") gdb.execute(instr) gdb.execute("set logging off") replyContents = open(ltxname, 'r').read() # read entire file return replyContents # next until breakpoint def nextUntilBreakpoint(): isInBreakpoint = -1; # as long as we don't find "Breakpoint" in report: while isInBreakpoint == -1: REP=logExecCapture("n") isInBreakpoint = REP.find("Breakpoint") print "LOOP:: ", isInBreakpoint, "\n", REP
Basically, pygdb-logg.gdb
loads the pygdb-logg.py
python script, sets up the alias nub
for nextUntilBreakpoint
, and initializes the session - everything else is handled by the python script. And here is a sample session - in respect to the test source in OP:
$ gdb -x pygdb-logg.gdb -se test.exe ... Reading symbols from /path/to/test.exe...done. Breakpoint 1 at 0x80483ec: file test.c, line 14. Breakpoint 2 at 0x80483c7: file test.c, line 7. Breakpoint 1, main () at test.c:14 14 count = 1; (gdb) nub LOOP:: -1 15 count += 2; LOOP:: -1 16 count = 0; LOOP:: -1 19 doFunction(); LOOP:: 1 Breakpoint 2, doFunction () at test.c:7 7 count += 2; (gdb) nub LOOP:: -1 9 count--; LOOP:: -1 10 } LOOP:: -1 main () at test.c:20 20 printf("%d\n", count); 1 LOOP:: -1 21 } LOOP:: -1 19 doFunction(); LOOP:: 1 Breakpoint 2, doFunction () at test.c:7 7 count += 2; (gdb)
... just as I wanted it :P Just don't know how reliable it is (and whether it will be possible to use in avr-gdb
, which is what I need this for :) EDIT: version of avr-gdb in Ubuntu 11.04 is currently 6.4, which doesn't recognize the python command :()
Well, hope this helps someone,
Cheers!
Here some references:
parse_and_eval
gone? - Stack OverflowIf 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