Đợt trước có lần mình gặp một lỗi khá khó liên quan đến tiêu đề của bài viết. Sơ qua về hệ thống thì dự án mình import source code của 1 số thư viện bên thứ 3.
Project sẽ định kỳ chạy tool valgrind để scan các lỗi liên quan đến memory. Đợt đó valgrind report 1 lỗi liên quan đến truy cập vào vùng nhớ đã được cấp phát nhưng chưa memset. Dạng như garbage memory.
Sau khi mình enable debug symbol và chạy lại lần nữa thì valgrind thậm chí còn chỉ ra được chính xác dòng nào trong source code truy cập vào vùng đó.
Tuy nhiên vấn đề khó khăn ở đây là valgrind báo lỗi nằm trong api của thư viện bên thứ 3. Mình dành ra 1, 2 hôm để cố đọc hiểu flow của api đấy nhưng thực sự thì vẫn không hiểu. Mình chỉ hiểu được 1 chút loanh quanh ở đoạn code lỗi. Đó là nó so sánh 2 chuỗi string, dạng như mã hóa từ dữ liệu đầu vào. Tuy nhiên nó chia nhỏ 2 chuỗi string ra thành 2 mảng 2 chiều, mỗi phần tử của 1 mảng sẽ chứa 1 phần của chuỗi đích và chuỗi nguồn. Sau đó nó có vòng for để so sánh từng phần và lỗi truy cập ở trong vòng for đó.
Loanh quanh mất mấy hôm mình cũng thấy chán vì phải debug vào thư viện của bên khác, tự nhiên mình nghĩ giá như valgrind hỗ trợ tính năng chạy kết hợp cùng gdb. Có nghĩa thay vì chỉ chạy valgrind để scan lỗi thì valgrind sẽ dừng lại khi phát hiện lỗi và gdb sẽ kết nối vào chương trình cần debug, tính năng này mình thấy rất hữu ích. Nếu là người làm ra tool valgrind chắc mình cũng muốn hỗ trợ tính năng này. Mình lên google search thì thấy đúng là valgrind hỗ trợ thật.
Sau khi setup để sử dụng song song valgrind cùng với gdb. Mình chạy lại valgrind 1 lần nữa thì lúc này gdb dừng lại tại thời điểm chương trình truy cập vào garbage memory kia. Dùng gdb debug backtrace để xem luồng gọi hàm trong api thì mình phát hiện ra có 1 chỗ trong thư viện họ sử dụng malloc trong switch case. Hầu hết họ dùng hàm calloc để cấp phát, tuy nhiên có 1 chỗ họ lại dùng hàm malloc. Chỗ cấp phát đó sử dụng để khởi tạo bộ nhớ cho mảng 2 chiều chứa chuỗi nguồn.
Chương trình chạy bình thường không bị lỗi, vì 1 phần garbage mem của chuỗi nguồn sẽ chứa ký tự bất định, khi so sánh với chuỗi đích thì sẽ khác nhau và nó cũng dừng lại chứ không so sánh tiếp. Vậy nên nếu không dùng valgrind thì cũng không phát hiện ra được lỗi này.
Memset bộ nhớ sau khi allocate là 1 quy tắc bắt buộc khi lập trình C. Quy tắc này tuy đơn giản nhưng giúp chúng ta tránh được rất nhiều các lỗi hóc búa xảy ra về sau.
