1. Problem Statement
Production chậm. Bạn có 30 phút và một máy 64 core đang chạy 200 process. Câu hỏi không phải “gõ lệnh gì” mà là: mỗi công cụ nhìn hệ thống qua cửa sổ nào, trả giá bao nhiêu để nhìn, và nói dối ở đâu. Không hiểu cơ chế thì: dùng strace làm sập production (overhead 100x), đọc %util của NVMe như HDD (sai), tin flame graph thiếu frame (vì thiếu frame pointer), hay đo latency bằng trung bình (che mất tail).
2. Bản đồ: bốn nguồn dữ liệu của mọi công cụ
Mọi công cụ quan sát Linux đều uống từ bốn nguồn:
1. /proc, /sys — kernel XUẤT BẢN counter/state ra file ảo
(top, ps, vmstat, iostat, sar, ss... đọc từ đây)
2. Hardware PMU — counter trong CPU: cycles, instructions, cache miss
(perf stat/record ở chế độ hardware event)
3. Static tracepoints — điểm hook ĐẶT SẴN trong source kernel (~2000 điểm:
+ kprobes/uprobes sched:*, syscalls:*, block:*...) và hook ĐỘNG vào
bất kỳ hàm kernel (kprobe) / hàm user (uprobe)
(perf, ftrace, eBPF đều cắm vào tầng này)
4. ptrace — quyền điều khiển/chặn process khác (strace, gdb)
← nguồn DUY NHẤT có overhead thảm họaNắm bảng này là nắm được cả chương: mỗi công cụ = cách đọc + tổng hợp một hoặc vài nguồn trên.
3. Bộ đồ nghề cổ điển — đọc counter
3.1. top/htop — cơ chế và những chỗ nói dối
Cơ chế: mỗi 1-3s đọc /proc/<pid>/stat của mọi process, lấy delta CPU tick chia khoảng thời gian. Hệ quả: (1) %CPU là trung bình của interval — burst 50ms không hiện; (2) process sống ngắn không bao giờ xuất hiện (sinh và chết giữa hai lần đọc — dùng perf sched/eBPF execsnoop để bắt); (3) đọc cột: %us user, %sy kernel, %wa iowait (nghĩa thật: CPU rảnh trong khi có IO đang chờ — không phải “CPU bận vì IO”; %wa cao + %us thấp = máy rảnh chờ disk, không phải máy quá tải), %si softirq (network!), %st bị hypervisor cướp (VM hàng xóm ồn).
Load average: trung bình mũ 1/5/15 phút của số task runnable + D-state — vì có D nên load 40 với CPU idle 90% = bão IO/NFS, không phải bão CPU (case chương 15).
3.2. vmstat / iostat / sar — delta của counter toàn cục
vmstat 1: mỗi giây đọc/proc/stat,/proc/vmstat. Cột quan trọng:r(run queue — so với số core),b(D-state),si/so(swap in/out — phải là 0),cs(context switch),wa.iostat -x 1: đọc/proc/diskstats. Nhớ:%util= tỷ lệ thời gian thiết bị có ≥1 IO — với NVMe xử lý 64k lệnh song song, 100% util có thể vẫn còn 90% năng lực; nhìnawait(latency) vàaqu-sz(độ sâu hàng đợi) mới đúng.sar: chính là bộ counter đó nhưng ghi lịch sử (10 phút/lần qua cron) — trả lời “đêm qua 3h có gì” khi mọi công cụ khác chỉ thấy hiện tại. Bật sẵnsysstattrên mọi máy — miễn phí, cứu mạng khi điều tra hồi cứu.
3.3. strace / ltrace — mạnh, chậm, nguy hiểm
Cơ chế strace: ptrace(PTRACE_SYSCALL) — mỗi syscall của target dừng 2 lần (vào + ra), kernel schedule strace chạy để đọc register, rồi đánh thức lại target. Mỗi syscall thành ~4 context switch → chậm 10-100 lần. Quy tắc: production chịu tải → không strace; thay bằng perf trace (tracepoint, overhead thấp) hoặc eBPF. strace dành cho: process đã hỏng sẵn, môi trường dev, hoặc câu hỏi “nó chết ở syscall nào” (strace -f -e trace=%file xem mở file gì là use case kinh điển vô hại với process ngắn). ltrace tương tự cho library call, còn chậm hơn.
4. perf — kính hiển vi thống kê
4.1. Cơ chế sampling
perf record KHÔNG đo mọi sự kiện — nó lấy mẫu: PMU đếm (ví dụ) cycles, tràn ngưỡng → NMI interrupt → perf ghi lại (PC + stack) → reset. 99Hz nghĩa là ~99 ảnh chụp/giây/CPU. Hệ quả: overhead thấp (1-5%), kết quả là phân bố thống kê — hàm chiếm 30% sample ≈ 30% CPU time; hàm 0.1% có thể không hiện. Sampling trả lời “CPU đang ở đâu”, không trả lời “hàm này chạy bao nhiêu lần” (cái đó cần tracing).
perf top # "top của hàm" — nhìn nhanh 30 giây đầu
perf record -F 99 -g -p <pid> -- sleep 30
perf report # hoặc:
perf script | stackcollapse-perf.pl | flamegraph.pl > cpu.svg
Flame graph: trục ngang = tỷ lệ sample (KHÔNG phải thời gian tuần tự), trục dọc = stack. Cao nguyên rộng trên đỉnh = hotspot. Bẫy số một: stack cụt/vô nghĩa vì binary build không có frame pointer — Go mặc định OK; C/C++ cần -fno-omit-frame-pointer (nhiều distro đã bật lại mặc định từ 2023-2024) hoặc dùng DWARF (--call-graph dwarf, nặng hơn); JIT (Java/Node) cần perf-map-agent/--perf-basic-prof.
perf stat (đếm chứ không sample) cho bức tranh vi kiến trúc: IPC < 1 → nghẽn memory; cache-misses, branch-misses (chương 02). perf sched ghi sự kiện scheduler — độ trễ run queue per-task. perf c2c — bắt false sharing.
5. ftrace — máy ghi âm của kernel
Cơ chế: kernel được build với khả năng bật hook ở đầu mọi hàm kernel (nop được vá thành lệnh gọi khi bật — zero cost khi tắt); điều khiển qua /sys/kernel/tracing (tracefs). Không cần cài gì — có mặt trên mọi máy Linux, kể cả máy không cài nổi package lúc 3h sáng:
cd /sys/kernel/tracing
echo function_graph > current_tracer # vẽ cây gọi hàm kernel + thời gian
echo 1 > events/sched/sched_switch/enable # hoặc bật từng tracepoint
cat trace_pipe
# tiện hơn: trace-cmd record -e block; kernelshark để xem GUI
Dùng khi: cần biết chính xác kernel làm gì theo thứ tự thời gian (ai gọi vfs_fsync, vì sao ngủ ở đâu), độ trễ wakeup, hành vi IRQ. ftrace là công cụ “sự kiện tuần tự”; perf là “thống kê”; hai góc nhìn bổ nhau.
6. eBPF — cách quan sát hiện đại: chạy code trong kernel một cách an toàn
Bước nhảy khái niệm: thay vì kéo dữ liệu thô ra user space rồi tổng hợp (đắt), đưa chương trình tổng hợp vào kernel:
Bạn viết chương trình nhỏ (C bị giới hạn / bpftrace DSL)
→ biên dịch ra bytecode eBPF
→ VERIFIER kiểm chứng: không loop vô hạn, không đọc memory bừa,
có giới hạn lệnh → TỪ CHỐI nếu không chứng minh được an toàn
→ JIT thành mã máy, cắm vào hook: kprobe/uprobe/tracepoint/perf event
→ chạy MỖI LẦN sự kiện xảy ra, ghi kết quả vào BPF MAP (hash/histogram
trong kernel) → user space đọc map định kỳ
→ đo hàng triệu sự kiện/giây với overhead vài %, không sửa kernel,
không restart app, an toàn theo CONSTRUCT chứ không theo niềm tinĐây là điều strace/perf không làm được: ví dụ biolatency cắm vào block layer, cộng dồn histogram latency trong kernel, mỗi giây chỉ chuyển vài trăm byte (cái histogram) ra ngoài. Bộ công cụ dùng ngay (bcc-tools / bpftrace):
| Câu hỏi production | Công cụ |
|---|---|
| Thời gian chờ run queue? | runqlat |
| Ai đang exec process mới liên tục? | execsnoop |
| Phân bố latency block IO? IO nào chậm? | biolatency, biosnoop |
| Ai mở file gì? | opensnoop |
| TCP retransmit/drop từng sự kiện? | tcpretrans, tcpdrop |
| Thread block ở đâu (off-CPU)? | offcputime |
| Syscall nào nhiều nhất? | syscount |
| Page cache hit ratio? | cachestat |
Một dòng bpftrace trả lời câu hỏi tùy biến:
bpftrace -e 'kprobe:vfs_fsync { @[comm] = count(); }' # ai đang gọi fsync?
bpftrace -e 'tracepoint:sched:sched_switch { @[args->prev_comm] = count(); }'
eBPF còn vượt ra ngoài observability: XDP (xử lý packet tại driver — DDoS filter của Cloudflare), thay iptables (Cilium trong Kubernetes), seccomp-style policy — trở thành cơ chế mở rộng kernel chính thống như đã nhắc ở chương 03.
7. Phương pháp — công cụ vô dụng nếu không có trình tự
USE method (Brendan Gregg) cho mỗi tài nguyên (CPU, memory, disk, network, và cả lock, pool, queue của app): kiểm tra Utilization – Saturation – Errors:
CPU: util=%CPU | sat=run queue/PSI cpu | err=hiếm
Memory: util=available | sat=swap in-out, direct reclaim, PSI mem | err=OOM kill
Disk: util=await so baseline | sat=aqu-sz, PSI io | err=I/O error dmesg
Net: util=băng thông | sat=drop, overflow, retrans | err=nstat errors60 giây đầu tiên trên máy có sự cố (checklist kinh điển, thứ tự có chủ đích):
uptime (load trend) → dmesg | tail (OOM? lỗi disk? bug kernel?) → vmstat 1 (r, si/so, us/sy/wa) → mpstat -P ALL 1 (một core gánh hết? %si?) → pidstat 1 (ai ăn) → iostat -x 1 → free -m → sar -n DEV 1 → sar -n TCP,ETCP 1 (retrans!) → rồi mới đến perf/eBPF theo giả thuyết. Kỷ luật: đi từ rẻ đến đắt, từ rộng đến hẹp, mỗi bước loại trừ một lớp giả thuyết.
8. Anti-pattern
- strace vào process production chịu tải (đã nhấn mạnh, vẫn là tai nạn phổ biến nhất).
- Đo trung bình, bỏ tail — mọi công cụ histogram (biolatency, runqlat, HDR trong app) tồn tại vì p99 mới là trải nghiệm người dùng khi fan-out.
- Flame graph một lần lúc bình thường rồi kết luận — luôn so sánh baseline vs sự cố (differential flame graph).
- Benchmark microservice bằng
abtừ chính máy đó (đo cả contention của tool với app). - Sửa trước đo sau (“chắc là do GC”) — mọi thay đổi phải có số trước/sau cùng điều kiện.
- Quên rằng quan sát có giá: perf 99Hz ~vài %, nhưng uprobe vào hàm gọi 10 triệu lần/giây thì chính nó thành bottleneck.
9. Khi nào không nên tối ưu (việc đo)
Đừng xây “observability tối đa” trước khi có sự cố đầu tiên: bật mọi tracer, ghi mọi event = trả phí thường trực cho dữ liệu không ai xem. Nền tảng đúng cho 90% team: metrics chuẩn (USE cho host + RED cho service) + sar + PSI + khả năng bật eBPF/perf khi cần trong vài phút. Tracing sâu là công cụ điều tra, không phải trang sức dashboard.
Chương tiếp theo: gói mọi cơ chế đã học (namespace hóa tài nguyên, giới hạn bằng cgroup) thành sản phẩm định hình một thập kỷ hạ tầng: Chương 13: Container.