Reputation: 463
Say I'm in the middle of a function and LLDB stopped on a segfault (bad memory access).
Is it possible to clear the signal, and return to main
, and re-execute everything? (If the code has made any stateful changes, i would expect those to be kept).
I'm not asking to kill and restart the process in a new session.
(Not sure if this helps clarifying the question, but for those familiar with Java debuggers, there is this concept of "Drop to frame", which basically pops the top x frames off, and restarts the execution)
Updated: More details after using thread return
, suggested below in an answer.
Using thread return
and adjusting the pc
did get me back to previous execution points but the signal is not cleared, and lldb refused to continue.
(lldb) target create "test.out"
Current executable set to '/Users/user/test.out' (x86_64).
(lldb) run
Process 89918 launched: '/Users/user/test.out' (x86_64)
before npe
i=0
i=1
i=2
i=3
i=4
Process 89918 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x5)
frame #0: 0x0000000100003ee1 test.out`cause_npe(n=5) at test.c:17:11
14 printf("i=%d\n", i);
15
16 // cause bad mem access here
-> 17 return *(int*)(n);
18 }
19
20 int main ()
Target 0: (test.out) stopped.
(lldb) thread return
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x5)
frame #0: 0x0000000100003f1a test.out`main at test.c:25:3
22 // signal (SIGSEGV, my_handler);
23 printf( "before npe\n");
24 cause_npe(5);
-> 25 printf("recovered after signal\n");
26 return 0;
27 }
(lldb) register write pc `$pc-8`
(lldb) register write pc `$pc-8`
(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x5)
* frame #0: 0x0000000100003f0a test.out`main at test.c:23:3
frame #1: 0x00007fff6efa3cc9 libdyld.dylib`start + 1
(lldb) n
Process 89918 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x5)
frame #0: 0x0000000100003f0a test.out`main at test.c:23:3
20 int main ()
21 {
22 // signal (SIGSEGV, my_handler);
-> 23 printf( "before npe\n");
24 cause_npe(5);
25 printf("recovered after signal\n");
26 return 0;
Target 0: (test.out) stopped.
test.c
code:
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int cause_npe(int n) {
// prints a few lines before the bad mem access
for (int i = 0; i < n; ++i)
printf("i=%d\n", i);
// cause bad mem access here
return *(int*)(n);
}
int main () {
printf( "before npe\n");
cause_npe(5);
printf("recovered after signal\n");
return 0;
}
Upvotes: 0
Views: 685
Reputation: 27148
lldb has a thread return
command that pops frames off the stack. That will return to a point just after the call that was active in the stack frame you returned to, so you have to use thread jump
to put the pc back on the call site.
Doing this will leave the memory state intact, so anything accessed from a global or static variable will retain its state. thread return
is pretty brute force, however, it blindly unwinds the stack, it doesn't try to emulate an exception throw with all its cleanups. So any stack allocated objects will be stranded rather than destroyed, and you are likely to end up with over-retained objects if you return past the releases.
Also, if you program is multi-threaded, you'll have to be careful that other threads haven't been passed stack allocated objects from the thread you are unwinding. Those will get reallocated when you re-execute, so the two threads will have different copies.
Anyway, while possible, this is not a general purpose technique, and if you use it you want to be skeptical of what you see or you are likely to spend time chasing down artifacts of the unwinding instead of learning anything useful.
Upvotes: 1