LLDB:在遇到信号时在同一会话中重新启动应用程序(并保留所有 memory/context)?
LLDB: restart an appliation in the same session (and keeping all memory/context) upon hitting a signal?
假设我正在执行一个函数,LLDB 因段错误(错误的内存访问)而停止。
是否可以清除信号,return到main
,然后重新执行一切? (如果代码进行了任何状态更改,我希望保留这些更改)。
我不是要在新会话中终止并重新启动进程。
(不确定这是否有助于澄清问题,但对于那些熟悉 Java 调试器的人来说,有一个“Drop to frame”的概念,它基本上弹出前 x 个帧,然后重新启动执行)
更新: 使用 thread return
后的更多详细信息,建议在下面的答案中提出。
使用 thread return
并调整 pc
确实让我回到了之前的执行点,但信号未清除,lldb 拒绝继续。
(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
代码:
#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;
}
lldb 有一个 thread return
命令可以从堆栈中弹出帧。这将 return 到您 return 进入的堆栈帧中处于活动状态的调用之后的某个点,因此您必须使用 thread jump
将 pc 放回调用站点。
这样做会使内存状态保持完整,因此从全局或静态变量访问的任何内容都将保留其状态。 thread return
是相当蛮力的,但是,它盲目地展开堆栈,它不会尝试通过所有清理来模拟异常抛出。因此,任何堆栈分配的对象都将被搁置而不是被销毁,如果您 return 超过了发布时间,您很可能最终会得到过度保留的对象。
此外,如果您的程序是多线程的,则必须注意其他线程尚未从您正在展开的线程传递堆栈分配的对象。这些将在您重新执行时重新分配,因此两个线程将具有不同的副本。
无论如何,虽然可能,但这不是通用技术,如果您使用它,您希望对您所看到的内容持怀疑态度,或者您可能会花时间追寻展开的工件而不是学习任何有用的东西.
假设我正在执行一个函数,LLDB 因段错误(错误的内存访问)而停止。
是否可以清除信号,return到main
,然后重新执行一切? (如果代码进行了任何状态更改,我希望保留这些更改)。
我不是要在新会话中终止并重新启动进程。
(不确定这是否有助于澄清问题,但对于那些熟悉 Java 调试器的人来说,有一个“Drop to frame”的概念,它基本上弹出前 x 个帧,然后重新启动执行)
更新: 使用 thread return
后的更多详细信息,建议在下面的答案中提出。
使用 thread return
并调整 pc
确实让我回到了之前的执行点,但信号未清除,lldb 拒绝继续。
(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
代码:
#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;
}
lldb 有一个 thread return
命令可以从堆栈中弹出帧。这将 return 到您 return 进入的堆栈帧中处于活动状态的调用之后的某个点,因此您必须使用 thread jump
将 pc 放回调用站点。
这样做会使内存状态保持完整,因此从全局或静态变量访问的任何内容都将保留其状态。 thread return
是相当蛮力的,但是,它盲目地展开堆栈,它不会尝试通过所有清理来模拟异常抛出。因此,任何堆栈分配的对象都将被搁置而不是被销毁,如果您 return 超过了发布时间,您很可能最终会得到过度保留的对象。
此外,如果您的程序是多线程的,则必须注意其他线程尚未从您正在展开的线程传递堆栈分配的对象。这些将在您重新执行时重新分配,因此两个线程将具有不同的副本。
无论如何,虽然可能,但这不是通用技术,如果您使用它,您希望对您所看到的内容持怀疑态度,或者您可能会花时间追寻展开的工件而不是学习任何有用的东西.