Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Printing a stack trace from another thread

I know I can get the stack trace of the current thread using backtrace() or [NSThread callStackSymbols], but how would I get at the stack trace of a DIFFERENT thread (assuming it's been frozen)?

like image 970
Karl Avatar asked Jan 22 '11 00:01

Karl


2 Answers

EDIT: My original answer will not print from an arbitrary thread. I've since written a proper implementation in my crash handler project: https://github.com/kstenerud/KSCrash

Specifically, these files:

  • https://github.com/kstenerud/KSCrash/blob/master/KSCrash/KSCrash/Reporting/Tools/KSBacktrace.h
  • https://github.com/kstenerud/KSCrash/blob/master/KSCrash/KSCrash/Reporting/Tools/KSBacktrace.c

With some help from:

  • https://github.com/kstenerud/KSCrash/blob/master/KSCrash/KSCrash/Reporting/Tools/KSMach.h
  • https://github.com/kstenerud/KSCrash/blob/master/KSCrash/KSCrash/Reporting/Tools/KSMach.c

What you do is:

  • Make a new machine context structure (_STRUCT_MCONTEXT)
  • Fill in its stack state using thread_get_state()
  • Get the program counter (first stack trace entry) and frame pointer (all the rest)
  • Step through the stack frame pointed to by the frame pointer and store all instruction addresses in a buffer for later use.

Note that you should pause the thread before doing this or else you can get unpredictable results.

The stack frame is filled with structures containing two pointers:

  • Pointer to the next level up on the stack
  • instruction address

So you need to take that into account when walking the frame to fill out your stack trace. There's also the possibility of a corrupted stack, leading to a bad pointer, which will crash your program. You can get around this by copying memory using vm_read_overwrite(), which first asks the kernel if it has access to the memory, so it doesn't crash.

Once you have the stack trace, you can just call backtrace() on it like normal (The crash handler has to be async-safe so it implements its own backtrace method, but in normal cases backtrace() is fine).

like image 174
Karl Avatar answered Sep 30 '22 20:09

Karl


Here is some safer way to get the callstack from another thread: Implementation and some background information. It uses signal handling and spawns a signal handler in the target thread. It has also the advantage that it is more cross-platform than your solution, i.e. it should work anywhere where you have <signal.h> and <execinfo.h>.

For the printing, you can use backtrace_symbols as you do in your own suggestion. But you might be interested in an extended version of that as implemented here. It uses libbfd (from binutils; the recent version also mostly works on MacOSX, see here for a small limitation which might not be relevant for you) to read the debugging information and to add line-number and other information (it also falls back to dladdr if all else fails; that is what backtrace_symbols is doing).

like image 24
Albert Avatar answered Sep 30 '22 20:09

Albert