"App khởi động rồi chết ngay, log không in gì cả." "Nó nói 'không tìm thấy file' nhưng file nào?" "Chương trình treo, chẳng biết nó kẹt ở đâu." Những tình huống này khiến việc debug tưởng như đoán mò — trừ khi bạn dùng một công cụ cho thấy chính xác chương trình đang làm gì ở mức thấp nhất: strace. Mọi việc "thật" một chương trình làm — đọc file, gửi mạng, cấp bộ nhớ — cuối cùng đều là một system call (lời gọi hệ thống) tới kernel. strace chen vào giữa và ghi lại từng cái. Bài này (phần 1 loạt "Debug và hiệu năng Linux") chạy thật để thấy strace biến hộp đen thành trong suốt.

Mọi việc thật đều là syscall

Chương trình của bạn không tự đọc đĩa hay gửi gói mạng — nó nhờ kernel làm qua syscall: openat (mở file), read/write (đọc/ghi), connect (kết nối mạng), mmap (cấp bộ nhớ)... strace bám vào tiến trình và in mỗi syscall cùng tham số và kết quả trả về.

strace cat data.txt
# openat(AT_FDCWD, "data.txt", O_RDONLY) = 3   <- mở file, được fd 3
# read(3, "noi dung file\n", 131072)     = 14   <- đọc 14 byte
# write(1, "noi dung file\n", 14)         = 14   <- ghi 14 byte ra stdout (fd 1)

Chỉ ba dòng đã kể toàn bộ câu chuyện: cat mở data.txt thành fd 3, đọc 14 byte, ghi 14 byte ra stdout (fd 1). Bạn thấy đúng file nó mở, đúng số byte, không phải suy đoán.

Ảnh chụp đoạn mã nền tối minh hoạ strace xem chương trình thật sự nói gì với kernel, mọi việc thật đều là syscall strace ghi lại chúng đọc ghi file mở mạng cấp bộ nhớ đều qua lời gọi hệ thống strace chen giữa chương trình và kernel in mọi syscall cộng kết quả strace cat data.txt openat data.txt bằng 3 read 3 bằng 14 write 1 bằng 14 bạn thấy chính xác nó mở file nào đọc bao nhiêu byte ghi đi đâu, bắt lỗi thật chương trình mở nhầm thiếu file gì app chạy lỗi mà log không nói gì strace cho câu trả lời openat etc config-khong-ton-tai O_RDONLY bằng -1 ENOENT thấy ngay file nào thiếu quyền nào bị từ chối EACCES, cờ hay dùng -f theo cả tiến trình con fork -e trace network chỉ lọc nhóm syscall -p PID gắn vào tiến trình đang chạy -c tóm tắt đếm cộng thời gian mỗi loại syscall -o file ghi ra file, khi nào dùng sao app không tìm thấy file cấu hình strace thấy đường dẫn thật app gọi mạng đi đâu -e trace network xem connect app treo ở đâu strace -p thấy nó kẹt ở syscall nào

Hình 1: Mọi thao tác thật là syscall; strace ghi lại từng cái cùng kết quả. Bắt lỗi qua mã trả về (ENOENT, EACCES), lọc bằng -e trace=, gắn vào tiến trình đang chạy bằng -p, tóm tắt bằng -c.

Đo thật: bắt lỗi mà log giấu

Ảnh chụp bảng kết quả chạy thật strace trong go-lab output thật, một strace cat data.txt thấy openat read write openat AT_FDCWD data.txt O_RDONLY bằng 3 read 3 noi dung file 131072 bằng 14 write 1 noi dung file 14 bằng 14 mở file fd 3 đọc 14 byte ghi 14 byte ra stdout fd 1, hai bắt file thiếu ENOENT openat AT_FDCWD etc config-khong-ton-tai O_RDONLY bằng -1 ENOENT No such file or directory biết chính xác file nào thiếu dù log app im lặng, ba -e trace network chỉ xem syscall mạng socket AF_UNIX SOCK_STREAM bằng 3 connect 3 sun_path var run nscd socket bằng -1 ENOENT thấy app thử kết nối tới đâu thành hay bại, bốn -c tóm tắt đếm syscall 100.00 0.000197 4 43 1 total tổng 43 syscall cho lệnh cat nền cho tối ưu bài sau

Hình 2: Chạy thật — strace cat phơi bày openat→fd 3, read=14, write=14; mở file thiếu cho -1 ENOENT (No such file); -e trace=network chỉ hiện socket/connect; -c tóm tắt tổng 43 syscall.

  • Bắt file thiếu: một app cố đọc /etc/config-khong-ton-tai. Log app có thể im lặng, nhưng strace cho: openat(..., "/etc/config-khong-ton-tai", O_RDONLY) = -1 ENOENT (No such file or directory). Bạn biết chính xác đường dẫn nó tìm và vì sao thất bại. Kết quả -1 EACCES thì là thiếu quyền, -1 ENOENT là không có file — hai lỗi khác nhau mà strace phân biệt rõ.
  • Lọc theo nhóm: strace -e trace=network chỉ hiện syscall mạng (socket, connect) — thấy app thử kết nối tới đâu và thành/bại. Trong demo, nó thử socket /var/run/nscd/socket (cache DNS) và nhận ENOENT.
  • Gắn vào tiến trình đang chạy: strace -p <PID> bám vào một tiến trình đã chạy mà không cần khởi động lại — vô giá khi debug một service đang treo trên production (thấy nó kẹt ở syscall nào).
  • Tóm tắt: strace -c không in từng dòng mà đếm — tổng 43 syscall cho lệnh cat. Đây là nền cho việc tìm nghẽn hiệu năng (bài sau).

Đánh đổi cần cân nhắc

strace làm chương trình chậm đi đáng kể — không dùng trên hot path production. Mỗi syscall bị dừng lại để ghi, nên một chương trình strace có thể chậm hàng chục lần. Dùng nó để chẩn đoán trên một lần chạy hoặc thời gian ngắn (timeout, -p rồi tách sớm), đừng để nó chạy dài trên service đang phục vụ tải. Với môi trường production nhạy cảm, cân nhắc các công cụ eBPF nhẹ hơn (bpftrace) chỉ lấy mẫu.

Output rất dài — luôn lọc. Một chương trình thực tế gọi hàng nghìn syscall (nạp thư viện, mmap...). In hết là ngập màn hình. Dùng -e trace= để lọc nhóm quan tâm (file, network, openat), -o file để ghi ra file rồi grep, hoặc -c để xem tổng quan trước khi soi chi tiết. Phần lớn "nhiễu" đầu output là nạp libc và động — thứ bạn cần thường nằm sau đó.

strace cho thấy cái gì, không cho thấy tại sao trong code. Nó là công cụ ranh giới kernel: thấy app mở file X và thất bại, nhưng không thấy dòng code nào gọi. Với lỗi logic trong chương trình của bạn, debugger (gdb/dlv) hoặc log ứng dụng hợp hơn. strace toả sáng khi vấn đề nằm ở tương tác với hệ thống (file, mạng, quyền) hoặc khi bạn không có mã nguồn (debug một binary lạ).

Ba ý mang về

  1. strace phơi bày mọi syscall chương trình gọi: đo thật strace cat cho thấy openat→fd, read=14 byte, write=14 byte — bạn thấy chính xác file nào được mở, bao nhiêu byte, ghi đi đâu, thay vì đoán.
  2. Bắt lỗi mà log giấu qua mã trả về: đo thật openat file thiếu → -1 ENOENT; strace phân biệt ENOENT (không có file) với EACCES (thiếu quyền), và -e trace=network thấy app kết nối tới đâu.
  3. Dùng đúng chỗ: -p gắn vào tiến trình đang treo, -c tóm tắt để tìm nghẽn — nhưng strace làm chậm chương trình nên chỉ để chẩn đoán ngắn, và nó cho biết cái gì ở ranh giới kernel chứ không phải dòng code nào.

Nguồn

Phần sau ta dùng strace để đo hiệu năng: -c đếm và -T đo thời gian mỗi syscall, tìm chính xác lời gọi nào khiến chương trình chậm — I/O, mạng, hay chờ khoá.