c - `ucontext_t` 的正确操作方法是什么?

标签 c callstack ucontext

Redis 的源代码,在文件 src/debug.c 中,它使用 backtrace() 来记录调用堆栈。在这些操作中,我注意到 getMcontextEip(),在 Linux 中似乎是这样的:

  static void *getMcontextEip(ucontext_t *uc) {
    /* Linux */
  #if defined(__i386__)
    return (void*) uc->uc_mcontext.gregs[14]; /* Linux 32 */
  #elif defined(__X86_64__) || defined(__x86_64__)
    return (void*) uc->uc_mcontext.gregs[16]; /* Linux 64 */
  #elif defined(__ia64__)                     /* Linux IA64 */
    return (void*) uc->uc_mcontext.sc_ip;
  #endif
  }

所有这些背后的机制是:当有信号(即 SIGFPE)时,它将被捕获并尝试将调用堆栈记录到文件中:

void log_stack_trace(ucontext_t *uc) {
    void *trace[100];
    int fd = open(file);
    int trace_size = backtrace(trace, 100); /* get call stack */

    /* overwrite sigaction with caller's address */
    if (getMcontextEip(uc) != NULL)
        trace[1] = getMcontextEip(uc);

    backtrace_symbols_fd(trace, trace_size, fd); /* log to file */
}

从评论中,我们知道它旨在用调用者的地址覆盖 sigaction,但是是否有任何提示可以做到这一点?我已经模拟了一个 SIGFPE 信号并在 GDB 中调试它,ucontext_t 看起来像这样:

(gdb) p *uc
$6 = {
  uc_flags = 0, 
  uc_link = 0x0, 
  uc_stack = {
    ss_sp = 0x0, 
    ss_flags = 2, 
    ss_size = 0
  }, 
  uc_mcontext = {
    gregs = {51, 0, 123, 123, 0, 0, -1073745320, -1073745376, -1208258560, 0, -1073745852, 5, 0, 0, 134514547, 115, 2163270, -1073745376, 123}, 
    fpregs = 0xbfffefb0, 
    oldmask = 0, 
    cr2 = 0
  }, 
  uc_sigmask = {
    __val = {0, 0, 44472, 8441088, 0, 0, 4294902655, 4294901760, 4294967295, 0 <repeats 23 times>}
  }, 
  __fpregs_mem = {
    cw = 0, 
    sw = 0, 
    tag = 895, 
    ipoff = 0, 
    cssel = 0, 
    dataoff = 0, 
    datasel = 0, 
    _st = {{
        significand = {0, 0, 8064, 0}, 
        exponent = 0
      }, {
        significand = {0, 0, 0, 0}, 
        exponent = 0
      }, {
        significand = {0, 0, 0, 0}, 
        exponent = 0
      }, {
        significand = {0, 0, 0, 0}, 
        exponent = 0
      }, {
        significand = {0, 0, 0, 0}, 
        exponent = 0
      }, {
        significand = {0, 0, 0, 0}, 
        exponent = 0
      }, {
        significand = {0, 0, 0, 0}, 
        exponent = 0
      }, {
        significand = {0, 0, 0, 0}, 
        exponent = 0
      }}, 
    status = 0
  }
}

getMcontextEip中,它只是返回i386平台上的uc->uc_mcontext.gregs[14],这是为什么呢?为什么是 14 而不是其他(有 19 个元素)?

最佳答案

我相信“14”是 EIP 寄存器,因此当前指令指针(又名程序计数器)是您需要知道在收到信号时您在调用堆栈中的位置的起点。

您可能会发现 V8 如何将其用于其采样分析器很有用: http://code.google.com/p/v8/source/browse/branches/bleeding_edge/src/sampler.cc?spec=svn16109&r=16109

关于c - `ucontext_t` 的正确操作方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18913859/

相关文章:

c - 使用 semaphore.h 和 ucontext.h 找不到这样的文件

在使用 getchar() 的普通程序中未检测到 CTRL+D (EOF)?

c - C语言输出字符串的三元运算

文件范围内的编译器错误 "error: variably modified ' string'”

c - Malloc 打印不正确?

javascript - 深入了解 v8 最大堆栈帧/大小

c - 为什么 makecontext 不调用我的函数?

c - 类似函数的行为,无需使用任何调用堆栈

node.js - Go 有一个 "infinite call stack"等价物吗?

c++ - 考虑 makecontext() 什么是 uc_stack.ss_size 好?