как правильно работать с ucontext_t?

Из исходного кода 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 */
}

из комментария мы знаем, что он был разработан для перезаписывания подписи адресом вызывающего абонента, но есть ли какой-нибудь намек на это? Я смоделировал сигнал 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 он просто возвращает uc->uc_mcontext.gregs[14] на платформе i386, зачем это делать? и почему 14 а не другие(было 19 элементов)?


person coanor    schedule 20.09.2013    source источник


Ответы (1)


Я полагаю, что "14" - это регистр EIP, поэтому текущий указатель инструкций (также известный как счетчик программ), который является отправной точкой, вам нужно знать, где вы находились в стеке вызовов, когда был получен сигнал.

Возможно, вам будет полезно посмотреть, как V8 использует это для своего профилировщика выборки: http://code.google.com/p/v8/source/browse/branches/bleeding_edge/src/sampler.cc?spec=svn16109&r=16109

person Maks    schedule 16.10.2013