1

Is there a way, to record the execution of a particular function (or the entire program) in terms of the executed source code lines?

Consdier I set a breakpoint in gdb to function foo, and then repetedly call step, and it will tell me something like this:

(gdb) break foo
Thread 1 "main" hit Breakpoint 1, foo () at foo.cpp:10
(gdb) step
foo () at foo.cpp:12
(gdb) step
foo () at foo.cpp:13
(gdb) step
foo () at foo.cpp:12
(gdb) step
foo () at foo.cpp:14

Then I repeat that until foo is no longer in the output of bt. This gives me a trace of execution (foo.cpp:10->12->13->12->14), that is particularly useful to compare long control flows.

Is there a way to do this with gdb or is there another tool that does this? I am only interested in deterministic traces, not sampling. Ideally this could also be done for stepi (on instruction level) / next (without entering subroutines).

too honest for this site
  • 12,050
  • 4
  • 30
  • 52
Zulan
  • 21,896
  • 6
  • 49
  • 109
  • Looks like you debug stripped code or don't provide the source code. And that code is C++, not C. – too honest for this site Sep 20 '16 at 19:30
  • In fact for this particular problem the source code is not available, but it is not stripped. Of course source line level would not be applicable for stripped binaries. I do strongly believe this question applies to both C and C++ and thus as per the C-tag wiki it is appropriate to tag both. The only difference would be demangling. If you remove the tag please indicate how a C answer wouldn't be appropriate for C++. – Zulan Sep 20 '16 at 20:06
  • There seems to be a [similar question](http://stackoverflow.com/questions/6947389/how-to-print-every-executed-line-in-gdb-automatically-until-a-given-breakpoint-i) with the difference being the end condition. – Zulan Sep 20 '16 at 20:18
  • It is actually not about any of these languages, as it would be exactly the same for Pascal or Ada. In the first place, gdb is language-agnostic. Thanks for the information – too honest for this site Sep 20 '16 at 20:30
  • @Olaf: I don't see how the removal of tags constitutes a significant improvement of this question. I did intentionally select the sepcific C and C++ tags. See for instance [the gdb docs](https://sourceware.org/gdb/onlinedocs/gdb/C.html#C): *Since C and C++ are so closely related, many features of gdb apply to both languages. Whenever this is the case, we discuss those languages together.* Given that Java etc. answers would not be acceptable. This is **not** an entirely language agnostic question. – Zulan Sep 20 '16 at 20:39
  • 1
    I am not sure I am answering your question (therefore it's a comment), but a usual way is to pair `expect` and `gdb/mi` (see https://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI.html) – user58697 Sep 20 '16 at 20:39
  • 1
    I think you can use the solutions in the question you linked to, but with the end condition `! $_caller_is ("foo", 0)` (0 means to look only at the current frame). – Mark Plotnick Sep 20 '16 at 21:04
  • Thanks for the hints @user58697 and @Mark Plotnick, I did it in python as I am more familar with it than `expect`. Feel free to add an alternative `gdb/mi` / `expect` solution. – Zulan Sep 20 '16 at 21:22

1 Answers1

1

Based on this similar question, I was able to put together a quick python script for my purpose. Fortunately with less required bug-workarounds:

import sys
import gdb
import os
import re

def in_frames(needle):
    """ Check if the passed frame is still on the current stack """
    hay = gdb.newest_frame()
    while hay:
        if hay == needle:
            return True
        hay = hay.older()
    return False

# Use this to reduce any kind of unwanted noise
def filter_step(output):
    output = re.sub(r'^.*No such file or directory\.\n', r'', output, flags=re.M)
    output = re.sub(r'^\d+\s+in\s+.*\n', r'', output, flags=re.M)
    return output

def step_trace(filename=None, step="step"):
    counter = 0
    if filename:
        output = ""
    frame = gdb.newest_frame()
    print("Stepping until end of {} @ {}:{}".format(frame.name(), frame.function().symtab, frame.function().line))
    while in_frames(frame):
        counter += 1
        if filename:
            output += filter_step(gdb.execute(step, to_string=True))
        else:
            gdb.execute(step)

    if filename:
        with open(filename, "w") as file:
            file.write(output)
    print("Done stepping through {} lines.".format(counter))

To output a trace to a file

(gdb) source step_trace.py
(gdb) python step_trace("filename.log")

or directly

(gdb) source step_trace.py
(gdb) python step_trace()
Community
  • 1
  • 1
Zulan
  • 21,896
  • 6
  • 49
  • 109