Đã 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
g
NU đ
eb
xấ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ử
- 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.
- 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 ở.
- 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.
- 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)
- 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)
- Đó 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:
- [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
- https://stackoverflow.com/questions/5115613/core-dump-file-analysis
- https://stackoverflow.com/questions/8305866/how-do-i-analyze-a-programs-core-dump-file-with-gdb-when-it-has-command-line-pa/30524347#30524347
- [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
- [đượ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
- [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?
- [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
- [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