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 超过了发布时间,您很可能最终会得到过度保留的对象。

此外,如果您的程序是多线程的,则必须注意其他线程尚未从您正在展开的线程传递堆栈分配的对象。这些将在您重新执行时重新分配,因此两个线程将具有不同的副本。

无论如何,虽然可能,但这不是通用技术,如果您使用它,您希望对您所看到的内容持怀疑态度,或者您可能会花时间追寻展开的工件而不是学习任何有用的东西.