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

后端 未结 4 1631
猫巷女王i
猫巷女王i 2020-11-27 03:51

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 exampl

相关标签:
4条回答
  • 2020-11-27 04:03

    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
    0 讨论(0)
  • 2020-11-27 04:04

    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:

    GDB TUI Screenshot

    ... 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!

    0 讨论(0)
  • 2020-11-27 04:17

    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]
    
    0 讨论(0)
  • 2020-11-27 04:18

    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
    
    0 讨论(0)
提交回复
热议问题