Sample watchpoints or breakpoints with GDB (and FlameGraph)

| 🤔 | 👍 | 👎 |

GDB can be used to get the stack each time a breakpoint is reached.

GDB in batch mode can be used to get the stack each time a breakpoint/watchpoint is hit:

Sampling breakpoints

# This is sample.gdb:

# my_function may not be availabe straight away.
# First get into main:
break main
run
delete

# Now, we can set the breakpoint:
break my_function
commands
  silent
  backtrace
  continue
end

# Resume the program:
continue

And run it with:

gdb --batch -x sample.gdb ./my_program > my_program.txt

Sampling breakpoints as a Python script

We can use Python instead which is simpler and more flexible:

class MyBreakpoint(gdb.Breakpoint):
      def stop (self):
        gdb.execute("backtrace")
        return False

main = MyBreakpoint("my_function");
gdb.execute("run")

The script must end in .py in order to be recognised by GDB as a Python script:

gdb --batch -x sample.py ./my_program > my_program.txt

Example

Here is an example with a watchpoint I used to find where an expected value in a program was coming from:

# Run the program a first time.
# The program calls abort() when the expected value is reached.
# A conditional breakpoint could be used instead.
run

# At this point, setup a watchpoint on the location of this unexpected value:
frame 3
# -l is used in order to set the breakpoint on a given address:
watch -l *(int*) ((char*)heap_region1->start_addr + ((char*)&heapinfo1->type-(char*)heap_region1->data))
commands
  backtrace
  continue
end

# Restart the application in order to figure out who writes at this address:
run

In order to have stable addresses between the two runs, we need to disable ASLR :

setarch x86_64 -R gdb --batch -x ./my_program > my_program.txt

We can even generate a FlameGraph:

[corresponding flamegraph]
FlameGraph generated from this watchpoint