Mục tiêu của bài viết này là giới thiệu các công cụ (tools) rất hữu ích trong việc debug lỗi race condition. Đây vốn là một loại bug mất rất nhiều thời gian nếu chúng ta chỉ tìm lỗi bằng cách review code thủ công.
1. Bối cảnh và Phân loại Lỗi
Trong một dự án với mã nguồn rất phức tạp, hệ thống automation ghi nhận một lỗi. Lỗi này có các đặc điểm sau:
- Xuất hiện một cách ngẫu nhiên.
- Xác suất xuất hiện rất nhỏ (khoảng 1 lần / vài nghìn lần chạy liên tục).
- Nếu đặt thêm log (
print) vào bất cứ điểm nào trong mã nguồn, lỗi sẽ không bao giờ xuất hiện nữa.
Dựa trên các triệu chứng kinh điển này, lỗi được phân loại là Race Condition giữa các thread.
2. Thách thức: Phương pháp Debug
Với một mã nguồn phức tạp mà lập trình viên chỉ hiểu chi tiết được 10-15%, việc debug bằng cách review code và “tưởng tượng” luồng hoạt động của các thread để tìm ra điểm tranh chấp là không khả quan. Phương pháp này có thể mất vài tháng để đọc hiểu code.
Do đó, giải pháp được chọn là sửdụng các công cụ phân tích động (Dynamic Analysis Tools).
3. Phương pháp luận: Lựa chọn Công cụ
Sau khi tìm hiểu, có hai công cụ phù hợp được đưa ra xem xét:
- ThreadSanitizer (TSan):
- Ưu điểm: Không làm chậm tốc độ thực thi của chương trình nhiều.
- Nhược điểm: Yêu cầu phải thay đổi tùy chọn build (build option) của toàn bộ dự án, có thể đòi hỏi can thiệp phức tạp vào nhiều file
Makefilekhác nhau.
- Helgrind (thuộc bộ Valgrind):
- Ưu điểm: Chỉ cần bật tùy chọn build debug (
-g), không cần can thiệp vào hệ thống build. - Nhược điểm: Gây chậm chương trình một cách đáng kể. Có khả năng khi chạy dưới tool này, điều kiện thời gian (timing) thay đổi và lỗi sẽ không xuất hiện nữa.
- Ưu điểm: Chỉ cần bật tùy chọn build debug (
Quyết định: Sử dụng Helgrind do tính dễ tích hợp của nó.

4. Quá trình Phân tích và Kết quả
hi chạy chương trình dưới sự giám sát của Helgrind, đúng như dự đoán, lỗi (bug) không tái hiện được nữa (do tốc độ chạy bị chậm lại).
Tuy nhiên, Helgrind vẫn thành công trong việc report (báo cáo) một số điểm trong mã nguồn có khả năng gây ra race condition.
Lúc này, công việc chuyển từ “tìm bug” sang “phân tích các cảnh báo của tool” (có khoảng 5 điểm nghi vấn). Tiêu chí phân tích được đặt ra là:Tìm mối liên hệ logic giữa loại tài nguyên (ví dụ: global data) bị tranh chấp tại điểm nghi vấn và triệu chứng đầu ra (output) của bug đang gặp phải.
Sau khi sàng lọc, chỉ còn một điểm nghi vấn duy nhất có mối liên hệ logic.
5. Kết luận và Nguyên nhân Gốc (Root Cause)
Điểm nghi vấn này mô tả một khả năng race condition giữa hai thread:
- Một thread tạo request và gửi xuống phần cứng.
- Một thread là callback, được phần cứng tự động gọi lên (ở một context khác).
Phân tích sâu hơn cho thấy, bug này thực ra đã tồn tại từ rất lâu trong hệ thống nhưng không bị phát hiện do xác suất cực thấp. Gần đây, một kỹ sư khác đã thực hiện chỉnh sửa code theo hướng tối ưu hóa (optimize). Chính sự tối ưu hóa này đã vô tình làm thay đổi timing và làm tăng xác suất xuất hiện của race condition, và may mắn là hệ thống automation test đã ghi nhận được.
Với loại lỗi race condition trong một hệ thống source code phức tạp, việc chỉ đọc hiểu code để tìm lỗi có thể mất vài tháng. Tuy nhiên, bằng việc sử dụng các công cụ chuyên dụng, việc khoanh vùng và tìm ra lỗi trở nên dễ dàng và hiệu quả hơn rất nhiều.
