I am trying to place a conditional breakpoint on a function that would check the contents of an array. My idea was to use memcmp() in the condition:
typedef struct {
uint8_t arr[4];
} arg_t;
Then in gdb (declaring an array inline):
b func() if memcmp(arg.arr, (uint8_t[]){1, 2, 3, 4}, sizeof(arg.arr)) == 0
However, this does not work:
(gdb) c
Continuing.
Error in testing breakpoint condition:
Too many array elements
I can do it with if arg.arr[0] == 1 && arg.arr[1] == 2 && ... but in my real case access to the array (containing an IPv6 address) is rather convoluted, so it becomes unwieldy rather quickly.
UPDATE: After Mark's comment, I tried the following test program:
#include <stdio.h>
#include <stdint.h>
#include <string.h>
int main(void) {
uint8_t a[] = { 1, 2, 3, 4 };
printf("memcmp returns %d\n", memcmp(a, (uint8_t[4]){1,2,3,4}, sizeof(a)));
return 0;
}
After compiling it, I placed a conditional break on the return using the explicit array size syntax, and it worked but it does not seem to stop as expected:
(gdb) br hello.c:8 if memcmp(a, (uint8_t[4]){1,2,3,4}, sizeof(a)) == 0
Breakpoint 1 at 0x75c: file hello.c, line 8.
(gdb) run
Starting program: /mnt/c/stuff/src/test/hello
memcmp returns 0
[Inferior 1 (process 153) exited normally]
I was trying to evaluate the return value of the function at the break site manually:
(gdb) br hello.c:8
Note: breakpoint 1 also set at pc 0x800075c.
Breakpoint 2 at 0x800075c: file hello.c, line 8.
(gdb) dele 1
(gdb) run
Starting program: /mnt/c/stuff/src/test/hello
memcmp returns 0
Breakpoint 2, main () at hello.c:8
8 return 0;
(gdb) p memcmp(a, (uint8_t[4]){1,2,3,4}, sizeof(a))
$1 = (int (*)(const void *, const void *, size_t)) 0x7fffff18aba0 <__memcmp_avx2_movbe>
I was surprised to see this, I suspect this may be due to memcmp() being a compiler intrinsic to the avx2 instruction, in which case I might need to cast it somehow?
This may not be what you want, but for complex tests, I get the program to help gdb by adding some check routines.
Method 1:
I create a void checkon (void) { if (expr_to_stop_on) badnews(); }
And then have: void badnews { stopme = 1; }
I then instrument the code with calls to checkon
I then tell gdb to do b badnews.
Method 2:
An alternative may be to create: int checkfail(void) { return expr_to_stop_on; }
Then, tell gdb to watch myarray.
This creates a watchpoint (using H/W assist). The watchpoint is like a breakpoint.
You can then do: cond 1 checkfail()
Method 3:
Similar to method 2, but instead of a watchpoint, use a tracepoint [with the same cond command (Actually, you may need to use actions instead).
This essentially single steps the program and allows a set of commands to be executed at every tracepoint.
This can be slow because every line has to do these things.
I think method 2 is probably the best bet.
(Posting my own answer to be able to close this, but credit goes to #mark-plotnick)
It works with the gdb builtin $_memeq instead of using memcmp - suspecting because of the latter not being a real function in my environment but YMMV:
(gdb) br arrbreak.c:8 if $_memeq(a, (uint8_t[4]){1,2,3,4}, sizeof(a))
Breakpoint 2 at 0x800075c: file arrbreak.c, line 8.
(gdb) run
Starting program: /mnt/c/stuff/src/test/arrbreak
memcmp returns 0
Breakpoint 2, main () at arrbreak.c:8
8 return 0;
(gdb) p $_memeq(a, (uint8_t[4]){1,2,3,4}, sizeof(a))
$1 = 1
(gdb) p $_memeq(a, (uint8_t[4]){1,2,3,5}, sizeof(a))
$2 = 0
(gdb)
I am running an application through gdb and I want to set a breakpoint for any time a specific variable is accessed / changed. Is there a good method for doing this? I would also be interested in other ways to monitor a variable in C/C++ to see if/when it changes.
watch only breaks on write, rwatch let you break on read, and awatch let you break on read/write.
You can set read watchpoints on memory locations:
gdb$ rwatch *0xfeedface
Hardware read watchpoint 2: *0xfeedface
but one limitation applies to the rwatch and awatch commands; you can't use gdb variables
in expressions:
gdb$ rwatch $ebx+0xec1a04f
Expression cannot be implemented with read/access watchpoint.
So you have to expand them yourself:
gdb$ print $ebx
$13 = 0x135700
gdb$ rwatch *0x135700+0xec1a04f
Hardware read watchpoint 3: *0x135700 + 0xec1a04f
gdb$ c
Hardware read watchpoint 3: *0x135700 + 0xec1a04f
Value = 0xec34daf
0x9527d6e7 in objc_msgSend ()
Edit: Oh, and by the way. You need either hardware or software support. Software is obviously much slower. To find out if your OS supports hardware watchpoints you can see the can-use-hw-watchpoints environment setting.
gdb$ show can-use-hw-watchpoints
Debugger's willingness to use watchpoint hardware is 1.
What you're looking for is called a watchpoint.
Usage
(gdb) watch foo: watch the value of variable foo
(gdb) watch *(int*)0x12345678: watch the value pointed by an address, casted to whatever type you want
(gdb) watch a*b + c/d: watch an arbitrarily complex expression, valid in the program's native language
Watchpoints are of three kinds:
watch: gdb will break when a write occurs
rwatch: gdb will break wnen a read occurs
awatch: gdb will break in both cases
You may choose the more appropriate for your needs.
For more information, check this out.
Assuming the first answer is referring to the C-like syntax (char *)(0x135700 +0xec1a04f) then the answer to do rwatch *0x135700+0xec1a04f is incorrect. The correct syntax is rwatch *(0x135700+0xec1a04f).
The lack of ()s there caused me a great deal of pain trying to use watchpoints myself.
I just tried the following:
$ cat gdbtest.c
int abc = 43;
int main()
{
abc = 10;
}
$ gcc -g -o gdbtest gdbtest.c
$ gdb gdbtest
...
(gdb) watch abc
Hardware watchpoint 1: abc
(gdb) r
Starting program: /home/mweerden/gdbtest
...
Old value = 43
New value = 10
main () at gdbtest.c:6
6 }
(gdb) quit
So it seems possible, but you do appear to need some hardware support.
Use watch to see when a variable is written to, rwatch when it is read and awatch when it is read/written from/to, as noted above. However, please note that to use this command, you must break the program, and the variable must be in scope when you've broken the program:
Use the watch command. The argument to the watch command is an
expression that is evaluated. This implies that the variabel you want
to set a watchpoint on must be in the current scope. So, to set a
watchpoint on a non-global variable, you must have set a breakpoint
that will stop your program when the variable is in scope. You set the
watchpoint after the program breaks.
In addition to what has already been answered/commented by asksol and Paolo M
I didn't at first read understand, why do we need to cast the results. Though I read this: https://sourceware.org/gdb/onlinedocs/gdb/Set-Watchpoints.html, yet it wasn't intuitive to me..
So I did an experiment to make the result clearer:
Code: (Let's say that int main() is at Line 3; int i=0 is at Line 5 and other code.. is from Line 10)
int main()
{
int i = 0;
int j;
i = 3840 // binary 1100 0000 0000 to take into account endianness
other code..
}
then i started gdb with the executable file
in my first attempt, i set the breakpoint on the location of variable without casting, following were the results displayed
Thread 1 "testing2" h
Breakpoint 2 at 0x10040109b: file testing2.c, line 10.
(gdb) s
7 i = 3840;
(gdb) p i
$1 = 0
(gdb) p &i
$2 = (int *) 0xffffcbfc
(gdb) watch *0xffffcbfc
Hardware watchpoint 3: *0xffffcbfc
(gdb) s
[New Thread 13168.0xa74]
Thread 1 "testing2" hit Breakpoint 2, main () at testing2.c:10
10 b = a;
(gdb) p i
$3 = 3840
(gdb) p *0xffffcbfc
$4 = 3840
(gdb) p/t *0xffffcbfc
$5 = 111100000000
as we could see breakpoint was hit for line 10 which was set by me. gdb didn't break because although variable i underwent change yet the location being watched didn't change (due to endianness, since it continued to remain all 0's)
in my second attempt, i did the casting on the address of the variable to watch for all the sizeof(int) bytes. this time:
(gdb) p &i
$6 = (int *) 0xffffcbfc
(gdb) p i
$7 = 0
(gdb) watch *(int *) 0xffffcbfc
Hardware watchpoint 6: *(int *) 0xffffcbfc
(gdb) b 10
Breakpoint 7 at 0x10040109b: file testing2.c, line 10.
(gdb) i b
Num Type Disp Enb Address What
6 hw watchpoint keep y *(int *) 0xffffcbfc
7 breakpoint keep y 0x000000010040109b in main at testing2.c:10
(gdb) n
[New Thread 21508.0x3c30]
Thread 1 "testing2" hit Hardware watchpoint 6: *(int *) 0xffffcbfc
Old value = 0
New value = 3840
Thread 1 "testing2" hit Breakpoint 7, main () at testing2.c:10
10 b = a;
gdb break since it detected the value has changed.
#include <stdio.h>
typedef struct ThingStruct {
int arr[8];
int after;
} Thing;
void foo(int i) {
Thing thing;
int* ip = &thing.after;
thing.after = 12345;
printf("beforehand\n");
thing.arr[i] = 55;
printf("done\n");
}
int main() {
foo(8);
}
This code changes thing.after by accidentally going off the end of the array. I want to try to find the line where where thing.after is changing by using gdb. So I compile with -g , put a breakpoint on line 12, then put a watchpoint on thing.after, but the watchpoint doesn't trigger, even though putting a breakpoint on line 14 does show that thing.after did change.
I even tried taking the address of thing.after and setting a watchpoint on that, but it still does not trigger.
Watch point needs to be re-added each time the foo function is entered (Note that, as you are watching the local variable, it will not be valid after the stack frame exits and will be automatically deleted after the foo returns). Also, if the watched variable changes on the current line to be executed, then the watch point is not getting triggered (not sure why). For me it works when I add the watch point watch thing.after just after entering foo when on line int* ip = &thing.after;. When I continue, the watch point hits 2 times.
You didn't say which platform, what version of GDB, or what command you used to set the watchpoint.
Using gdb 7.9 on Ubuntu/x86_64, things work as I expect them to work:
(gdb) b foo
Breakpoint 1 at 0x400538: file t.c, line 10.
(gdb) r
Starting program: /tmp/a.out
Breakpoint 1, foo (i=8) at t.c:10
10 int* ip = &thing.after;
(gdb) watch thing.after
Hardware watchpoint 2: thing.after
(gdb) c
Continuing.
Hardware watchpoint 2: thing.after
Old value = 4195712
New value = 12345
foo (i=8) at t.c:12
12 printf("beforehand\n");
(gdb) c
Continuing.
beforehand
Hardware watchpoint 2: thing.after
Old value = 12345
New value = 55
foo (i=8) at t.c:14
14 printf("done\n");
(gdb) q
Code snippets from two C source files:
A.c
Channel *testChannelGet()
{
Channel *ch = channelGet (parser,parserCh);
return ch;
}
B.c
Channel *channelGet(UINT8 parser, UINT16 parserCh)
{
chnl.player = &solPlayer;
return((Channel *)&chnl);
}
I compile both files and create a static and a shared library. Now I call testChannelGet from a sample program. When I link it against the static library, it works perfectly. But if I link it against the shared library, its SEGFAULTing. Debugging tells me that the pointer returned from channelGet is changing the moment it returns. GDB output below.
174 Channel *ch = channelGet (parser,parserCh);
(gdb) s
channelGet (parser=1 '\001', parserCh=1) at B.c:15174
15174 chnl.player = &solPlayer;
(gdb) n
15175 return((Channel *)&chnl);
(gdb) p ((Channel *)&chnl)
$1 = (Channel *) 0x7ffff7fed1a0
(gdb) n
15176 }
(gdb) n
testChannelGet at A.c:175
175 return ch;
(gdb) p ch
$2 = (Channel *) 0xfffffffff7fed1a0
It seems the address value points to a different offset now - 0xfffffffff7fed1a0 vs 0x7ffff7fed1a0 . The last bytes in both addresses are the same.
Any hints? I have tried the -fPIC option to no avail.
Is there a prototype in scope for channelGet() in A.c?
If not, the results you're seeing could be explained as follows:
channelGet() is assumed to return int (due to lack of prototype), so the result is truncated to 0xf7fed1a0
then it is cast to a 64-bit pointer, so gets sign-extended to 0xfffffffff7fed1a0
(You should get complaints about this if you compile with warnings enabled, of course...)
Run your program under valgrind. Find and fix any errors it reports.
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?
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:
I first found about Convenience Variables, and thought - given there are program counters and such available, there must be some GDB convenience variable that gives the "breakpoint" status, and can be used directly in a GDB script. After looking through GDB reference Index for a while, however, I simply cannot find any such variables (my attempts are in nub.gdb)
In lack of such a "breakpoint status" internal variable - the only thing left to do, is to capture the ('stdout') command line output of GDB (in response to commands) as a string, and parse it (looking for "Breakpoint")
Then, I found out about Python API to GDB, and the 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]"!
So, first I tried to make a script (pygdb-nub.py,gdbwrap) that would utilize gdb.execute in the recommended manner; failed here - because of this:
Bug 627506 – python: gdb.execute([...], to_string=True) partly prints to stdout/stderr
Bug 10808 – Allow GDB/Python API to capture and store GDB output
Then, I thought I'd use a python script to 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)
Then, I thought I'd use python scripts to be called from GDB (via 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:
GDB: error detected on stdin
GDB has problems with getting commands piped to STDIN
Re: [Gdb] How do i use GDB other input?
gdb doesn't accept input on stdin
Using gdb in an IDE - comp.os.linux.development.apps | Google Groups
rmathew: Terminal Sickness
[TUTORIAL] Calling an external program in C (Linux) - GIDForums
shell - how to use multiple arguments with a shebang (i.e. #!)? - Stack Overflow
Redirecting/storing output of shell into GDB variable? - Stack Overflow
Corey Goldberg: Python - Redirect or Turn Off STDOUT and STDERR
The Cliffs of Inanity › 9. Scripting gdb
gdb python scripting: where has parse_and_eval gone? - Stack Overflow
shell - Invoke gdb to automatically pass arguments to the program being debugged - Stack Overflow
Storing Files/Directories In Memory With tmpfs | HowtoForge - Linux Howtos and Tutorials
simple way to touch a file if it does not exist | Python | Python
os.fork() different in cgi-script? - Python
java - Writing tests that use GDB - how to capture output? - Stack Overflow
Debugging with GDB: How to create GDB Commands in Python - Wiki
GDB reference card
What about doing it like this in gdb, using a command file. Change file argument, and loop count as required.
gdb -x run.gdb
run.gdb:
set pagination off
set logging file gdb.log
set logging on
set $i = 0
file main
break main
break WriteData
# sadly, commands not getting executed on reaching breakpoint 2
commands 2
set $i=1000
print "commands 2 : %d",$i
end
run
while ( $i < 1000 )
step
# next
# continue
set $i = $i + 1
end
Based on the link in #sdaau's answer (http://www.mail-archive.com/gdb#gnu.org/msg00031.html), I created my own script to simply keep sending 's' and reading the output of gdb continuously, while printing output to textfile and terminal, of course, my script can be modified to fit anyone else's needs, however, I hope that the modification I made should fit most people needs.
http://www.codeground.net/coding/gdb-step-into-all-lines-to-get-full-application-flow/
wget http://www.codeground.net/downloads/gdbwalkthrough.c
gcc gdbwalkthrough.c -o gdbwalkthrough
./gdbwalkthrough <application full path> [application arguments]
As a new answer, since the previous is already hogged :) Basically, if the point is to observe execution of source (and/or assembly) code lines as the program as running - as the motivation is often for me when looking into "automatic printout" -- then, basically, a very quick way is to use GDB TUI mode; I quote:
c - gdb behavior : value optimized out - Stack Overflow #1354762
Use the GDB TUI mode. My copy of GDB enables it when I type the minus and Enter. Then type C-x 2 (that is hold down Control and press X, release both and then press 2). That will put it into split source and disassembly display. Then use stepi and nexti to move one machine instruction at a time. Use C-x o to switch between the TUI windows.
The trick here is that, even if you hit continue - this time source will be shown and indicated on the TUI; and followed as the program runs:
... and this for me avoids many situations where I'd have to script the breakpoints in "auto-stepping context" (although there are still such situations).. Docs about TUI: TUI - Debugging with GDB
Cheers!
Actually, I have a Github repo with a Python-GDB extension, which does exactly the same thing as You have described, but with some more functionality.
You can just clone the repo:
git clone https://github.com/Viaceslavus/gdb-debug-until.git
and feed the python script to GDB with the following command inside GDB:
source gdb-debug-until/debug_until.py
(Change python script path if necessary)
And now you can use the following command to run through each line of your code until a breakpoint:
debug-until somefile.c:100 --args="" --end="somefile.c:200"
"somefile.c:100" here is a starting breakpoint, and "somefile.c:200" is the final breakpoint.
"--args" specifies a set of arguments to your program (you can omit it if there are no arguments).
With this extension you can also run few times through the code (with '-r' option) and even specify some events that should be handled while debugging. For more info see:
https://github.com/Viaceslavus/gdb-debug-until
The currently accepted answer includes a lot of file io and does only stop on breakpoints, but watchpoints, signals and possibly even the program end is ignored.
Using the python api this can be handled nicely:
define a user command (with additional argument to say how fast to auto-step)
optional: define a parameter for the default (both variants below)
do the while loop within python, handle the "expected" keyboard interrupt of CTRL-C
register a stop event handler that checks for the stop reason and store the kind of step there
adjust the while loop to stop for a "not simple" stop (breakpoint/watchpoint/signal/...)
The following code may be placed in a gdb-auto-step.py which can be made active with source gdb-auto-step.py whenever you want that (or include in the .gdbinit file to make it always available):
import gdb
import time
import traceback
class CmdAutoStep (gdb.Command):
"""Auto-Step through the code until something happens or manually interrupted.
An argument says how fast auto stepping is done (1-19, default 5)."""
def __init__(self):
print('Registering command auto-step')
super(CmdAutoStep, self).__init__("auto-step", gdb.COMMAND_RUNNING)
gdb.events.stop.connect(stop_handler_auto_step)
def invoke(self, argument, from_tty):
# sanity check - are we even active, prevents a spurious "no registers" exception
try:
gdb.newest_frame()
except gdb.error:
raise gdb.GdbError("The program is not being run.")
# calculate sleep time
if argument:
if not argument.isdigit():
raise gdb.GdbError("argument must be a digit, not " + argument)
number = int(argument)
if number == 0 or number > 19:
raise gdb.GdbError("argument must be a digit between 1 and 19")
sleep_time = 3.0 / (1.4 ** number)
# activate GDB scrolling, otherwise we'd auto-step only one page
pagination = gdb.parameter("pagination")
if pagination:
gdb.execute("set pagination off", False, False)
# recognize the kind of stop via stop_handler_auto_step
global last_stop_was_simple
last_stop_was_simple = True
# actual auto-stepping
try:
while last_stop_was_simple:
gdb.execute("step")
time.sleep(sleep_time)
# we just quit the loop as requested
# pass keyboard and user errors unchanged
except (KeyboardInterrupt, gdb.GdbError):
raise
# that exception is unexpected, but we never know...
except Exception:
traceback.print_exc()
# never leave without cleanup...
finally:
if pagination:
gdb.execute("set pagination on", False, False)
def stop_handler_auto_step(event):
# check the type of stop, the following is the common one after step/next,
# a more complex one would be a subclass (for example breakpoint or signal)
global last_stop_was_simple
last_stop_was_simple = type(event) is gdb.StopEvent
CmdAutoStep()
To specify the default with a parameter (aka "the gdb way") add a new parameter via api and use it as follows (comes also with 0 = unlimited, handling process exit, an additional auto-next command and more class wrapping):
import gdb
import time
import traceback
class ParameterAutoSpeed (gdb.Parameter):
"""default speed for auto-step and auto-next commands (0-19, default 5)"""
def __init__(self):
self.set_doc = """Set speed for "auto-step" and "auto-next",
internally used to calculate sleep time between iterations of "step" / "next";
set "auto-speed 0" causes there to be no sleeping."""
self.show_doc = "Speed value for auto-step/auto-next."
super(ParameterAutoSpeed, self).__init__("auto-speed", gdb.COMMAND_RUNNING, gdb.PARAM_UINTEGER)
self.value = 5
self.backup = self.value
def get_set_string (self):
try:
self.value = int(self.validate(self.value))
except gdb.GdbError:
self.value = int (self.backup)
raise
self.backup = self.value
return ""
def validate (self, argument):
"""validation for auto-step/auto-next speed"""
try:
speed = int(argument)
if speed < 0 or speed > 19:
raise ValueError()
except (TypeError, ValueError):
raise gdb.GdbError("speed argument must be an integer between 1 and 19, or 0")
return speed
class CmdAutoNext (gdb.Command):
"""Auto-Next through the code until something happens or manually interrupted.
An argument says how fast auto stepping is done (see parameter "auto-speed")."""
def __init__(self, worker):
self.worker = worker
super(CmdAutoNext, self).__init__("auto-next", gdb.COMMAND_RUNNING)
def invoke(self, argument, from_tty):
self.worker.invoke (argument)
class CmdAutoStep (gdb.Command):
"""Auto-Step through the code until something happens or manually interrupted.
An argument says how fast auto stepping is done (see parameter "auto-speed").
Note: To be usable you likely need several "skip" setup for not stepping into functions of
the C library and other system libraries which may have no debug symbols available
or are of no interest.
You may press [CTRL]+[C] and execute "skip file", then "finish" to leave those."""
def __init__(self, worker):
self.worker = worker
super(CmdAutoStep, self).__init__("auto-step", gdb.COMMAND_RUNNING)
def invoke(self, argument, from_tty):
self.worker.invoke (argument, do_step=True)
class AutoWorker ():
def __init__(self):
print('Registering parameter auto-speed and commands auto-step, auto-next')
self.speed = ParameterAutoSpeed()
CmdAutoStep(self)
CmdAutoNext(self)
gdb.events.stop.connect(self.stop_handler_auto)
gdb.events.exited.connect(self.exit_handler_auto)
def invoke(self, argument, do_step=False):
# calculate sleep time
if argument:
number = self.speed.validate(argument) # raises an error if not valid
else:
number = self.speed.value
if number:
sleep_time = 3.0 / (1.4 ** number)
else:
sleep_time = 0
# activate GDB scrolling, otherwise we'd auto-step/next only one page
pagination = gdb.parameter("pagination")
if pagination:
gdb.execute("set pagination off", False, False)
# recognize the kind of stop via stop_handler_auto_step
self.last_stop_was_simple = True
# actual auto-stepping
try:
while self.last_stop_was_simple:
if do_step:
gdb.execute ("step")
else:
gdb.execute ("next")
time.sleep(sleep_time)
# we just quit the loop as requested
# pass keyboard and user errors unchanged
except (KeyboardInterrupt, gdb.GdbError):
raise
# wrap GDB errors like "the program is not being run" to let them be
# handled via default invoke error reporting, not as a python error
except gdb.error as err:
raise gdb.GdbError(err)
# that exception is unexpected, but we never know...
except Exception:
traceback.print_exc()
# never leave without cleanup...
finally:
if pagination:
gdb.execute("set pagination on", False, False)
def stop_handler_auto(self, event):
# check the type of stop, the following is the common one after step/next,
# a more complex one would be a subclass (for example breakpoint or signal)
self.last_stop_was_simple = type(event) is gdb.StopEvent
def exit_handler_auto(self, event):
self.last_stop_was_simple = False
AutoWorker()