Bài 34.124 phút đọc
Bốn công cụ và khi nào dùng cái nào
Sau bài này bạn sẽ làm được
- Chọn đúng công cụ cho từng loại triệu chứng
- Nêu chi phí tốc độ của từng công cụ
- Biết cờ dịch tối thiểu cho mỗi công cụ
- Biết cặp sanitizer nào không dùng chung được
Bốn công cụ, bốn việc khác nhau. Sai lầm phổ biến nhất là mở GDB ngay khi có lỗi. GDB là công cụ cuối cùng bạn nên chạm tới, vì ba công cụ kia thường chỉ thẳng ra dòng lỗi mà không cần bạn hỏi gì.
#Bốn công cụ
| Công cụ | Nó làm gì | Cờ dịch | Khi nào chạy |
|---|---|---|---|
| AddressSanitizer | Bắt mọi lỗi truy cập bộ nhớ ngay tại chỗ | -fsanitize=address | Trong mọi lần chạy kiểm thử |
| UndefinedBehaviorSanitizer | Bắt tràn số, dịch bit sai, chia cho không | -fsanitize=undefined | Trong mọi lần chạy kiểm thử |
| Valgrind | Bắt lỗi bộ nhớ và biến chưa khởi tạo, không cần dịch lại | Không cần cờ, chỉ cần -g | Khi không dịch lại được, hoặc cần bắt biến chưa khởi tạo |
| GDB | Dừng chương trình và soi vào bên trong | -g3 -O0 | Khi ba công cụ trên đã im lặng mà kết quả vẫn sai |
#Chọn theo triệu chứng
| Triệu chứng | Chạy cái này trước | Vì sao |
|---|---|---|
| Segmentation fault | ASan | Nó in ra dòng truy cập sai và dòng cấp phát, không cần bạn hỏi |
| Chương trình dùng bộ nhớ tăng dần | ASan hoặc Valgrind | Cả hai đều liệt kê chỗ cấp phát chưa giải phóng |
| Kết quả sai ở -O2, đúng ở -O0 | UBSan | Gần như chắc chắn có UB ở đâu đó, xem Bài 33.2 |
| Kết quả khác nhau giữa hai lần chạy | Valgrind | Biến chưa khởi tạo, và chỉ Valgrind bắt được |
| Đứng im, không chạy tiếp | GDB, gắn vào tiến trình | Xem nó đang kẹt ở đâu bằng backtrace |
| Kết quả sai một cách nhất quán | GDB | Đây là lỗi logic, không công cụ nào tự tìm được |
| Sập trên máy khách, không tái hiện được | GDB với core dump | Đọc lại trạng thái lúc sập |
| Kết quả sai khi chạy nhiều luồng | ThreadSanitizer | Chỉ nó bắt được đua dữ liệu |
#Chi phí và giới hạn
| Công cụ | Chậm hơn | Tốn thêm bộ nhớ | Giới hạn chính |
|---|---|---|---|
| ASan | Khoảng 2 lần | Khoảng 3 lần | Không bắt biến chưa khởi tạo, không bắt tràn số |
| UBSan | Khoảng 1.2 lần | Không đáng kể | Chỉ bắt được UB có kiểm tra lúc chạy |
| Valgrind | 20 tới 50 lần | Khoảng 2 lần | Rất chậm, và không bắt tràn trên ngăn xếp |
| TSan | 5 tới 15 lần | 5 tới 10 lần | Không dùng chung với ASan được |
| GDB | Không, khi chưa dừng | Không đáng kể | Cần bạn biết nên hỏi gì |
Chạy ba lượt trong tích hợp liên tục
# Luot 1: ASan va UBSan, bat het loi bo nho va UB
gcc -std=c11 -g3 -O1 -fno-omit-frame-pointer \
-fsanitize=address,undefined -fno-sanitize-recover=all \
-o thu_san *.c
./thu_san
# Luot 2: TSan, bat dua du lieu. PHAI dich rieng.
gcc -std=c11 -g3 -O1 -fsanitize=thread -pthread -o thu_tsan *.c
./thu_tsan
# Luot 3: Valgrind, bat bien chua khoi tao
gcc -std=c11 -g -O1 -o thu_vg *.c
valgrind --leak-check=full --track-origins=yes --error-exitcode=1 ./thu_vg
# Ba luot nay chay duoc song song, nen tong thoi gian la luot cham nhat.#Kết hợp thế nào
Quy trình chuẩn khi gặp lỗi
1. Tai hien duoc chua?
Chua -> tim dau vao nho nhat gay loi truoc da.
Roi -> di tiep.
2. Chay ban ASan va UBSan.
Co bao -> doc bao cao, sua, quay lai buoc 1.
Im lang -> di tiep.
3. Chay Valgrind.
Co bao -> thuong la bien chua khoi tao. Sua, quay lai buoc 1.
Im lang -> di tiep.
4. Den day thi khong con loi BO NHO nao. Con lai la loi LOGIC.
Mo GDB. Dat diem dung truoc cho nghi ngo.
Neu mot bien bi doi gia tri bat thuong: dung watchpoint.
5. Sua xong: viet mot bai kiem thu tai hien dung loi do.#Trên Windows thì sao
terminal
# MinGW-W64 8.1.0, bản dùng trong giáo trình này
gcc -fsanitize=address t.c -o t
ld.exe: cannot find -lasan collect2.exe: error: ld returned 1 exit status
gcc -fsanitize=undefined t.c -o t
ld.exe: cannot find -lubsan collect2.exe: error: ld returned 1 exit status
gdb --version
GNU gdb (GDB) 8.1
| Công cụ | Linux | macOS | Windows |
|---|---|---|---|
| GDB | Có | Có, hoặc dùng LLDB | Có, qua MinGW |
| ASan | Có | Có | Có, qua MSVC hoặc clang-cl |
| UBSan | Có | Có | Một phần, qua Clang |
| Valgrind | Có | Bản cũ, hay hỏng | Không |
| TSan | Có | Có | Không |
| Dr. Memory | Có | Không | Có, thay cho Valgrind |
| Application Verifier | Không | Không | Có, của Microsoft |
Bốn cách để có sanitizer trên Windows
# 1. WSL2, cach de nhat: mot ban Linux day du
wsl --install -d Ubuntu
sudo apt install build-essential gdb valgrind
gcc -fsanitize=address,undefined -g prog.c -o prog
# 2. MSVC co ASan tu Visual Studio 2019 phien ban 16.9
cl /fsanitize=address /Zi prog.c
# 3. clang-cl, ban Clang dung ABI cua MSVC
clang-cl -fsanitize=address -Zi prog.c
# 4. Dr. Memory, cong cu thay Valgrind tren Windows
drmemory -- prog.exe
# Giao trinh nay dung cach 1 cho Chuong 34, va chay thang tren
# Windows cho Chuong 35.Tự làm thử
- Cài WSL2 với Ubuntu và dịch một chương trình có lỗi bằng
-fsanitize=address. - Đo thời gian chạy của một chương trình ở bốn cấu hình: thường, ASan, UBSan, Valgrind.
- Thử dịch chung
-fsanitize=addressvới-fsanitize=threadvà đọc thông báo lỗi. - Viết một chương trình đọc biến chưa khởi tạo và xác nhận ASan im lặng còn Valgrind thì không.
- Thêm ba lượt kiểm tra vào Makefile của một dự án của bạn.
- Thử
ASAN_OPTIONS=halt_on_error=0trên chương trình có nhiều lỗi.
Trình chấm điểm tự động sẽ được bổ sung ở giai đoạn sau. Hiện tại bạn tự chạy thử trên máy.
Tóm tắt
- Thứ tự đúng là ASan và UBSan trước, Valgrind sau, GDB cuối cùng.
- Ba công cụ đầu không cần bạn biết gì về lỗi, còn GDB chỉ trả lời câu hỏi bạn đặt.
- ASan chậm hai lần, UBSan gần như miễn phí, Valgrind chậm hai tới năm mươi lần.
- ASan không bắt biến chưa khởi tạo, và không dùng chung được với TSan.
- MinGW không có
libasanvàlibubsan, còn Valgrind không có bản Windows. Dùng WSL2 hoặc MSVC.