Debug Crash Ứng Dụng Linux với Core Dump và GDB: Hướng Dẫn Cài Đặt systemd-coredump

Linux tutorial - IT technology blog
Linux tutorial - IT technology blog

Ai cũng từng trải qua cảnh đó — service crash lúc 2 giờ sáng, process biến mất, và manh mối duy nhất là dòng “Segmentation fault” nằm sâu trong syslog. Debug vào sáng hôm sau cảm giác như điều tra pháp y mà thiếu đi một nửa bằng chứng. Cấu hình core dump đúng cách biến cuộc điều tra đó thành thứ gì đó thực sự khả thi.

Điều khó ở chỗ là Linux cung cấp nhiều cách để xử lý core dump — và chúng hoạt động khá khác nhau khi vào production. Thứ chạy tốt trong môi trường dev thường âm thầm thất bại ngay khi systemd xuất hiện. Để tôi chia sẻ những gì thực sự hiệu quả, dựa trên kinh nghiệm server thực tế.

So Sánh Các Phương Pháp: Ba Cách Xử Lý Core Dump trên Linux

1. Cách Truyền Thống: ulimit + kernel.core_pattern

Phương pháp cổ điển này có trước systemd hàng thập kỷ. Bạn đặt giới hạn kích thước file core và chỉ cho kernel biết nơi ghi dump:

# Kiểm tra cài đặt core dump hiện tại
ulimit -c

# Bật kích thước core dump không giới hạn (chỉ cho phiên hiện tại)
ulimit -c unlimited

# Đặt vị trí và pattern tên file core dump
echo '/tmp/cores/core.%e.%p.%t' | sudo tee /proc/sys/kernel/core_pattern

# Làm cho cài đặt tồn tại sau khi khởi động lại
echo 'kernel.core_pattern = /tmp/cores/core.%e.%p.%t' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

# Đảm bảo thư mục tồn tại và có quyền ghi
sudo mkdir -p /tmp/cores
sudo chmod 1777 /tmp/cores

Đối với các service được quản lý bởi systemd, ulimit theo phiên không được kế thừa. Bạn cũng cần thêm phần này vào unit file:

# /etc/systemd/system/myapp.service.d/override.conf
[Service]
LimitCORE=infinity

2. systemd-coredump (Phương Pháp Hiện Đại)

Khi systemd là init system của bạn — trường hợp này xảy ra trên bất kỳ Ubuntu, Debian, RHEL hay Fedora hiện đại nào — systemd-coredump kết nối trực tiếp vào cơ chế dump của kernel. Nó tự động capture dump, lưu dưới dạng nén trong /var/lib/systemd/coredump/, và đánh index qua journal. Truy vấn ngay lập tức bằng coredumpctl.

# Xác minh kernel đang route dump qua systemd
cat /proc/sys/kernel/core_pattern
# Kết quả mong đợi:
# |/lib/systemd/systemd-coredump %P %u %g %s %t %c %h

3. ABRT (Công Cụ Báo Cáo Lỗi Tự Động)

ABRT là sản phẩm của Red Hat, được cài mặc định trên Fedora và RHEL. Nó theo dõi crash, capture chúng, và tùy chọn gửi báo cáo lỗi lên Bugzilla. ABRT chạy nhiều daemon và có thành phần GUI — hữu ích cho workstation desktop, nhưng quá nặng cho production server. Trên các hệ thống Ubuntu hoặc cài đặt tối giản, chi phí overhead hiếm khi xứng đáng.

Ưu và Nhược Điểm của Từng Phương Pháp

ulimit Truyền Thống

  • Ưu điểm: Hoạt động ở mọi nơi không cần dependency bổ sung; toàn quyền kiểm soát vị trí file; không có daemon nền
  • Nhược điểm: File core có thể lấp đầy đĩa nhanh nếu không dọn dẹp định kỳ; không có nén tự động hay metadata; dễ bỏ sót giới hạn per-service trong unit file systemd; tìm đúng file core sau sự cố đòi hỏi tìm kiếm thủ công

systemd-coredump

  • Ưu điểm: Tự động nén zstd (tỷ lệ 3–5× trên heap dump); metadata phong phú lưu trong journal (PID, signal, đường dẫn executable, timestamp, cgroup); coredumpctl giúp tìm kiếm và trích xuất dễ dàng; tích hợp với journalctl để đối chiếu log
  • Nhược điểm: Yêu cầu systemd (không phải vấn đề trên distro hiện đại); dump tích lũy trong /var/lib/systemd/coredump/ — cần đủ dung lượng đĩa ở đó; tốn thêm một chút tài nguyên lúc crash do quá trình nén

ABRT

  • Ưu điểm: Báo cáo upstream tự động; tích hợp GUI tốt cho workstation
  • Nhược điểm: Tốn nhiều tài nguyên; mặc định gửi dữ liệu ra ngoài (lo ngại quyền riêng tư); không có sẵn trên Debian/Ubuntu nếu không cài thủ công phức tạp; quá nhiều tính năng so với nhu cầu debug post-mortem cho server

Cài Đặt Khuyến Nghị: Tại Sao systemd-coredump Là Lựa Chọn Tốt Nhất

Với bất kỳ production Linux server nào chạy systemd, systemd-coredump là lựa chọn đúng đắn. Trên máy Ubuntu 22.04 của tôi với 4GB RAM, sự khác biệt trở nên rõ ràng sau sự cố nghiêm trọng đầu tiên. Trước đây, tìm file core có nghĩa là chạy find / -name 'core.*' và hy vọng không có gì đã ghi đè lên nó. Bây giờ coredumpctl list hiển thị mọi crash — đường dẫn binary, signal, timestamp — trong chưa đầy một giây.

Nén zstd là điểm cộng thực sự. Heap dump 400MB nén xuống khoảng 120MB, nên các crash lặp lại trong lúc deploy lỗi sẽ không ăn hết đĩa của bạn. Mười dòng config. Sau đó coredumpctl kết hợp GDB xử lý mọi thứ từ triage nhanh đến unwinding call stack 50 frame.

Hướng Dẫn Triển Khai

Bước 1: Xác Minh systemd-coredump Đang Hoạt Động

Trên Ubuntu 20.04+ và hầu hết distro dựa trên systemd hiện đại, systemd-coredump đã được cài sẵn. Kiểm tra lại:

# Ubuntu/Debian
sudo apt install systemd-coredump

# RHEL/Fedora
sudo dnf install systemd

# Xác nhận kernel đang route dump qua systemd
cat /proc/sys/kernel/core_pattern
# Kết quả nên là: |/lib/systemd/systemd-coredump %P %u %g %s %t %c %h

Bước 2: Tinh Chỉnh coredump.conf

Chỉnh sửa /etc/systemd/coredump.conf cho phù hợp với profile bộ nhớ của server:

[Coredump]
# Lưu dưới dạng file nén trên đĩa (không nhúng vào journal)
Storage=external

# Dùng nén zstd
Compress=yes

# Kích thước tối đa mỗi core dump — điều chỉnh theo RSS của process lớn nhất
ProcessSizeMax=2G

# Tổng ngân sách đĩa cho tất cả core đã lưu
ExternalSizeMax=10G
MaxUse=5G
KeepFree=1G
# Áp dụng cấu hình
sudo systemctl daemon-reload

Trên server 4GB RAM, ProcessSizeMax=2G capture toàn bộ heap của bất kỳ process nào chạy quá tay. Đồng thời còn đủ dư địa để bản thân quá trình dump không kích hoạt OOM kill.

Bước 3: Kích Hoạt Crash Thử Nghiệm

Viết một chương trình C nhỏ với null pointer dereference cố ý:

// crash_test.c
#include <stdio.h>
#include <stdlib.h>

void broken_function() {
    int *ptr = NULL;
    *ptr = 42;  // Segfault xảy ra ở đây
}

int main() {
    printf("Sắp crash...\n");
    broken_function();
    return 0;
}
# Biên dịch VỚI debug symbol — quan trọng để GDB output dễ đọc
gcc -g -o crash_test crash_test.c

# Chạy chương trình
./crash_test
# Kết quả: Sắp crash...
# Segmentation fault (core dumped)

Bước 4: Tìm và Trích Xuất Core

# Liệt kê tất cả core dump đã capture
coredumpctl list

# Kết quả mẫu:
# TIME                             PID  UID  GID SIG     COREFILE EXE
# Tue 2026-06-03 14:22:11 JST    4821 1000 1000 SIGSEGV present  /home/user/crash_test

# Metadata chi tiết cho crash gần nhất
coredumpctl info

# Trích xuất file core để phân tích với GDB
coredumpctl dump -o ./core_crash_test

Bước 5: Phân Tích với GDB

Tải GDB với binary và core đã trích xuất:

gdb ./crash_test ./core_crash_test

# Hoặc để coredumpctl xử lý trực tiếp:
coredumpctl gdb

Trong GDB, đây là các lệnh thực sự hữu ích:

# Call stack đầy đủ tại thời điểm crash
(gdb) bt
(gdb) bt full          # bao gồm biến local của từng frame

# Di chuyển đến một frame cụ thể trong stack
(gdb) frame 1

# Xem các biến trong frame hiện tại
(gdb) info locals

# In giá trị một biến cụ thể
(gdb) print ptr

# Hiển thị các thanh ghi CPU tại điểm crash
(gdb) info registers

# Hiển thị source code xung quanh điểm crash
(gdb) list

# Disassemble (hữu ích cho binary đã strip)
(gdb) disassemble

Với chương trình thử nghiệm ở trên, bt chỉ thẳng vào broken_function với số dòng chính xác và null pointer trong ptr. Đó là toàn cảnh — binary, vị trí crash, trạng thái biến — được tái dựng nhiều giờ sau sự cố.

Debug Binary Production Khi Không Có Debug Symbol

Binary production thực tế thường đã bị strip. Cài đặt gói debuginfo nếu có:

# Ubuntu/Debian — bật repo debug symbol
sudo apt install ubuntu-dbgsym-keyring
echo "deb http://ddebs.ubuntu.com $(lsb_release -cs) main restricted universe multiverse" | \
  sudo tee /etc/apt/sources.list.d/ddebs.list
sudo apt update

# Cài debug symbol cho một gói cụ thể (ví dụ: nginx)
sudo apt install nginx-dbgsym

Với ứng dụng tự biên dịch, hãy giữ riêng file debug symbol bên cạnh binary đã strip:

# Biên dịch có symbol, sau đó strip để deploy nhưng vẫn giữ lại debug info
objcopy --only-keep-debug myapp myapp.debug
strip --strip-debug myapp

# Trong GDB, load symbol thủ công:
(gdb) symbol-file /path/to/myapp.debug

Tích Hợp Vào Runbook Xử Lý Sự Cố

Sau khi cấu hình systemd-coredump xong, những bước này chỉ mất 2 phút và nên là việc đầu tiên bạn làm sau bất kỳ sự cố crash nào:

  1. Chạy coredumpctl list --since "2 hours ago" để tìm các crash gần đây
  2. Đối chiếu timestamp với journalctl -u yourservice --since "..."
  3. Lưu trữ core trước khi bị xoay vòng: coredumpctl dump PID -o /var/incident-cores/$(date +%Y%m%d_%H%M%S).core
  4. Với các service tự biên dịch, luôn lưu binary chưa strip hoặc file .debug ở vị trí có version gắn với bản build

Crash mà không có core dump là một bí ẩn. Crash mà có core dump chỉ là một buổi debug thông thường. Cài đặt một lần — lần sau khi cảnh báo 2 giờ sáng kêu lên, bạn đã có toàn bộ bức tranh sẵn sàng.

Share: