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

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 😛 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

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)