2011-05-10 38 views
18

Tôi đang cố gắng in backtrace khi chương trình C++ của tôi chấm dứt. Chức năng in ấn backtrace là như dưới đây;Cách lấy backtrace chi tiết hơn

void print_backtrace(void){ 

     void *tracePtrs[10]; 
     size_t count; 

     count = backtrace(tracePtrs, 10); 

     char** funcNames = backtrace_symbols(tracePtrs, count); 

     for (int i = 0; i < count; i++) 
      syslog(LOG_INFO,"%s\n", funcNames[i]); 

     free(funcNames); 

} 

Nó cung cấp kết quả như;

desktop program: Received SIGSEGV signal, last error is : Success 
    desktop program: ./program() [0x422225] 
    desktop program: ./program() [0x422371] 
    desktop program: /lib/libc.so.6(+0x33af0) [0x7f0710f75af0] 
    desktop program: /lib/libc.so.6(+0x12a08e) [0x7f071106c08e] 
    desktop program: ./program() [0x428895] 
    desktop program: /lib/libc.so.6(__libc_start_main+0xfd) [0x7f0710f60c4d] 
    desktop program: ./program() [0x4082c9] 

Có cách nào để có được backtrace chi tiết hơn với các tên hàm và dòng, như đầu ra gdb không?

+0

Bạn đã cài đặt bản cài đặt gỡ lỗi chưa? IIRC Linux sẽ sử dụng một libc với các biểu tượng gỡ lỗi trong nó cho mục đích này nếu bạn chuyển -g trên dòng lệnh tới GCC. –

+0

Tại sao không sử dụng gdb, tôi có thể hỏi? Ngoài ra, phần [Backtraces của hướng dẫn sử dụng GNU libc] (http://www.gnu.org/s/hello/manual/libc/Backtraces.html) có vẻ hữu ích. –

Trả lời

0

Nếu bạn muốn có một backtrace rất bị hất, bạn nên sử dụng ptrace (2) để theo dõi quá trình bạn muốn backtrace.

Bạn sẽ có thể xem tất cả các chức năng quá trình của bạn sử dụng nhưng bạn cần một số kiến ​​thức cơ bản asm

18

Có - vượt qua cờ -rdynamic để mối liên kết. Nó sẽ gây ra mối liên kết trong các bảng liên kết tên của tất cả các hàm tĩnh không có trong mã của bạn, chứ không phải chỉ các hàm được xuất.

Giá bạn trả là thời gian khởi động của chương trình của bạn. Đối với các chương trình vừa và nhỏ bạn sẽ không nhận thấy nó. Những gì bạn nhận được là backtrace() có thể cung cấp cho bạn tên của tất cả các hàm tĩnh không có trong dấu vết của bạn.

Tuy nhiên - Hãy coi chừng: có một số vấn đề bạn cần phải nhận thức được:

  1. backtrace_symbols cấp phát bộ nhớ từ malloc. Nếu bạn đã vào một SIGSEGV do tham nhũng malloc đấu trường (khá phổ biến), bạn sẽ tăng gấp đôi lỗi ở đây và không bao giờ nhìn thấy dấu vết của bạn trở lại.

  2. Tùy thuộc vào nền tảng này chạy trên (ví dụ x86), tên địa chỉ/chức năng của hàm chính xác nơi bạn bị rơi sẽ được thay thế tại vị trí trên ngăn xếp với địa chỉ trả về của trình xử lý tín hiệu. Bạn cần có được EIP đúng của hàm bị lỗi từ các tham số xử lý tín hiệu cho các nền tảng đó.

  3. nhật ký hệ thống không phải là chức năng an toàn của tín hiệu async. Nó có thể mất một khóa trong nội bộ và nếu khóa được thực hiện khi vụ tai nạn xảy ra (vì bạn bị rơi vào giữa một cuộc gọi đến syslog) bạn có một khóa chết

Nếu bạn muốn tìm hiểu tất cả các chi tiết đẫm máu , hãy xem video này của tôi cho một cuộc nói chuyện về nó tại OLS: http://free-electrons.com/pub/video/2008/ols/ols2008-gilad-ben-yossef-fault-handlers.ogg

2
  1. Tạo một ống
  2. fork() child process
  3. Make thực hiện addr2line
  4. trong pa thuê xử lý, chuyển đổi các địa chỉ trở về từ backtrace() để Hexadecimal
  5. Viết địa chỉ hex vào ống
  6. đọc lại sản lượng từ addr2line và in/log nó

Vì bạn đang làm tất cả điều này từ trình xử lý tín hiệu, hãy đảm bảo không sử dụng chức năng không an toàn-tín hiệu không đồng bộ. Bạn có thể xem danh sách các chức năng POSIX không an toàn-tín hiệu an toàn here.

2

Nếu bạn đang sử dụng tốt với chỉ nhận được vết lùi thích hợp khi chạy qua valgrind, thì đây có thể là một lựa chọn cho bạn:

VALGRIND_PRINTF_BACKTRACE (định dạng, ...):

Nó sẽ cung cấp cho bạn backtrace cho tất cả các chức năng, bao gồm cả các hàm tĩnh.

4

Nhập địa chỉ vào addr2line và nó sẽ hiển thị cho bạn tên tệp, số dòng và tên hàm.

1

Các lựa chọn tốt hơn tôi đã tìm thấy là libbacktrace bởi Ian Lance Taylor:

https://github.com/ianlancetaylor/libbacktrace

backtrace_symbols() không chỉ là biểu tượng in xuất khẩu và không thể được ít di động vì nó đòi hỏi sự libc GNU.

addr2line đẹp vì nó bao gồm tên tệp và số dòng. Nhưng nó không thành công ngay sau khi bộ nạp thực hiện các chuyển vị. Ngày nay ASLR là phổ biến, nó sẽ thất bại rất thường xuyên.

riêng độc lập sẽ không cho phép người ta in tên tệp và số dòng. Để thực hiện điều này, bạn cần phân tích cú pháp thông tin gỡ lỗi DWARF bên trong tệp nhị phân ELF. Điều này có thể được thực hiện bằng cách sử dụng libdwarf, mặc dù. Nhưng tại sao bận tâm khi libbacktrace cung cấp cho bạn mọi thứ cần thiết miễn phí?

0

Nếu bạn không muốn sử dụng phương thức "tín hiệu một quy trình khác chạy gdb trên bạn", tôi cho rằng gby đang ủng hộ, bạn cũng có thể thay đổi mã của mình để gọi mở() trên tệp nhật ký sự cố và sau đó backtrace_symbols_fd() với fd được trả về bởi open() - cả hai hàm đều là tín hiệu async an toàn theo hướng dẫn glibc. Dĩ nhiên, bạn sẽ vẫn cần phải có động lực. Ngoài ra, từ những gì tôi đã thấy, đôi khi bạn vẫn cần chạy addr2line trên một số địa chỉ mà các hàm backtrace *() sẽ không thể giải mã được.

Cũng lưu ý rằng fork() không phải là tín hiệu không đồng bộ an toàn: http://article.gmane.org/gmane.linux.man/1893/match=fork+async, ít nhất không phải trên Linux. Không phải là syslog(), như ai đó đã chỉ ra.

Các vấn đề liên quan