SORU
30 HAZİRAN 2010, ÇARŞAMBA


Nasıl'daha iyi, program gdb çağırmak için s stacktrace baskı?

Şimdi böyle bir fonksiyon kullanıyorum:

#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

void print_trace() {
    char pid_buf[30];
    sprintf(pid_buf, "--pid=%d", getpid());
    char name_buf[512];
    name_buf[readlink("/proc/self/exe", name_buf, 511)]=0;
    int child_pid = fork();
    if (!child_pid) {           
        dup2(2,1); // redirect output to stderr
        fprintf(stdout,"stack trace for %s pid=%s\n",name_buf,pid_buf);
        execlp("gdb", "gdb", "--batch", "-n", "-ex", "thread", "-ex", "bt", name_buf, pid_buf, NULL);
        abort(); /* If gdb failed to start */
    } else {
        waitpid(child_pid,NULL,0);
    }
}

Ve çıkış print_trace detayları görüyorum.

Bunu yapmanın başka yolları nelerdir?

CEVAP
6 Ocak 2011, PERŞEMBE


Ayrıca satır numaralarını görmek istiyorum (şimdi silinmiş) benim diğer cevap bahsettiğiniz. Uygulamanız içinden gdb başlatırken bunu nasıl emin değilim.

Ama fonksiyon isimleri ve ilgili satır numaraları ile birlikte basit bir stacktrace yazdırmak için birkaç yöntem paylaşacağımgdb kullanmadan. Çoğu geldiçok güzelLinux Journal makale:

  • Yöntem #1:

İlk yöntem yaymak için. baskı ve günlüğü iletileri ile sırayla yürütme yolu gösterecek. Bir yer karmaşık bir program, bu seçenek olabilir hantal olmak can sıkıcı olsa bile, bazı GCC-belirli yardımıyla makrolar, biraz basitleştirilmiş olabilir. Örneğin, hata ayıklama bir makro düşünün gibi:

 #define TRACE_MSG fprintf(stderr, __FUNCTION__     \
                          "() [%s:%d] here I am\n", \
                          __FILE__, __LINE__)

Bu makro hızlı bir şekilde yaymak kesim tarafından program boyunca ve yapıştırma. İhtiyacın olduğunda artık, anahtarı sadece kapalı no-op için bunu kullandınız.

  • Yöntem #2:(Satır Numaraları, ama ben yöntemi 4) hakkında bir şey yazmıyor

Bir yığın almak için daha güzel bir şekilde geri iz, ancak, bazı kullanmaktır özel destek fonksiyonları tarafından sağlanan abone olarak giriş. Anahtar bir geri iz(), hangi çerçeveleri yığını gider arama sözcüğün ve bir dizi sağlar programı adresi dönüş. Daha sonra harita olabilir bir vücut için her adresi kodunuzda belirli bir fonksiyon tarafından nesne bir göz ile dosya olması nm komutu. Ya, bunu yapabilirsin daha basit yolu ()--backtrace_symbols. Bu işlev listesi dönüştürür tarafından döndürülen dönüş adresleri, () geri iz dizeleri bir liste halinde, her bir işlevin adını içeren işlevi içinde mahsup ve iade adresi. Dizeleri listesidir senin yığınından ayrılan alan (gibi malloc dedin()), siz de edin () ücretsiz işi en kısa sürede .

Sayfa source code örnekler beri okumaya teşvik ediyorum. Bir işlev adı için bir adres dönüştürmek için uygulama ile derleme gerekir-rdynamicseçeneği.

  • Yöntem #3:(Yöntem 2 yapmanın daha iyi bir yolu)

Daha kullanışlı bir uygulama bu teknik bir yığın koyarak bir sinyal içinde geri iz işleyicisi ve ikinci catch tüm "kötü" programınızı alabilir sinyalleri (SIGSEGV, SIGBUS, SİNYALİ ve ÇAĞRISI gibi). Programınızı bu şekilde, ne yazık ki çöküyor ve sen değil bir hata ayıklayıcı ile çalışan, bir yığın izleme ve nereden elde hatam oldu. Bu teknik aynı zamanda senin nerede olduğunu anlamak için kullanılabilir program durur diye döngü. yanıt

Bu teknik uygulaması here mevcuttur.

  • Yöntem #4:

Yöntem #3 satır numaralarını yazdırmak için yaptığım küçük bir gelişme. Bu yöntem #2 de çalışması için kopyalanmış olabilir.

Temelde, kullandığı 18**addr2lineiçin

dosya adları, adresleri ve dönüştürmek Satır Numaraları.

Kaynak kodu aşağıda tüm yerel işlevler için Satır Numaraları yazdırır. Eğer başka bir kitaplık işlevi çağrılırsa, dosya isimleri yerine ??:0 Bir çift görebilirsiniz.

#include <stdio.h>
#include <signal.h>
#include <stdio.h>
#include <signal.h>
#include <execinfo.h>

void bt_sighandler(int sig, struct sigcontext ctx) {

  void *trace[16];
  char **messages = (char **)NULL;
  int i, trace_size = 0;

  if (sig == SIGSEGV)
    printf("Got signal %d, faulty address is %p, "
           "from %p\n", sig, ctx.cr2, ctx.eip);
  else
    printf("Got signal %d\n", sig);

  trace_size = backtrace(trace, 16);
  /* overwrite sigaction with caller's address */
  trace[1] = (void *)ctx.eip;
  messages = backtrace_symbols(trace, trace_size);
  /* skip first stack frame (points here) */
  printf("[bt] Execution path:\n");
  for (i=1; i<trace_size;   i)
  {
    printf("[bt] #%d %s\n", i, messages[i]);

    /* find first occurence of '(' or ' ' in message[i] and assume
     * everything before that is the file name. (Don't go beyond 0 though
     * (string terminator)*/
    size_t p = 0;
    while(messages[i][p] != '(' && messages[i][p] != ' '
            && messages[i][p] != 0)
          p;

    char syscom[256];
    sprintf(syscom,"addr2line %p -e %.*s", trace[i], p, messages[i]);
        //last parameter is the file name of the symbol
    system(syscom);
  }

  exit(0);
}


int func_a(int a, char b) {

  char *p = (char *)0xdeadbeef;

  a = a   b;
  *p = 10;  /* CRASH here!! */

  return 2*a;
}


int func_b() {

  int res, a = 5;

  res = 5   func_a(a, 't');

  return res;
}


int main() {

  /* Install our signal handler */
  struct sigaction sa;

  sa.sa_handler = (void *)bt_sighandler;
  sigemptyset(&sa.sa_mask);
  sa.sa_flags = SA_RESTART;

  sigaction(SIGSEGV, &sa, NULL);
  sigaction(SIGUSR1, &sa, NULL);
  /* ... add any other signal here */

  /* Do something */
  printf("%d\n", func_b());
}

Derlenmiş kodu: gcc sighandler.c -o sighandler -rdynamic

Program çıktıları:

Got signal 11, faulty address is 0xdeadbeef, from 0x8048975
[bt] Execution path:
[bt] #1 ./sighandler(func_a 0x1d) [0x8048975]
/home/karl/workspace/stacktrace/sighandler.c:44
[bt] #2 ./sighandler(func_b 0x20) [0x804899f]
/home/karl/workspace/stacktrace/sighandler.c:54
[bt] #3 ./sighandler(main 0x6c) [0x8048a16]
/home/karl/workspace/stacktrace/sighandler.c:74
[bt] #4 /lib/tls/i686/cmov/libc.so.6(__libc_start_main 0xe6) [0x3fdbd6]
??:0
[bt] #5 ./sighandler() [0x8048781]
??:0

Güncelleme 2012/04/28yeni linux çekirdek sürümleri için sigaction yukarıdaki imza kullanılmıyor. Ayrıca this answer yürütülebilir adı kapma tarafından biraz geliştirdim. Burada up to date version bir:

char* exe = 0;

int initialiseExecutableName() 
{
    char link[1024];
    exe = new char[1024];
    snprintf(link,sizeof link,"/proc/%d/exe",getpid());
    if(readlink(link,exe,sizeof link)==-1) {
        fprintf(stderr,"ERRORRRRR\n");
        exit(1);
    }
    printf("Executable name initialised: %s\n",exe);
}

const char* getExecutableName()
{
    if (exe == 0)
        initialiseExecutableName();
    return exe;
}

/* get REG_EIP from ucontext.h */
#define __USE_GNU
#include <ucontext.h>

void bt_sighandler(int sig, siginfo_t *info,
                   void *secret) {

  void *trace[16];
  char **messages = (char **)NULL;
  int i, trace_size = 0;
  ucontext_t *uc = (ucontext_t *)secret;

  /* Do something useful with siginfo_t */
  if (sig == SIGSEGV)
    printf("Got signal %d, faulty address is %p, "
           "from %p\n", sig, info->si_addr, 
           uc->uc_mcontext.gregs[REG_EIP]);
  else
    printf("Got signal %d#92;\n", sig);

  trace_size = backtrace(trace, 16);
  /* overwrite sigaction with caller's address */
  trace[1] = (void *) uc->uc_mcontext.gregs[REG_EIP];

  messages = backtrace_symbols(trace, trace_size);
  /* skip first stack frame (points here) */
  printf("[bt] Execution path:#92;\n");
  for (i=1; i<trace_size;   i)
  {
    printf("[bt] %s#92;\n", messages[i]);

    /* find first occurence of '(' or ' ' in message[i] and assume
     * everything before that is the file name. (Don't go beyond 0 though
     * (string terminator)*/
    size_t p = 0;
    while(messages[i][p] != '(' && messages[i][p] != ' '
            && messages[i][p] != 0)
          p;

    char syscom[256];
    sprintf(syscom,"addr2line %p -e %.*s", trace[i] , p, messages[i] );
           //last parameter is the filename of the symbol
    system(syscom);

  }
  exit(0);
}

ve bu gibi yeniden:

int main() {

  /* Install our signal handler */
  struct sigaction sa;

  sa.sa_sigaction = (void *)bt_sighandler;
  sigemptyset (&sa.sa_mask);
  sa.sa_flags = SA_RESTART | SA_SIGINFO;

  sigaction(SIGSEGV, &sa, NULL);
  sigaction(SIGUSR1, &sa, NULL);
  /* ... add any other signal here */

  /* Do something */
  printf("%d#92;n", func_b());

}

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • IGN

    IGN

    19 EYLÜL 2006
  • Matt Davis

    Matt Davis

    4 ŞUBAT 2006
  • Press Start

    Press Start

    4 HAZİRAN 2006