How to debug where a function returns using LLDB from the command line

How to debug where a function returns using LLDB from the command line

I often find myself in a situation when I want to know where a function returns. There’s no need to know the return value, as this may be the same for multiple code paths (e.g., nullptr if something went wrong). It is embarrassing, but I sometimes have put fprintf(stderr, "T1"); in my code just to follow which path the execution took. Needless to say, this behavior requires manual editing and recompilation and should be avoided if possible.

Here’s a way to elegantly debug where a function returns using lldb from the command line.

Everything you need to grow your career.

With your free Red Hat Developer program membership, unlock our library of cheat sheets and ebooks on next-generation application development.

SIGN UP

Consider this test.cpp program and all you want to do is find out where the function foo returns:

int foo(int argc) {
  switch (argc) {
  case 1:
    return 1;
  case 2:
    return 2;
  case 3:
    return 3;
  }
  return -1;
}

int main(int argc, char *argv[]) { return foo(argc); }

Note that there are five return statements in this code, but we only want to know which of the four inside of foo are being hit.

Let’s start by compiling the above program with debug symbols:

clang -g test.cpp

To get to know where foo returns, you can run the following command.

lldb -b -o "br set -X foo -p return" -o r ./a.out -- hello world
  1. The -b toggles on batch mode. I find this handy because it lets you execute your program in a fire-and-forget fashion without leaving you in the debugger when your program is done.
  2. The -o "br set -X foo -p return" sets a breakpoint on the pattern return inside the function foo. Note that a breakpoint is limited only to the return statements inside the function foo (we have four, not five locations).
  3. The -o r runs the program and stops at the breakpoint inside foo.
  4. Everything after the -- is passed to our program ./a,out as arguments.

Here you see the effect:

(lldb) target create "./a.out"
Current executable set to './a.out' (x86_64).
(lldb) settings set -- target.run-args  "hello" "world"
(lldb) br set -X foo -p return
Breakpoint 1: 4 locations.
(lldb) r
Process 7542 stopped
* thread #1, name = 'a.out', stop reason = breakpoint 1.3
    frame #0: 0x0000000000401170 a.out`foo(argc=3) at test.cpp:8:5
   5   	  case 2:
   6   	    return 2;
   7   	  case 3:
-> 8   	    return 3;
   9   	  }
   10  	  return -1;
   11  	}

Process 7542 launched: '/home/kkleine/a.out' (x86_64)

I hope you like this tip. For more useful LLDB tips on breakpoints, please visit this page: https://lldb.llvm.org/use/tutorial.html#setting-breakpoints

Share