วิธีสร้าง stacktrace โดยอัตโนมัติเมื่อโปรแกรมของฉันขัดข้อง


590

ฉันกำลังทำงานบน Linux ด้วยคอมไพเลอร์ GCC เมื่อโปรแกรม C ++ ของฉันล่มฉันต้องการให้สแต็คสร้างเทรซ

โปรแกรมของฉันถูกเรียกใช้โดยผู้ใช้หลายคนและยังทำงานบน Linux, Windows และ Macintosh (ทุกรุ่นรวบรวมโดยใช้gcc)

ฉันต้องการให้โปรแกรมของฉันสามารถสร้างการติดตามสแต็กเมื่อเกิดข้อผิดพลาดและในครั้งต่อไปที่ผู้ใช้เรียกใช้มันจะถามพวกเขาว่าการส่งสแต็กติดตามมาให้ฉันได้หรือไม่ดังนั้นฉันจึงสามารถติดตามปัญหาได้ ฉันสามารถจัดการการส่งข้อมูลให้ฉันได้ แต่ฉันไม่รู้วิธีสร้างสตริงการติดตาม ความคิดใด ๆ


4
backtrace และ backtrace_symbols_fd ไม่เป็น async-signal-safe คุณไม่ควรใช้ฟังก์ชั่นเหล่านี้ในตัวจัดการสัญญาณ
Parag Bafna

10
backtrace_symbols เรียก malloc ดังนั้นจะต้องไม่ใช้ในตัวจัดการสัญญาณ อีกสองฟังก์ชัน (backtrace และ backtrace_symbols_fd) ไม่มีปัญหานี้และมักใช้ในตัวจัดการสัญญาณ
cmccabe

3
@cmccabe ที่ไม่ถูกต้อง backtrace_symbols_fd มักจะไม่เรียก malloc แต่อาจมีบางสิ่งที่ผิดพลาดในบล็อก catch_error
Sam Saffron

6
มัน "อาจ" ในแง่ที่ไม่มี POSIX spec สำหรับ backtrace_symbols_fd (หรือ backtrace ใด ๆ ); แต่ backtrace_symbols_fd GNU / Linux ถูกระบุให้ malloc ไม่เคยโทรตามlinux.die.net/man/3/backtrace_symbols_fd ดังนั้นจึงปลอดภัยที่จะสมมติว่าจะไม่เรียก malloc บน Linux
codetaku

มันพังได้อย่างไร
Ciro Santilli 郝海东冠状病六四事件法轮功

คำตอบ:


509

สำหรับ Linux และฉันเชื่อว่า Mac OS X หากคุณใช้ gcc หรือคอมไพเลอร์ใด ๆ ที่ใช้ glibc คุณสามารถใช้ฟังก์ชั่น backtrace () execinfo.hเพื่อพิมพ์ stacktrace และออกอย่างสง่างามเมื่อคุณได้รับการแบ่งเซ็กเมนต์ เอกสารที่สามารถพบได้ในคู่มือการ libc

ต่อไปนี้เป็นตัวอย่างโปรแกรมที่ติดตั้งSIGSEGVตัวจัดการและพิมพ์สแต็คสแตร็กstderrเมื่อเป็น segfaults baz()ฟังก์ชั่นที่นี่เป็นสาเหตุที่ทำให้ segfault ที่เรียกตัวจัดการ:

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


void handler(int sig) {
  void *array[10];
  size_t size;

  // get void*'s for all entries on the stack
  size = backtrace(array, 10);

  // print out all the frames to stderr
  fprintf(stderr, "Error: signal %d:\n", sig);
  backtrace_symbols_fd(array, size, STDERR_FILENO);
  exit(1);
}

void baz() {
 int *foo = (int*)-1; // make a bad pointer
  printf("%d\n", *foo);       // causes segfault
}

void bar() { baz(); }
void foo() { bar(); }


int main(int argc, char **argv) {
  signal(SIGSEGV, handler);   // install our handler
  foo(); // this will call foo, bar, and baz.  baz segfaults.
}

การคอมไพล์ด้วย-g -rdynamicทำให้คุณได้รับข้อมูลสัญลักษณ์ในผลลัพธ์ของคุณซึ่ง glibc สามารถใช้เพื่อสร้าง stacktrace ที่ดี:

$ gcc -g -rdynamic ./test.c -o test

การดำเนินการนี้ทำให้คุณได้รับผลลัพธ์นี้:

$ ./test
Error: signal 11:
./test(handler+0x19)[0x400911]
/lib64/tls/libc.so.6[0x3a9b92e380]
./test(baz+0x14)[0x400962]
./test(bar+0xe)[0x400983]
./test(foo+0xe)[0x400993]
./test(main+0x28)[0x4009bd]
/lib64/tls/libc.so.6(__libc_start_main+0xdb)[0x3a9b91c4bb]
./test[0x40086a]

นี่แสดงให้เห็นถึงโหลดโมดูลออฟเซ็ตและฟังก์ชั่นที่แต่ละเฟรมในสแต็กมาจาก ที่นี่คุณสามารถดูจัดการสัญญาณที่ด้านบนของสแต็คและฟังก์ชั่น libc ก่อนที่mainนอกเหนือไปmain, foo, และbarbaz


53
นอกจากนี้ยังมี /lib/libSegFault.so ซึ่งคุณสามารถใช้กับ LD_PRELOAD
CesarB

6
ดูเหมือนว่าสองรายการแรกในเอาต์พุต backtrace ของคุณมีที่อยู่ผู้ส่งคืนภายในตัวจัดการสัญญาณและอาจเป็นหนึ่งในด้านsigaction()ใน libc ในขณะที่ backtrace ของคุณดูเหมือนจะถูกต้อง แต่บางครั้งฉันพบว่าจำเป็นต้องมีขั้นตอนเพิ่มเติมเพื่อให้แน่ใจว่าตำแหน่งที่แท้จริงของความผิดพลาดปรากฏใน backtrace เนื่องจากsigaction()เคอร์เนลสามารถเขียนทับได้
jschmier

9
จะเกิดอะไรขึ้นถ้าความผิดพลาดนั้นมาจากภายใน malloc? คุณจะไม่กดปุ่มล็อคแล้วค้างเมื่อ "backtrace" พยายามจัดสรรหน่วยความจำหรือไม่?
Mattias Nilsson

7
catchsegvไม่ใช่สิ่งที่ OP ต้องการ แต่ยอดเยี่ยมสำหรับการจับข้อบกพร่องของการแบ่งเซ็กเมนต์และรับข้อมูลทั้งหมด
Matt Clarkson

8
สำหรับ ARM ฉันต้องคอมไพล์ด้วย -funwind-tables มิฉะนั้นความลึกของสแต็กของฉันคือ 1 เสมอ (ว่าง)
jfritz42

128

มันง่ายยิ่งกว่า "man backtrace" มีไลบรารี่ขนาดเล็ก (เฉพาะ GNU) ที่แจกจ่ายด้วย glibc เป็น libSegFault.so ซึ่งฉันเชื่อว่าเขียนโดย Ulrich Drepper เพื่อสนับสนุนโปรแกรม catchsegv (ดู "man catchsegv")

สิ่งนี้ทำให้เรามีความเป็นไปได้ 3 อย่าง แทนที่จะเรียกใช้ "โปรแกรม -o hai":

  1. ทำงานภายใน catchsegv:

    $ catchsegv program -o hai
  2. เชื่อมโยงกับ libSegFault ขณะใช้งานจริง:

    $ LD_PRELOAD=/lib/libSegFault.so program -o hai
  3. เชื่อมโยงกับ libSegFault ณ เวลารวบรวม:

    $ gcc -g1 -lSegFault -o program program.cc
    $ program -o hai

ในทั้ง 3 กรณีคุณจะได้รับ backtraces ที่ชัดเจนโดยมีการปรับให้เหมาะสมน้อยลง (gcc -O0 หรือ -O1) และสัญลักษณ์การดีบัก (gcc -g) มิฉะนั้นคุณอาจจะจบลงด้วยกองที่อยู่หน่วยความจำ

คุณสามารถจับสัญญาณเพิ่มเติมสำหรับการติดตามสแต็กด้วยสิ่งต่อไปนี้

$ export SEGFAULT_SIGNALS="all"       # "all" signals
$ export SEGFAULT_SIGNALS="bus abrt"  # SIGBUS and SIGABRT

ผลลัพธ์จะมีลักษณะดังนี้ (สังเกตว่า backtrace ที่ด้านล่าง):

*** Segmentation fault Register dump:

 EAX: 0000000c   EBX: 00000080   ECX:
00000000   EDX: 0000000c  ESI:
bfdbf080   EDI: 080497e0   EBP:
bfdbee38   ESP: bfdbee20

 EIP: 0805640f   EFLAGS: 00010282

 CS: 0073   DS: 007b   ES: 007b   FS:
0000   GS: 0033   SS: 007b

 Trap: 0000000e   Error: 00000004  
OldMask: 00000000  ESP/signal:
bfdbee20   CR2: 00000024

 FPUCW: ffff037f   FPUSW: ffff0000  
TAG: ffffffff  IPOFF: 00000000  
CSSEL: 0000   DATAOFF: 00000000  
DATASEL: 0000

 ST(0) 0000 0000000000000000   ST(1)
0000 0000000000000000  ST(2) 0000
0000000000000000   ST(3) 0000
0000000000000000  ST(4) 0000
0000000000000000   ST(5) 0000
0000000000000000  ST(6) 0000
0000000000000000   ST(7) 0000
0000000000000000

Backtrace:
/lib/libSegFault.so[0xb7f9e100]
??:0(??)[0xb7fa3400]
/usr/include/c++/4.3/bits/stl_queue.h:226(_ZNSt5queueISsSt5dequeISsSaISsEEE4pushERKSs)[0x805647a]
/home/dbingham/src/middle-earth-mud/alpha6/src/engine/player.cpp:73(_ZN6Player5inputESs)[0x805377c]
/home/dbingham/src/middle-earth-mud/alpha6/src/engine/socket.cpp:159(_ZN6Socket4ReadEv)[0x8050698]
/home/dbingham/src/middle-earth-mud/alpha6/src/engine/socket.cpp:413(_ZN12ServerSocket4ReadEv)[0x80507ad]
/home/dbingham/src/middle-earth-mud/alpha6/src/engine/socket.cpp:300(_ZN12ServerSocket4pollEv)[0x8050b44]
/home/dbingham/src/middle-earth-mud/alpha6/src/engine/main.cpp:34(main)[0x8049a72]
/lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe5)[0xb7d1b775]
/build/buildd/glibc-2.9/csu/../sysdeps/i386/elf/start.S:122(_start)[0x8049801]

หากคุณต้องการทราบรายละเอียดเต็มไปด้วยเลือดแหล่งที่ดีที่สุดคือแหล่งที่มา: ดูhttp://sourceware.org/git/?p=glibc.git;a=blob;f=debug/segfault.cและไดเรกทอรีหลักhttp://sourceware.org/git/?p=glibc.git;a=tree;f=debug


1
"ความเป็นไปได้ 3. เชื่อมโยงกับ libSegFault ณ เวลารวบรวม" ไม่ทำงาน
HHK

5
@crafter: คุณหมายถึงอะไร "ไม่ทำงาน" คุณได้ลองทำอะไรในภาษา / คอมไพเลอร์ / Toolchain / การกระจาย / ฮาร์ดแวร์? มันไม่ได้รวบรวมหรือไม่ เพื่อตรวจจับข้อผิดพลาด? เพื่อผลิตผลทั้งหมดหรือไม่ เพื่อให้ได้ผลผลิตที่ยากต่อการใช้งาน? ขอบคุณสำหรับรายละเอียดมันจะช่วยให้ทุกคน
Stéphane Gourichon

1
'แหล่งที่ดีที่สุดน่าเสียดายที่แหล่งที่มา' ... หวังว่าสักวันหน้า man ของ catchsegv จะพูดถึง SEGFAULT_SIGNALS ก่อนหน้านี้มีคำตอบที่จะอ้างถึง
greggo

ฉันไม่อยากจะเชื่อเลยว่าฉันเขียนโปรแกรม C มา 5 ปีแล้วและไม่เคยได้ยินเรื่องนี้: /
DavidMFrey

6
@ StéphaneGourichon @HansKratz ในการเชื่อมโยงกับ libSegFault คุณจะต้องเพิ่ม-Wl,--no-as-neededในธงคอมไพเลอร์ มิฉะนั้นldจะไม่เชื่อมโยงกับlibSegFaultเพราะมันรู้ว่าไบนารีไม่ได้ใช้สัญลักษณ์ใด ๆ
ฟิลลิป

122

ลินุกซ์

ในขณะที่การใช้ฟังก์ชั่น backtrace () ใน execinfo.h เพื่อพิมพ์ stacktrace และออกอย่างสง่างามเมื่อคุณได้รับข้อผิดพลาดการแบ่งส่วนได้รับการแนะนำแล้วฉันไม่เห็นการกล่าวถึงความซับซ้อนที่จำเป็นเพื่อให้แน่ใจว่า ความผิดพลาด (อย่างน้อยสำหรับบางสถาปัตยกรรม - x86 & ARM)

สองรายการแรกในกลุ่มเฟรมสแต็กเมื่อคุณเข้าสู่ตัวจัดการสัญญาณมีที่อยู่ผู้ส่งคืนภายในตัวจัดการสัญญาณและอีกหนึ่งรายการภายใน sigaction () ใน libc กรอบสแต็คของฟังก์ชั่นสุดท้ายที่เรียกว่าสัญญาณ (ซึ่งเป็นที่ตั้งของความผิดพลาด) หายไป

รหัส

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#ifndef __USE_GNU
#define __USE_GNU
#endif

#include <execinfo.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ucontext.h>
#include <unistd.h>

/* This structure mirrors the one found in /usr/include/asm/ucontext.h */
typedef struct _sig_ucontext {
 unsigned long     uc_flags;
 struct ucontext   *uc_link;
 stack_t           uc_stack;
 struct sigcontext uc_mcontext;
 sigset_t          uc_sigmask;
} sig_ucontext_t;

void crit_err_hdlr(int sig_num, siginfo_t * info, void * ucontext)
{
 void *             array[50];
 void *             caller_address;
 char **            messages;
 int                size, i;
 sig_ucontext_t *   uc;

 uc = (sig_ucontext_t *)ucontext;

 /* Get the address at the time the signal was raised */
#if defined(__i386__) // gcc specific
 caller_address = (void *) uc->uc_mcontext.eip; // EIP: x86 specific
#elif defined(__x86_64__) // gcc specific
 caller_address = (void *) uc->uc_mcontext.rip; // RIP: x86_64 specific
#else
#error Unsupported architecture. // TODO: Add support for other arch.
#endif

 fprintf(stderr, "signal %d (%s), address is %p from %p\n", 
  sig_num, strsignal(sig_num), info->si_addr, 
  (void *)caller_address);

 size = backtrace(array, 50);

 /* overwrite sigaction with caller's address */
 array[1] = caller_address;

 messages = backtrace_symbols(array, size);

 /* skip first stack frame (points here) */
 for (i = 1; i < size && messages != NULL; ++i)
 {
  fprintf(stderr, "[bt]: (%d) %s\n", i, messages[i]);
 }

 free(messages);

 exit(EXIT_FAILURE);
}

int crash()
{
 char * p = NULL;
 *p = 0;
 return 0;
}

int foo4()
{
 crash();
 return 0;
}

int foo3()
{
 foo4();
 return 0;
}

int foo2()
{
 foo3();
 return 0;
}

int foo1()
{
 foo2();
 return 0;
}

int main(int argc, char ** argv)
{
 struct sigaction sigact;

 sigact.sa_sigaction = crit_err_hdlr;
 sigact.sa_flags = SA_RESTART | SA_SIGINFO;

 if (sigaction(SIGSEGV, &sigact, (struct sigaction *)NULL) != 0)
 {
  fprintf(stderr, "error setting signal handler for %d (%s)\n",
    SIGSEGV, strsignal(SIGSEGV));

  exit(EXIT_FAILURE);
 }

 foo1();

 exit(EXIT_SUCCESS);
}

เอาท์พุต

signal 11 (Segmentation fault), address is (nil) from 0x8c50
[bt]: (1) ./test(crash+0x24) [0x8c50]
[bt]: (2) ./test(foo4+0x10) [0x8c70]
[bt]: (3) ./test(foo3+0x10) [0x8c8c]
[bt]: (4) ./test(foo2+0x10) [0x8ca8]
[bt]: (5) ./test(foo1+0x10) [0x8cc4]
[bt]: (6) ./test(main+0x74) [0x8d44]
[bt]: (7) /lib/libc.so.6(__libc_start_main+0xa8) [0x40032e44]

ความเป็นอันตรายทั้งหมดของการเรียกฟังก์ชั่น backtrace () ในตัวจัดการสัญญาณยังคงมีอยู่และไม่ควรมองข้าม แต่ฉันพบว่าการทำงานที่ฉันอธิบายไว้ที่นี่ค่อนข้างมีประโยชน์ในการดีบักการขัดข้อง

เป็นสิ่งสำคัญที่จะต้องทราบว่าตัวอย่างที่ฉันให้ไว้ได้รับการพัฒนา / ทดสอบบน Linux สำหรับ x86 ฉันยังประสบความสำเร็จในการดำเนินการเกี่ยวกับเรื่องนี้โดยใช้ ARM แทน uc_mcontext.arm_pcuc_mcontext.eip

นี่คือลิงค์ไปยังบทความที่ฉันได้เรียนรู้รายละเอียดสำหรับการดำเนินการนี้: http://www.linuxjournal.com/article/6391


11
บนระบบที่ใช้ GNU ld อย่าลืมคอมไพล์ด้วย-rdynamicเพื่อแนะนำให้ linker เพิ่มสัญลักษณ์ทั้งหมดไม่เพียง แต่ใช้กับตารางสัญลักษณ์แบบไดนามิก วิธีนี้ทำให้backtrace_symbols()สามารถแปลงที่อยู่เป็นชื่อฟังก์ชั่นได้
jschmier

1
นอกจากนี้คุณต้องเพิ่มตัวเลือก "-mapcs-frame" ในบรรทัดคำสั่งของ GCC เพื่อสร้างสแต็กเฟรมบนแพลตฟอร์ม ARM
qehgt

3
อาจจะสายเกินไป แต่เราสามารถใช้addr2lineคำสั่งเพื่อรับบรรทัดที่ตรงที่เกิดความผิดพลาดได้หรือไม่?
กระตือรือร้น

4
เมื่อวันที่เมื่อเร็ว ๆ นี้สร้างของไม่ได้มีข้อมูลที่มีชื่อว่าglibc uc_mcontext eipขณะนี้มีอาร์เรย์ที่ต้องทำดัชนีซึ่งuc_mcontext.gregs[REG_EIP]เทียบเท่ากัน
mmlb

6
สำหรับ ARM backtraces ของฉันมีความลึก 1 เสมอจนกว่าฉันจะเพิ่มตัวเลือก -funwind-tables เข้ากับคอมไพเลอร์
jfritz42

84

แม้ว่าจะมีการให้คำตอบที่ถูกต้องซึ่งอธิบายถึงวิธีการใช้backtrace()ฟังก์ชันGNU libc 1และฉันได้ให้คำตอบของตัวเองที่อธิบายถึงวิธีการตรวจสอบย้อนหลังจากจุดจัดการสัญญาณไปยังตำแหน่งที่แท้จริงของความผิด2ฉันไม่เห็น การเอ่ยถึงการถอดสัญลักษณ์ C ++ ใด ๆ ออกมาจาก backtrace

เมื่อได้รับ backtraces จากโปรแกรม C ++ เอาต์พุตสามารถรันผ่านc++filt1เพื่อลดสถานะของสัญลักษณ์หรือโดยใช้1โดยตรงabi::__cxa_demangle

  • 1 Linux & OS X โปรดสังเกตว่าc++filtและ__cxa_demangleเฉพาะ GCC
  • 2 ลินุกซ์

ตัวอย่าง C ++ Linux ต่อไปนี้ใช้ตัวจัดการสัญญาณเดียวกับคำตอบอื่น ๆของฉันและแสดงให้เห็นถึงวิธีการที่c++filtสามารถนำมาใช้เพื่อลดความซับซ้อนของสัญลักษณ์

รหัส :

class foo
{
public:
    foo() { foo1(); }

private:
    void foo1() { foo2(); }
    void foo2() { foo3(); }
    void foo3() { foo4(); }
    void foo4() { crash(); }
    void crash() { char * p = NULL; *p = 0; }
};

int main(int argc, char ** argv)
{
    // Setup signal handler for SIGSEGV
    ...

    foo * f = new foo();
    return 0;
}

ผลลัพธ์ ( ./test):

signal 11 (Segmentation fault), address is (nil) from 0x8048e07
[bt]: (1) ./test(crash__3foo+0x13) [0x8048e07]
[bt]: (2) ./test(foo4__3foo+0x12) [0x8048dee]
[bt]: (3) ./test(foo3__3foo+0x12) [0x8048dd6]
[bt]: (4) ./test(foo2__3foo+0x12) [0x8048dbe]
[bt]: (5) ./test(foo1__3foo+0x12) [0x8048da6]
[bt]: (6) ./test(__3foo+0x12) [0x8048d8e]
[bt]: (7) ./test(main+0xe0) [0x8048d18]
[bt]: (8) ./test(__libc_start_main+0x95) [0x42017589]
[bt]: (9) ./test(__register_frame_info+0x3d) [0x8048981]

Demangled Output ( ./test 2>&1 | c++filt):

signal 11 (Segmentation fault), address is (nil) from 0x8048e07
[bt]: (1) ./test(foo::crash(void)+0x13) [0x8048e07]
[bt]: (2) ./test(foo::foo4(void)+0x12) [0x8048dee]
[bt]: (3) ./test(foo::foo3(void)+0x12) [0x8048dd6]
[bt]: (4) ./test(foo::foo2(void)+0x12) [0x8048dbe]
[bt]: (5) ./test(foo::foo1(void)+0x12) [0x8048da6]
[bt]: (6) ./test(foo::foo(void)+0x12) [0x8048d8e]
[bt]: (7) ./test(main+0xe0) [0x8048d18]
[bt]: (8) ./test(__libc_start_main+0x95) [0x42017589]
[bt]: (9) ./test(__register_frame_info+0x3d) [0x8048981]

บิลด์ต่อไปนี้สร้างขึ้นบนตัวจัดการสัญญาณจากคำตอบดั้งเดิมของฉันและสามารถแทนที่ตัวจัดการสัญญาณในตัวอย่างข้างต้นเพื่อสาธิตวิธีที่abi::__cxa_demangleสามารถใช้ในการทำให้สถานะสัญลักษณ์ลดลง ตัวจัดการสัญญาณนี้สร้างเอาต์พุต demangled เดียวกันกับตัวอย่างข้างต้น

รหัส :

void crit_err_hdlr(int sig_num, siginfo_t * info, void * ucontext)
{
    sig_ucontext_t * uc = (sig_ucontext_t *)ucontext;

    void * caller_address = (void *) uc->uc_mcontext.eip; // x86 specific

    std::cerr << "signal " << sig_num 
              << " (" << strsignal(sig_num) << "), address is " 
              << info->si_addr << " from " << caller_address 
              << std::endl << std::endl;

    void * array[50];
    int size = backtrace(array, 50);

    array[1] = caller_address;

    char ** messages = backtrace_symbols(array, size);    

    // skip first stack frame (points here)
    for (int i = 1; i < size && messages != NULL; ++i)
    {
        char *mangled_name = 0, *offset_begin = 0, *offset_end = 0;

        // find parantheses and +address offset surrounding mangled name
        for (char *p = messages[i]; *p; ++p)
        {
            if (*p == '(') 
            {
                mangled_name = p; 
            }
            else if (*p == '+') 
            {
                offset_begin = p;
            }
            else if (*p == ')')
            {
                offset_end = p;
                break;
            }
        }

        // if the line could be processed, attempt to demangle the symbol
        if (mangled_name && offset_begin && offset_end && 
            mangled_name < offset_begin)
        {
            *mangled_name++ = '\0';
            *offset_begin++ = '\0';
            *offset_end++ = '\0';

            int status;
            char * real_name = abi::__cxa_demangle(mangled_name, 0, 0, &status);

            // if demangling is successful, output the demangled function name
            if (status == 0)
            {    
                std::cerr << "[bt]: (" << i << ") " << messages[i] << " : " 
                          << real_name << "+" << offset_begin << offset_end 
                          << std::endl;

            }
            // otherwise, output the mangled function name
            else
            {
                std::cerr << "[bt]: (" << i << ") " << messages[i] << " : " 
                          << mangled_name << "+" << offset_begin << offset_end 
                          << std::endl;
            }
            free(real_name);
        }
        // otherwise, print the whole line
        else
        {
            std::cerr << "[bt]: (" << i << ") " << messages[i] << std::endl;
        }
    }
    std::cerr << std::endl;

    free(messages);

    exit(EXIT_FAILURE);
}

1
ขอบคุณสำหรับสิ่งนี้ jschmier ฉันสร้างสคริปต์ทุบตีเล็ก ๆ น้อย ๆ เพื่อป้อนผลลัพธ์ของสิ่งนี้ลงในยูทิลิตี้ addr2line ดู: stackoverflow.com/a/15801966/1797414
arr_sea

4
อย่าลืม #include <cxxabi.h>
Bamaco

1
เอกสารที่ดีและไฟล์ส่วนหัวตรงไปตรงมาได้รับการโพสต์ที่นี่ตั้งแต่ปี 2008 ... panthema.net/2008/0901-stacktrace-demangledคล้ายกับแนวทางของคุณ :)
kevinf

abi :: __ cxa_demangle ดูเหมือนจะไม่ปลอดภัยแบบสัญญาณซิงค์ดังนั้นตัวจัดการสัญญาณสามารถหยุดชะงักที่ใดที่หนึ่งใน malloc
orcy

การใช้std::cerr, free()และexit()ทุกละเมิดข้อ จำกัด กับการเรียกสายไม่ใช่ async สัญญาณปลอดภัยบนระบบ POSIX รหัสนี้จะหยุดชะงักถ้ากระบวนการของคุณล้มเหลวในการเรียกใด ๆ เช่นfree(), หรือmalloc() new detete
Andrew Henle

31

อาจคุ้มค่าที่จะดูที่Google Breakpadเครื่องมือสร้างการถ่ายโอนข้อมูลข้ามแพลตฟอร์มและเครื่องมือในการประมวลผลการถ่ายโอนข้อมูล


มันรายงานเกี่ยวกับสิ่งต่าง ๆ เช่นข้อผิดพลาดการแบ่งกลุ่ม แต่จะไม่รายงานข้อมูลใด ๆ เกี่ยวกับข้อยกเว้น C ++ ที่ไม่สามารถจัดการได้
DBedrenko

21

คุณไม่ได้ระบุระบบปฏิบัติการของคุณดังนั้นจึงเป็นการยากที่จะตอบ หากคุณใช้ระบบที่ใช้ gnu libc คุณอาจสามารถใช้ฟังก์ชัน libc backtrace()ได้

GCC ยังมีอีกสอง builtins ที่สามารถช่วยคุณ แต่ที่อาจจะหรืออาจไม่ได้รับการดำเนินการอย่างเต็มที่กับสถาปัตยกรรมของคุณและผู้ที่มีและ__builtin_frame_address __builtin_return_addressทั้งสองต้องการระดับจำนวนเต็มทันที (โดยทันทีฉันหมายความว่ามันไม่สามารถเป็นตัวแปร) หาก__builtin_frame_addressระดับที่กำหนดไม่ใช่ศูนย์คุณควรคว้าที่อยู่ผู้ส่งในระดับเดียวกัน


13

ขอบคุณสำหรับความกระตือรือร้นที่สนใจของฉันในการเพิ่มโปรแกรมอรรถประโยชน์ addr2line

ฉันได้เขียนสคริปต์ที่รวดเร็วและสกปรกเพื่อประมวลผลผลลัพธ์ของคำตอบที่ให้ไว้ที่นี่ : (ขอบคุณมาก jschmier!) โดยใช้ยูทิลิตี้ addr2line

สคริปต์ยอมรับอาร์กิวเมนต์เดี่ยว: ชื่อของไฟล์ที่มีเอาต์พุตจากยูทิลิตีของ jschmier

ผลลัพธ์ควรพิมพ์สิ่งต่อไปนี้สำหรับแต่ละระดับของการติดตาม:

BACKTRACE:  testExe 0x8A5db6b
FILE:       pathToFile/testExe.C:110
FUNCTION:   testFunction(int) 
   107  
   108           
   109           int* i = 0x0;
  *110           *i = 5;
   111      
   112        }
   113        return i;

รหัส:

#!/bin/bash

LOGFILE=$1

NUM_SRC_CONTEXT_LINES=3

old_IFS=$IFS  # save the field separator           
IFS=$'\n'     # new field separator, the end of line           

for bt in `cat $LOGFILE | grep '\[bt\]'`; do
   IFS=$old_IFS     # restore default field separator 
   printf '\n'
   EXEC=`echo $bt | cut -d' ' -f3 | cut -d'(' -f1`  
   ADDR=`echo $bt | cut -d'[' -f3 | cut -d']' -f1`
   echo "BACKTRACE:  $EXEC $ADDR"
   A2L=`addr2line -a $ADDR -e $EXEC -pfC`
   #echo "A2L:        $A2L"

   FUNCTION=`echo $A2L | sed 's/\<at\>.*//' | cut -d' ' -f2-99`
   FILE_AND_LINE=`echo $A2L | sed 's/.* at //'`
   echo "FILE:       $FILE_AND_LINE"
   echo "FUNCTION:   $FUNCTION"

   # print offending source code
   SRCFILE=`echo $FILE_AND_LINE | cut -d':' -f1`
   LINENUM=`echo $FILE_AND_LINE | cut -d':' -f2`
   if ([ -f $SRCFILE ]); then
      cat -n $SRCFILE | grep -C $NUM_SRC_CONTEXT_LINES "^ *$LINENUM\>" | sed "s/ $LINENUM/*$LINENUM/"
   else
      echo "File not found: $SRCFILE"
   fi
   IFS=$'\n'     # new field separator, the end of line           
done

IFS=$old_IFS     # restore default field separator 

12

ulimit -c <value>ตั้งค่าขีด จำกัด ขนาดไฟล์หลักบน unix โดยค่าเริ่มต้นหลัก จำกัด ขนาดไฟล์เป็น 0 คุณสามารถมองเห็นคุณค่ากับulimitulimit -a

นอกจากนี้หากคุณเรียกใช้โปรแกรมจากภายใน gdb โปรแกรมจะหยุดการทำงานของ "การละเมิดการแบ่งกลุ่ม" ( SIGSEGVโดยทั่วไปเมื่อคุณเข้าถึงหน่วยความจำที่คุณไม่ได้จัดสรรไว้) หรือคุณสามารถกำหนดจุดพัก

ddd และ nemiver เป็น front-end สำหรับ gdb ซึ่งทำให้การทำงานง่ายขึ้นสำหรับมือใหม่


6
การทิ้งขยะหลักมีประโยชน์มากกว่าการติดตามสแต็กอย่างไม่สิ้นสุดเนื่องจากคุณสามารถโหลดการถ่ายโอนข้อมูลหลักในการดีบักเกอร์และดูสถานะของโปรแกรมทั้งหมดและข้อมูล ณ จุดที่เกิดการขัดข้อง
Adam Hawes

1
สิ่งอำนวยความสะดวกด้านหลังที่คนอื่นแนะนำอาจจะดีกว่าไม่มีอะไร แต่มันเป็นพื้นฐานมาก - มันไม่ได้ให้หมายเลขบรรทัด ในทางกลับกันการใช้ core dumps ทำให้คุณสามารถดูสถานะทั้งหมดของแอปพลิเคชันย้อนหลังได้ในเวลาที่เกิดการขัดข้อง (รวมถึงการติดตามสแต็กโดยละเอียด) มีอาจจะเป็นปัญหาในทางปฏิบัติที่มีความพยายามที่จะใช้นี้สำหรับข้อมูลการแก้จุดบกพร่อง แต่ก็แน่นอนเป็นเครื่องมือที่มีประสิทธิภาพมากขึ้นสำหรับการเกิดปัญหาการวิเคราะห์และการยืนยันในระหว่างการพัฒนา (อย่างน้อยในลินุกซ์)
สูงศักดิ์

10

โปรดทราบว่าเมื่อคุณสร้างไฟล์แกนคุณจะต้องใช้เครื่องมือ gdb เพื่อดูมัน เพื่อให้ gdb เข้าใจถึงไฟล์หลักของคุณคุณจะต้องบอก gcc ให้กับเครื่องมือไบนารีด้วยสัญลักษณ์การดีบัก: ในการทำเช่นนี้คุณได้คอมไพล์ด้วยแฟล็ก -g:

$ g++ -g prog.cpp -o prog

จากนั้นคุณสามารถตั้งค่า "ulimit -c unlimited" เพื่อให้มันทิ้งคอร์หรือเพียงแค่เรียกใช้โปรแกรมของคุณภายใน gdb ฉันชอบแนวทางที่สองเพิ่มเติม:

$ gdb ./prog
... gdb startup output ...
(gdb) run
... program runs and crashes ...
(gdb) where
... gdb outputs your stack trace ...

ฉันหวังว่านี่จะช่วยได้.


4
คุณยังสามารถโทรติดต่อได้gdbทันทีจากโปรแกรมหยุดทำงาน เครื่องมือจัดการการตั้งค่าสำหรับ SIGSEGV, SEGILL, SIGBUS, SIGFPE ที่จะเรียก gdb รายละเอียด: stackoverflow.com/questions/3151779/… ข้อดีคือคุณจะได้รับ backtrace ที่สวยงามและมีคำอธิบายประกอบเหมือนbt fullคุณยังสามารถรับร่องรอยสแต็กของเธรดทั้งหมดได้อีกด้วย
วิ

คุณสามารถรับ backtrace ได้ง่ายกว่าในคำตอบ: gdb -silent ./prog core --eval-command = backtrace --batch -it จะแสดง backtrace และ debugger ที่ปิด
baziorek

10

ฉันเคยดูปัญหานี้มาระยะหนึ่งแล้ว

และฝังลึกลงไปในเครื่องมือประสิทธิภาพของ Google README

http://code.google.com/p/google-perftools/source/browse/trunk/README

พูดถึง libunwind

http://www.nongnu.org/libunwind/

ชอบที่จะได้ยินความคิดเห็นของห้องสมุดนี้

ปัญหาของ -dynynamic คือมันสามารถเพิ่มขนาดของไบนารีได้ค่อนข้างมากในบางกรณี


2
ใน x86 / 64 ฉันไม่ได้เห็น - rynamic เพิ่มขนาดไบนารีมาก การเพิ่ม -g ทำให้มีขนาดใหญ่ขึ้น
Dan

1
ฉันสังเกตเห็นว่า libunwind ไม่มีฟังก์ชันเพื่อรับหมายเลขบรรทัดและฉันเดา (ไม่ได้ทดสอบ) unw_get_proc_name ส่งคืนสัญลักษณ์ของฟังก์ชัน (ซึ่ง obfuscated สำหรับการบรรทุกเกินพิกัดและอื่น ๆ ) แทนชื่อเดิม
Herbert

1
ถูกต้อง. มันยากมากที่จะทำอย่างถูกต้อง แต่ฉันประสบความสำเร็จอย่างยอดเยี่ยมกับ gaddr2line มีข้อมูลที่เป็นประโยชน์มากมายที่นี่blog.bigpixel.ro/2010/09/stack-unwinding-stack-trace-with-gcc
Gregory

9

libc บางเวอร์ชันมีฟังก์ชันที่จัดการกับการติดตามสแต็ก คุณอาจจะสามารถใช้พวกเขา:

http://www.gnu.org/software/libc/manual/html_node/Backtraces.html

ฉันจำได้ว่าใช้libunwindเมื่อนานมาแล้วเพื่อรับร่องรอยสแต็ก แต่แพลตฟอร์มของคุณอาจไม่รองรับ


9

คุณสามารถใช้DeathHandler - คลาส C ++ ขนาดเล็กที่ทำทุกอย่างเพื่อคุณเชื่อถือได้


1
น่าเสียดายที่มันใช้execlp()ในการดำเนินการเรียก addr2line ... จะดีที่จะอยู่ในโปรแกรมของตัวเอง (ซึ่งเป็นไปได้โดยการรวมรหัส addr2line ในบางรูปแบบ)
ตัวอย่าง

9

ลืมเกี่ยวกับการเปลี่ยนแหล่งที่มาของคุณและทำแฮ็กด้วย backtrace () ฟังก์ชั่นหรือแมโคร - เหล่านี้เป็นเพียงโซลูชั่นที่ไม่ดี

ในฐานะที่เป็นวิธีแก้ปัญหาการทำงานที่ถูกต้องฉันจะแนะนำ:

  1. รวบรวมโปรแกรมของคุณด้วยการตั้งค่าสถานะ "-g" สำหรับการฝังสัญลักษณ์การดีบักไปยังไบนารี (ไม่ต้องกังวลสิ่งนี้จะไม่ส่งผลกระทบต่อประสิทธิภาพการทำงานของคุณ)
  2. บน linux ให้รันคำสั่งถัดไป: "ulimit -c unlimited" - เพื่อให้ระบบทำการทิ้งความผิดพลาดครั้งใหญ่
  3. เมื่อโปรแกรมของคุณล้มเหลวในไดเรกทอรีทำงานคุณจะเห็นไฟล์ "แกน"
  4. รันคำสั่งถัดไปเพื่อพิมพ์ backtrace ไปยัง stdout: gdb -batch -ex "backtrace" ./your_program_exe ./core

สิ่งนี้จะพิมพ์ backtrace ที่สามารถอ่านได้อย่างเหมาะสมของโปรแกรมของคุณด้วยวิธีที่มนุษย์สามารถอ่านได้ (พร้อมชื่อไฟล์ต้นฉบับและหมายเลขบรรทัด) นอกจากนี้วิธีการนี้จะให้อิสระแก่คุณในการทำให้ระบบของคุณเป็นอัตโนมัติ: มีสคริปต์สั้น ๆ ที่ตรวจสอบว่ากระบวนการสร้างคอร์ดัมพ์หรือไม่จากนั้นส่ง backtraces ทางอีเมลไปยังนักพัฒนาหรือบันทึกสิ่งนี้ลงในระบบการบันทึก


มันให้หมายเลขบรรทัดผิด สามารถปรับปรุงได้หรือไม่
HeyJude

7
ulimit -c unlimited

เป็นตัวแปรระบบซึ่งจะอนุญาตให้สร้างคอร์ดัมพ์หลังจากแอปพลิเคชันของคุณขัดข้อง ในกรณีนี้จำนวนไม่ จำกัด ค้นหาไฟล์ชื่อ core ในไดเรกทอรีเดียวกัน ตรวจสอบให้แน่ใจว่าคุณรวบรวมรหัสด้วยการเปิดใช้งานข้อมูลการดีบัก!

ความนับถือ


5
ผู้ใช้ไม่ได้ขอแกนดัมพ์ เขาขอร่องรอยสแต็ค ดูที่delorie.com/gnu/docs/glibc/libc_665.html
Todd Gamblin

1
คอร์หลักจะมี call stack ในขณะที่เกิดความผิดพลาดใช่มั้ย
Mo.

3
คุณสมมติว่าเขาใช้ Unix และใช้ Bash
Paul Tomblin

2
หากคุณใช้ tcsh คุณต้องทำlimit coredumpsize unlimited
sivabudh


6

ดูสิ่งอำนวยความสะดวก Stack Trace ในACE (ADAPTIVE Communication Environment) มันถูกเขียนขึ้นเพื่อให้ครอบคลุมทุกแพลตฟอร์มที่สำคัญ (และอื่น ๆ ) ไลบรารี่เป็นลิขสิทธิ์แบบ BSD คุณจึงสามารถคัดลอก / วางรหัสได้หากไม่ต้องการใช้ ACE


ลิงค์ดูเหมือนจะตาย
tglas

5

ฉันสามารถช่วยในเวอร์ชัน Linux: สามารถใช้ฟังก์ชั่น backtrace, backtrace_symbols และ backtrace_symbols_fd ได้ ดูหน้าคู่มือที่เกี่ยวข้อง


5

ดูเหมือนว่าในหนึ่งใน c ++ บูสเตอร์รุ่นล่าสุดที่ปรากฏขึ้นเพื่อให้สิ่งที่คุณต้องการอย่างแน่นอนรหัสอาจจะเป็นแบบหลายแพลตฟอร์ม มันเป็นboost :: stacktraceซึ่งคุณสามารถใช้เหมือนในตัวอย่างเพิ่ม :

#include <filesystem>
#include <sstream>
#include <fstream>
#include <signal.h>     // ::signal, ::raise
#include <boost/stacktrace.hpp>

const char* backtraceFileName = "./backtraceFile.dump";

void signalHandler(int)
{
    ::signal(SIGSEGV, SIG_DFL);
    ::signal(SIGABRT, SIG_DFL);
    boost::stacktrace::safe_dump_to(backtraceFileName);
    ::raise(SIGABRT);
}

void sendReport()
{
    if (std::filesystem::exists(backtraceFileName))
    {
        std::ifstream file(backtraceFileName);

        auto st = boost::stacktrace::stacktrace::from_dump(file);
        std::ostringstream backtraceStream;
        backtraceStream << st << std::endl;

        // sending the code from st

        file.close();
        std::filesystem::remove(backtraceFileName);
    }
}

int main()
{
    ::signal(SIGSEGV, signalHandler);
    ::signal(SIGABRT, signalHandler);

    sendReport();
    // ... rest of code
}

ใน Linux คุณรวบรวมรหัสข้างต้น:

g++ --std=c++17 file.cpp -lstdc++fs -lboost_stacktrace_backtrace -ldl -lbacktrace

ตัวอย่าง backtrace ที่คัดลอกมาจากเอกสารบูสต์ :

0# bar(int) at /path/to/source/file.cpp:70
1# bar(int) at /path/to/source/file.cpp:70
2# bar(int) at /path/to/source/file.cpp:70
3# bar(int) at /path/to/source/file.cpp:70
4# main at /path/to/main.cpp:93
5# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6
6# _start

4

* ระวัง: คุณสามารถสกัดกั้นSIGSEGV (ปกติสัญญาณนี้ถูกยกขึ้นก่อนที่จะหยุดทำงาน) และเก็บข้อมูลไว้ในไฟล์ (นอกเหนือจากไฟล์ core ที่คุณสามารถใช้เพื่อดีบักโดยใช้ gdb เป็นต้น)

ชนะ: ตรวจสอบนี้จาก MSDN

นอกจากนี้คุณยังสามารถดูรหัสโครเมี่ยมของ google เพื่อดูวิธีจัดการกับข้อขัดข้อง มันมีกลไกการจัดการข้อยกเว้นที่ดี


SEH ไม่ได้ช่วยในการสร้างการติดตามสแต็ก แม้ว่าอาจเป็นส่วนหนึ่งของโซลูชัน แต่โซลูชันนั้นยากที่จะนำไปใช้และให้ข้อมูลน้อยลงโดยเสียค่าใช้จ่ายในการเปิดเผยข้อมูลเพิ่มเติมเกี่ยวกับแอปพลิเคชันของคุณมากกว่าโซลูชันจริง : เขียน mini dump และตั้งค่า Windows ให้ทำสิ่งนี้โดยอัตโนมัติสำหรับคุณ
IIsspectable

4

ฉันพบว่าโซลูชัน @tgamblin ไม่สมบูรณ์ ไม่สามารถจัดการกับ stackoverflow ฉันคิดว่าเพราะตามค่าเริ่มต้นตัวจัดการสัญญาณถูกเรียกด้วยกองซ้อนเดียวกันและ SIGSEGV ถูกส่งออกมาสองครั้ง เพื่อปกป้องคุณต้องลงทะเบียนสแต็กอิสระสำหรับตัวจัดการสัญญาณ

คุณสามารถตรวจสอบได้ด้วยรหัสด้านล่าง โดยค่าเริ่มต้นตัวจัดการล้มเหลว ด้วยแมโครที่กำหนดไว้ STACK_OVERFLOW มันก็ใช้ได้

#include <iostream>
#include <execinfo.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <string>
#include <cassert>

using namespace std;

//#define STACK_OVERFLOW

#ifdef STACK_OVERFLOW
static char stack_body[64*1024];
static stack_t sigseg_stack;
#endif

static struct sigaction sigseg_handler;

void handler(int sig) {
  cerr << "sig seg fault handler" << endl;
  const int asize = 10;
  void *array[asize];
  size_t size;

  // get void*'s for all entries on the stack
  size = backtrace(array, asize);

  // print out all the frames to stderr
  cerr << "stack trace: " << endl;
  backtrace_symbols_fd(array, size, STDERR_FILENO);
  cerr << "resend SIGSEGV to get core dump" << endl;
  signal(sig, SIG_DFL);
  kill(getpid(), sig);
}

void foo() {
  foo();
}

int main(int argc, char **argv) {
#ifdef STACK_OVERFLOW
  sigseg_stack.ss_sp = stack_body;
  sigseg_stack.ss_flags = SS_ONSTACK;
  sigseg_stack.ss_size = sizeof(stack_body);
  assert(!sigaltstack(&sigseg_stack, nullptr));
  sigseg_handler.sa_flags = SA_ONSTACK;
#else
  sigseg_handler.sa_flags = SA_RESTART;  
#endif
  sigseg_handler.sa_handler = &handler;
  assert(!sigaction(SIGSEGV, &sigseg_handler, nullptr));
  cout << "sig action set" << endl;
  foo();
  return 0;
} 

4

พระราชาองค์ใหม่ในเมืองมาถึง https://github.com/bombela/backward-cpp

1 ส่วนหัวที่จะวางในรหัสของคุณและ 1 ห้องสมุดที่จะติดตั้ง

ส่วนตัวผมเรียกมันว่าใช้ฟังก์ชั่นนี้

#include "backward.hpp"
void stacker() {

using namespace backward;
StackTrace st;


st.load_here(99); //Limit the number of trace depth to 99
st.skip_n_firsts(3);//This will skip some backward internal function from the trace

Printer p;
p.snippet = true;
p.object = true;
p.color = true;
p.address = true;
p.print(st, stderr);
}

ว้าว! ในที่สุดนั่นก็เป็นสิ่งที่ควรทำ! ฉันเพิ่งทิ้งโซลูชันของตัวเองในความโปรดปรานของนี้
tglas

3

ฉันจะใช้รหัสที่สร้างกองติดตามสำหรับหน่วยความจำรั่วไหลออกมาในรั่วตรวจจับภาพ วิธีนี้ใช้ได้กับ Win32 เท่านั้น


และต้องการให้คุณจัดส่งสัญลักษณ์การแก้ปัญหาด้วยรหัสของคุณ โดยทั่วไปไม่พึงประสงค์ เขียนการถ่ายโอนข้อมูลขนาดเล็กและตั้งค่า Windows ให้ทำโดยอัตโนมัติสำหรับคุณในข้อยกเว้นที่ไม่สามารถจัดการได้
IIsspectable

3

ฉันได้เห็นคำตอบมากมายที่นี่เพื่อดำเนินการจัดการสัญญาณแล้วออกจาก นั่นคือวิธีที่จะไป แต่จำได้ว่าเป็นความจริงที่สำคัญมาก: exit(status)ถ้าคุณต้องการที่จะได้รับการถ่ายโอนข้อมูลหลักสำหรับข้อผิดพลาดที่สร้างขึ้นคุณไม่สามารถเรียก โทรabort()แทน!


3

ในฐานะที่เป็นวิธีการแก้ปัญหาที่ใช้ Windows เท่านั้นคุณจะได้รับเทียบเท่าของกองติดตาม (มีมากข้อมูลมากขึ้น) โดยใช้ของ Windows รายงานข้อผิดพลาด มีรายการรีจิสตรีไม่กี่รายการจึงสามารถตั้งค่าเพื่อรวบรวมการทิ้งโหมดผู้ใช้ :

เริ่มต้นด้วย Windows Server 2008 และ Windows Vista พร้อม Service Pack 1 (SP1) การรายงานข้อผิดพลาดของ Windows (WER) สามารถกำหนดค่าเพื่อให้การถ่ายโอนข้อมูลโหมดผู้ใช้เต็มรูปแบบถูกรวบรวมและจัดเก็บไว้ในเครื่องหลังจากแอปพลิเคชันโหมดผู้ใช้ล่ม [ ... ]

คุณลักษณะนี้ไม่ได้เปิดใช้งานตามค่าเริ่มต้น การเปิดใช้งานคุณสมบัตินี้ต้องการสิทธิ์ผู้ดูแลระบบ เมื่อต้องการเปิดใช้งานและกำหนดค่าคุณลักษณะให้ใช้ค่ารีจิสทรีต่อไปนี้ภายใต้คีย์Reporting \ LocalDumps HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows \ Windows ข้อผิดพลาด

คุณสามารถตั้งค่ารายการรีจิสตรีจากตัวติดตั้งซึ่งมีสิทธิ์ที่จำเป็น

การสร้างดัมพ์โหมดผู้ใช้มีข้อดีดังต่อไปนี้มากกว่าการสร้างการติดตามสแต็กบนไคลเอ็นต์:

  • มันใช้งานแล้วในระบบ คุณสามารถใช้ WER ตามที่ระบุไว้ข้างต้นหรือโทรMiniDumpWriteDumpด้วยตัวคุณเองหากคุณต้องการการควบคุมปริมาณข้อมูลที่จะถ่ายโอนข้อมูลอย่างละเอียดยิ่งขึ้น (ตรวจสอบให้แน่ใจว่าได้โทรจากกระบวนการอื่น)
  • วิธีที่สมบูรณ์กว่าการติดตามสแต็ก มันสามารถมีตัวแปรโลคัลฟังก์ชันอาร์กิวเมนต์สแต็คสำหรับเธรดอื่นโมดูลที่โหลดและอื่น ๆ ปริมาณข้อมูล (และขนาดที่ตามมา) สามารถปรับแต่งได้อย่างมาก
  • ไม่จำเป็นต้องจัดส่งสัญลักษณ์การดีบัก ทั้งสองนี้จะลดขนาดการปรับใช้ของคุณลงอย่างมากรวมทั้งทำให้การทำวิศวกรรมแอปพลิเคชันของคุณกลับยากขึ้น
  • ส่วนใหญ่เป็นอิสระจากคอมไพเลอร์ที่คุณใช้ การใช้ WER ไม่จำเป็นต้องมีรหัสใด ๆ ทั้งสองวิธีการมีฐานข้อมูลสัญลักษณ์ (PDB) มีประโยชน์อย่างมากสำหรับการวิเคราะห์ออฟไลน์ ฉันเชื่อว่า GCC สามารถสร้าง PDB ได้หรือมีเครื่องมือในการแปลงฐานข้อมูลสัญลักษณ์เป็นรูปแบบ PDB

รับทราบว่า WER สามารถถูกเรียกใช้โดยแอปพลิเคชันหยุดทำงานเท่านั้น (เช่นระบบยกเลิกกระบวนการเนื่องจากข้อยกเว้นที่ไม่สามารถจัดการได้) MiniDumpWriteDumpสามารถเรียกได้ตลอดเวลา สิ่งนี้อาจเป็นประโยชน์หากคุณต้องการถ่ายโอนข้อมูลสถานะปัจจุบันเพื่อวินิจฉัยปัญหาอื่น ๆ นอกเหนือจากความผิดพลาด

การอ่านค่าบังคับหากคุณต้องการประเมินความเหมาะสมของการทิ้งขยะขนาดเล็ก:


2

นอกเหนือจากคำตอบข้างต้นนี่คือวิธีที่คุณสร้าง Debian Linux OS สร้างการถ่ายโอนข้อมูลหลัก

  1. สร้างโฟลเดอร์“ coredumps” ในโฟลเดอร์โฮมของผู้ใช้
  2. ไปที่ /etc/security/limits.conf ใต้บรรทัด '' พิมพ์“ soft core unlimited” และ“ root soft core unlimited” หากเปิดใช้งาน core dumps สำหรับ root เพื่อให้มีพื้นที่ไม่ จำกัด สำหรับ core dumps
  3. หมายเหตุ:“ * ซอฟต์คอร์ไม่ จำกัด ” ไม่ครอบคลุมรูตซึ่งเป็นสาเหตุที่ต้องระบุรูทในบรรทัดของมัน
  4. หากต้องการตรวจสอบค่าเหล่านี้ให้ออกจากระบบลงชื่อเข้าใช้อีกครั้งและพิมพ์“ ulimit -a” ควรตั้งค่า“ ขนาดไฟล์หลัก” เป็นไม่ จำกัด
  5. ตรวจสอบไฟล์. bashrc (ผู้ใช้และรูทหากมี) เพื่อให้แน่ใจว่า ulimit ไม่ได้ถูกตั้งไว้ที่นั่น มิฉะนั้นค่าดังกล่าวจะถูกเขียนทับเมื่อเริ่มต้น
  6. เปิด /etc/sysctl.conf ป้อนข้อมูลต่อไปนี้ที่ด้านล่าง:“ kernel.core_pattern = /home//coredumps/%e_%t.dump” (% e จะเป็นชื่อกระบวนการและ% t จะเป็นเวลาของระบบ)
  7. ออกและพิมพ์“ sysctl -p” เพื่อโหลดการกำหนดค่าใหม่ตรวจสอบ / proc / sys / kernel / core_pattern และตรวจสอบว่าสิ่งนี้ตรงกับสิ่งที่คุณเพิ่งพิมพ์
  8. การถ่ายโอนข้อมูลหลักสามารถทดสอบได้โดยการเรียกใช้กระบวนการบนบรรทัดคำสั่ง (“ &”) แล้วฆ่าด้วย“ kill -11” หากการถ่ายโอนข้อมูลหลักประสบความสำเร็จคุณจะเห็น“ (การถ่ายโอนข้อมูลหลัก)” หลังจากตัวบ่งชี้ข้อผิดพลาดการแบ่งส่วน

2

หากคุณยังต้องการที่จะไปคนเดียวตามที่ฉันได้คุณสามารถเชื่อมโยงbfdและหลีกเลี่ยงการใช้addr2lineเช่นเดียวกับที่ฉันได้ทำที่นี่:

https://github.com/gnif/LookingGlass/blob/master/common/src/crash.linux.c

สิ่งนี้สร้างผลลัพธ์:

[E]        crash.linux.c:170  | crit_err_hdlr                  | ==== FATAL CRASH (a12-151-g28b12c85f4+1) ====
[E]        crash.linux.c:171  | crit_err_hdlr                  | signal 11 (Segmentation fault), address is (nil)
[E]        crash.linux.c:194  | crit_err_hdlr                  | [trace]: (0) /home/geoff/Projects/LookingGlass/client/src/main.c:936 (register_key_binds)
[E]        crash.linux.c:194  | crit_err_hdlr                  | [trace]: (1) /home/geoff/Projects/LookingGlass/client/src/main.c:1069 (run)
[E]        crash.linux.c:194  | crit_err_hdlr                  | [trace]: (2) /home/geoff/Projects/LookingGlass/client/src/main.c:1314 (main)
[E]        crash.linux.c:199  | crit_err_hdlr                  | [trace]: (3) /lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xeb) [0x7f8aa65f809b]
[E]        crash.linux.c:199  | crit_err_hdlr                  | [trace]: (4) ./looking-glass-client(_start+0x2a) [0x55c70fc4aeca]

1

บน Linux / unix / MacOSX ให้ใช้ไฟล์หลัก (คุณสามารถเปิดใช้งานด้วย ulimit หรือการเรียกระบบที่เข้ากันได้ ) ใน Windows ใช้การรายงานข้อผิดพลาดของ Microsoft (คุณสามารถเป็นพันธมิตรและเข้าถึงข้อมูลความผิดพลาดของแอปพลิเคชันของคุณ)


0

ฉันลืมเกี่ยวกับเทคโนโลยี GNOME ของ "apport" แต่ฉันไม่รู้มากเกี่ยวกับการใช้ มันถูกใช้เพื่อสร้าง stacktraces และการวินิจฉัยอื่น ๆ สำหรับการประมวลผลและสามารถยื่นข้อบกพร่องโดยอัตโนมัติ แน่นอนว่ามันคุ้มค่าที่จะเช็คอิน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.