Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to print every executed line in GDB automatically until a given breakpoint is reached?

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?

like image 802
sdaau Avatar asked Aug 04 '11 19:08

sdaau


People also ask

How do you get the line it breaks at in GDB?

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.

Which command in GDB is used to find all breakpoints?

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.

What is until in GDB?

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.

What is watchpoint in GDB?

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.


1 Answers

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
like image 62
sdaau Avatar answered Oct 06 '22 01:10

sdaau