Điểm:12

Tôi có thể tìm các tệp kết xuất lõi ở đâu và làm cách nào để xem và phân tích vết lùi (dấu vết ngăn xếp) trong một tệp?

lá cờ cn

Khi tôi chạy chương trình C của mình trên Ubuntu 20.04, tôi gặp lỗi thời gian chạy này:

Lỗi phân đoạn (đổ lõi)

Tôi thực sự cần tìm và xem cốt lõi tập tin, nhưng tôi không thể tìm thấy nó ở bất cứ đâu. Nó ở đâu và làm cách nào để xem backtrace trong đó?

Điểm:17
lá cờ cn

Đã thử nghiệm trong Ubuntu 20.04.

1. Kích hoạt các tệp cốt lõi

Trước hết, chạy ulimit -c để xem kích thước tối đa cho phép đối với các tệp cốt lõi trên hệ thống của bạn. Trên Ubuntu 20.04 đối với tôi, của tôi trả về 0, có nghĩa là không thể tạo tệp lõi.

ulimit --help cho thấy ý nghĩa của -c:

-c kích thước tối đa của các tệp lõi được tạo

Vì vậy, hãy đặt kích thước tệp lõi được phép thành vô hạn, như hình bên dưới. Lưu ý rằng tôi nghĩ rằng điều này chỉ áp dụng đến một thiết bị đầu cuối mà bạn chạy cái này trong, và tôi làm không phải nghĩ rằng nó liên tục trong các lần khởi động lại, vì vậy bạn phải chạy nó mỗi khi bạn muốn tạo các tệp cốt lõi và trong mỗi thiết bị đầu cuối bạn muốn nó hoạt động:

# đặt kích thước tệp kết xuất lõi tối đa thành không giới hạn
ulimit -c không giới hạn
# xác minh rằng nó hiện được đặt thành "không giới hạn"
ulimit -c

Đó là nó! Hiện nay, chạy chương trình của bạn và nếu nó gặp sự cố và thực hiện "kết xuất lõi" thì nó sẽ kết xuất lõi dưới dạng cốt lõi tập tin vào cùng thư mục bạn đã ở khi bạn gọi tệp thực thi. Tên của tệp chỉ đơn giản là "lõi".

2. Xem backtrace trong gdb

Bạn nên xây dựng chương trình C hoặc C++ của mình với các biểu tượng gỡ lỗi để xem thông tin hữu ích trong tệp chính của bạn. Không có biểu tượng gỡ lỗi, bạn chỉ có thể thấy địa chỉ của các hàm được gọi, không phải tên thực hoặc số dòng.

Trong gcc, sử dụng -ggdb -O0 để bật gỡ lỗig biểu tượng được tối ưu hóa cho gdb gNU đebxấu hơn. Bạn cũng có thể dùng -g -O0, -g3 -O0, v.v., nhưng -ggdb -O0 là tốt nhất. Chúng ta có thực sự cần tối ưu hóa cấp 0 (-O0) cho cái này? Vâng, vâng, chúng tôi làm. Xem câu trả lời của tôi ở đây: Stack Overflow: Sự khác biệt giữa trình biên dịch là gì -O0 tùy chọn và -Og Tùy chọn?

Ví dụ xây dựng và chạy các lệnh trong C và C++: vì vậy, các lệnh xây dựng và chạy đầy đủ của bạn trong C hoặc C++ có thể giống như sau:

# Lệnh xây dựng và chạy C cho "hello_world.c"
gcc -Wall -Wextra -Werror -ggdb -O0 -std=c11 -o hello_world hello_world.c \
&& ./Chào thế giới

# Lệnh xây dựng và chạy C++ cho "hello_world.c"
g++ -Wall -Wextra -Werror -ggdb -O0 -std=c++17 -o hello_world hello_world.c \
&& ./Chào thế giới

Mở tệp lõi trong gdb như thế này:

đường dẫn gdb/đến/của tôi/đường dẫn thực thi/đến/lõi

Giả sử bạn vừa chạy đường dẫn/đến/của tôi/có thể thực thi được, thì cốt lõi tệp sẽ nằm trong cùng thư mục mà bạn vừa truy cập khi lõi bị hủy, vì vậy bạn có thể chạy tệp này:

đường dẫn gdb/đến/của tôi/lõi thực thi

Trong gdb, xem backtrace (ngăn xếp lệnh gọi hàm tại thời điểm xảy ra sự cố) với:

bt
# hoặc (chính xác cùng một lệnh)
ở đâu

# HOẶC (để biết thêm chi tiết, chẳng hạn như xem tất cả các đối số của hàm--
# cảm ơn Peter Cordes trong phần bình luận bên dưới)
đầy đủ

# Để biết thông tin chi tiết và trợ giúp về gdb, hãy xem:
giúp bt
# hoặc
giúp ở đâu

QUAN TRỌNG: khi một kết xuất lõi xảy ra, nó KHÔNG tự động ghi đè lên bất kỳ cốt lõi tệp trong thư mục hiện tại của bạn bằng một tệp mới, vì vậy bạn phải xóa thủ công người già cốt lõi tập tin với lõi rm TRƯỚC KHI tạo tệp lõi mới khi chương trình của bạn gặp sự cố, để luôn có tệp lõi mới nhất để phân tích.

3. Dùng thử

  1. Trong một thiết bị đầu cuối, chạy ngủ 30 để bắt đầu một quá trình ngủ trong 30 giây.
  2. Trong khi nó đang chạy, nhấn Điều khiển + \ để buộc một kết xuất lõi. Bây giờ bạn sẽ thấy một cốt lõi tập tin trong thư mục bạn đang ở.
  3. Vì chúng tôi không có tệp thực thi cho điều này với các ký hiệu gỡ lỗi trong đó, nên chúng tôi sẽ chỉ mở tệp lõi trong gdb thay vì tệp thực thi có ký hiệu + tệp lõi. Vì vậy, chạy lõi gdb -c để mở tệp lõi vừa được tạo bởi sự cố bắt buộc.
  4. Bạn sẽ thấy điều này. Lưu ý rằng nó biết bạn đã gọi lệnh nào (ngủ 30) khi kết xuất lõi xảy ra:
    Lõi được tạo bởi `sleep 30'.
    Chương trình kết thúc với tín hiệu SIGQUIT, Quit.
    #0 0x00007f93ed32d334 trong ?? ()
    (gdb) 
    
  5. Chạy bt hoặc ở đâu để xem backtrace. Bạn sẽ thấy điều này:
    (gdb) bt
    #0 0x00007f93ed32d334 trong ?? ()
    #1 0x000000000000000a trong ?? ()
    #2 0x00007f93ed2960a5 trong ?? ()
    #3 0x0000000000000000 trong ?? ()
    (gdb)
    
  6. Đó là các địa chỉ của các chức năng được gọi trên ngăn xếp cuộc gọi. Nếu bạn đã bật biểu tượng gỡ lỗi, bạn sẽ thấy nhiều thông tin hơn, bao gồm tên hàm và số dòng, như thế này (lấy từ một chương trình C của tôi):
    #10 0x00007fc1152b8ebf trong __printf (định dạng=<đã tối ưu hóa>) tại printf.c:33
    #11 0x0000562bca17b3eb trong fast_malloc (num_bytes=1024) tại src/fast_malloc.c:225
    #12 0x0000562bca17bb66 trong malloc (num_bytes=1024) tại src/fast_malloc.c:496
    

4. Quên các tệp cốt lõi đi và chỉ cần chạy chương trình trực tiếp đến điểm sự cố trong gdb!

Như @Peter Cordes đã nêu trong các nhận xét bên dưới, bạn cũng có thể chạy trực tiếp chương trình bên trong gdb, để nó gặp sự cố ở đó, vì vậy bạn không cần phải mở tệp lõi sau khi thực tế! Ông tuyên bố:

Các lệnh GDB đó không dành riêng cho các tệp lõi, chúng hoạt động bất cứ khi nào bạn dừng tại điểm dừng. Nếu bạn gặp sự cố có thể tái tạo, việc chạy chương trình của bạn theo GDB thường dễ dàng hơn/tốt hơn (như gdb ./a.out) nên GDB sẽ có quy trình trong bộ nhớ thay vì tệp lõi. Ưu điểm chính là bạn có thể đặt điểm dừng hoặc điểm theo dõi ở đâu đó trước thứ đã bị hỏng và một bước để xem điều gì đang xảy ra. Hoặc với cơ sở ghi của GDB, bạn có thể bước ngược và xem điều gì dẫn đến sự cố, nhưng điều đó có thể không ổn định, chậm và tốn nhiều bộ nhớ.

Như đã nêu ở trên, bạn nên biên dịch chương trình của mình với các biểu tượng gỡ lỗi trên và với Mức tối ưu hóa 0, sử dụng -ggdb -O0. Xem ví dụ đầy đủ về các lệnh xây dựng và chạy trong C và C++ ở trên.

Bây giờ hãy chạy chương trình trong gdb:

# Mở tệp thực thi trong gdb
đường dẫn gdb/đến/của tôi/có thể thực thi được
# Chạy nó (nếu nó vẫn bị lỗi, bạn sẽ thấy nó bị lỗi)
r 
# Xem backtrace (ngăn xếp cuộc gọi)
bt  
# Thoát khi hoàn thành 
q

Và nếu bạn cần ghi thủ công vết lùi vào tệp nhật ký để phân tích sau này, bạn có thể thực hiện như thế này (phỏng theo ghi chú trong tôi eRCaGuy_dotfiles repo ở đây):

đặt tệp nhật ký gdb_log.txt
đặt đăng nhập
đặt lệnh theo dõi trên
hiển thị ghi nhật ký # chứng minh ghi nhật ký được bật
tuôn ra
thiết lập đẹp in trên
bt # xem đường lùi
tắt đăng xuất  
hiển thị ghi nhật ký # chứng minh ghi nhật ký đã tắt

Xong! Bây giờ bạn đã lưu vết lùi gdb trong tệp "gdb_log.txt".

Người giới thiệu:

  1. [câu trả lời tôi cần nằm trong chính câu hỏi này] https://stackoverflow.com/questions/2065912/core-dumped-but-core-file-is-not-in-the-current-directory
  2. https://stackoverflow.com/questions/5115613/core-dump-file-analysis
  3. https://stackoverflow.com/questions/8305866/how-do-i-analyze-a-programs-core-dump-file-with-gdb-when-it-has-command-line-pa/30524347#30524347
  4. [thông tin rất hữu ích, bao gồm. các Điều khiển + \ thủ thuật để buộc đổ lõi!] https://unix.stackexchange.com/questions/277331/segmentation-fault-core-dumped-to-where-what-is-it-and-why/409776#409776
  5. [được tham chiếu bởi câu trả lời ở trên] https://unix.stackexchange.com/questions/179998/where-to-search-for-the-core-file-generated-by-the-crash-of-a-linux-application/180004#180004
  6. [câu trả lời nằm trong chính câu hỏi] Tôi tìm kết xuất lõi trong Ubuntu 16.04LTS ở đâu?
  7. [câu trả lời của tôi] Stack Overflow: Sự khác biệt giữa trình biên dịch là gì -O0 tùy chọn và -Og Tùy chọn?

đọc thêm để làm

  1. [TÔI VẪN CẦN NGHIÊN CỨU & THỬ NÀY] Cách sử dụng LD_PRELOAD với gdb: https://stackoverflow.com/questions/10448254/how-to-use-gdb-with-ld-preload
Peter Cordes avatar
lá cờ fr
Nếu bạn có các biểu tượng gỡ lỗi, `bt full` rất hay: hiển thị các đối số và nội dung. Hoặc thậm chí `thread apply all bt full` cho chương trình đa luồng. (Mặc dù đó là nhiều hơn mức bình thường bạn muốn xem xét tất cả cùng một lúc, do đó, việc gửi báo cáo lỗi sẽ hữu ích hơn là cho mục đích sử dụng của riêng bạn.)
Gabriel Staples avatar
lá cờ cn
@PeterCordes, cảm ơn. Tôi cũng đã thêm ghi chú về `bt full` trong câu trả lời. Tôi hoàn toàn mới để xem xét các bãi chứa cốt lõi. Viết câu trả lời này ngày hôm qua vừa là lần đầu tiên tôi nhìn thấy tệp `core` vừa là lần đầu tiên tôi thực hiện truy ngược trên một tệp.
Peter Cordes avatar
lá cờ fr
Các lệnh GDB đó không dành riêng cho các tệp lõi, chúng hoạt động bất cứ khi nào bạn dừng tại điểm dừng. Nếu bạn gặp sự cố có thể tái tạo, việc chạy chương trình của bạn trong GDB thường dễ dàng hơn/tốt hơn (như `gdb ./a.out`), vì vậy GDB sẽ có quy trình trong bộ nhớ thay vì tệp lõi. Ưu điểm chính là bạn có thể đặt điểm ngắt hoặc điểm theo dõi ở đâu đó *trước* sự cố xảy ra và thực hiện một bước để xem điều gì đang xảy ra. Hoặc với các tiện ích ghi của GDB, bạn có thể *đi lùi* và xem nguyên nhân dẫn đến sự cố, nhưng điều đó có thể không ổn định, chậm và tốn nhiều bộ nhớ.
Điểm:1
lá cờ cn

Tìm thấy thông qua tìm kiếm. Tôi đang chạy Ubuntu Mate 21.10. Đối với những người đang chạy phiên bản muộn của Ubuntu, xuất hiện sẽ tạo ra bãi trong /var/lib/apport/coredump.

Nếu bạn không thể tìm thấy tệp kết xuất cốt lõi của mình, con mèo /var/log/apport.log. Khi tôi làm điều đó, tôi thấy:

thực thi không thuộc về một gói, bỏ qua
đã gọi cho pid 5545, tín hiệu 11, giới hạn lõi 0, chế độ kết xuất 1

Lưu ý giới hạn lõi 0, điều đó có nghĩa là sẽ không có tệp kết xuất lõi nào được tạo. Vì vậy, tôi đã chạy lệnh được hiển thị trong bài đăng này (ulimit -c không giới hạn), và lần này apport.log cho thấy điều này:

ghi kết xuất lõi vào core._my_prog.1000.e43b2f33-4708-438c-a7d7-05062f381382.5650.795448 (giới hạn: -1)

Tôi không thể tìm thấy cái này trong thư mục hiện tại hoặc thư mục chứa tệp thực thi, vì vậy tôi đã tìm trên toàn bộ hệ thống và tìm thấy nó trong /var/lib/apport/coredump.

Đăng câu trả lời

Hầu hết mọi người không hiểu rằng việc đặt nhiều câu hỏi sẽ mở ra cơ hội học hỏi và cải thiện mối quan hệ giữa các cá nhân. Ví dụ, trong các nghiên cứu của Alison, mặc dù mọi người có thể nhớ chính xác có bao nhiêu câu hỏi đã được đặt ra trong các cuộc trò chuyện của họ, nhưng họ không trực giác nhận ra mối liên hệ giữa câu hỏi và sự yêu thích. Qua bốn nghiên cứu, trong đó những người tham gia tự tham gia vào các cuộc trò chuyện hoặc đọc bản ghi lại các cuộc trò chuyện của người khác, mọi người có xu hướng không nhận ra rằng việc đặt câu hỏi sẽ ảnh hưởng—hoặc đã ảnh hưởng—mức độ thân thiện giữa những người đối thoại.