Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to break on a breakpoint only when a certain method is present in the call stack?

Suppose I have a method foo that gets called by different methods, when going through a hierarchy of object.

Is it possible to break inside method foo, only when it was called by method bar (so bar is present in the call stack)?

Does LLDB or GDB support such a use-case?

like image 257
alcroito Avatar asked Dec 21 '15 09:12

alcroito


Video Answer


3 Answers

Recent versions of gdb ship with some convenience functions written in Python for just this case. Take a look at $_caller_is and friends. (FWIW this exact use case was what motivated me to work on adding Python to gdb...)

A simple use would be:

(gdb) break foo if $_any_caller_matches("bar")

If the call stack contains more functions in between foo and bar calls resulting in a stack that looks like the following,

foo()
...
...
...
bar()
...
...
...
main()

you could pass an extra argument to _any_caller_matches which indicates the number of frames to check for the occurrence of bar

(gdb) break foo if $_any_caller_matches("bar", 10)

Reference: https://sourceware.org/gdb/current/onlinedocs/gdb/Convenience-Funs.html

$_any_caller_matches(regexp[, number_of_frames])

Returns one if any calling function’s name matches the regular expression regexp. Otherwise it returns zero.

If the optional argument number_of_frames is provided, it is the number of frames up in the stack to look. The default is 1.

This function differs from $_caller_matches in that this function checks all stack frames from the immediate caller to the frame specified by number_of_frames, whereas $_caller_matches only checks the frame specified by number_of_frames.

like image 125
Tom Tromey Avatar answered Oct 05 '22 04:10

Tom Tromey


The way you would do this in lldb is to write a Python based breakpoint command that checks the stack of the thread that hit the breakpoint, and continues if it doesn't contain a frame with that function. The breakpoint commands tell lldb to stop or not by returning True or False respectively from the function. So a very simplistic way to do this would be to make a Python file (break_here.py):

import lldb
desired_func = "some_func"
def break_here(frame, bp_loc, dict):
    thread = frame.thread
    for frame in thread.frames:
        if frame.name == desired_func:
            return True
    return False

Suppose this file is in /tmp. Then in lldb you do:

(lldb) com scr imp /tmp/break_here.py
(lldb) br s -n whatever
(lldb) br com add --python-function break_here.break_here
like image 44
Jim Ingham Avatar answered Oct 05 '22 02:10

Jim Ingham


In GDB you can associate a debugger command list to a breakpoint, so to achieve your aim, you could place a breakpoint in bar() where it calls foo() with a command list that that sets a breakpoint in foo() and continues. A further breakpoint in bar() after its call to foo() will be required to clear the breakpoint in foo().

So:

int bar()
{
    foo() ;     // Add breakpoint with command list here to set breakpoint in foo()
    return 0 ;  // Add breakpoint command list here to clear breakpoint in foo()
}

Of course if this happens very infrequently, you could set the breakpoints manually.

If foo() is not called directly from bar() or perhaps foo() is called from multiple places in bar(), then the same solution applies; it is sufficient to set the breakpoint at the start if bar() and clear it at the end.

One caveat; if the application is multi-threaded and either bar() or foo() may be called from more than one thread, then you will need a thread specific breakpoint.

like image 31
Clifford Avatar answered Oct 05 '22 03:10

Clifford