Cách tắt debug log trong production build
Ghi chú về macro ENABLE_DEBUG_LOG và cách biến debug log thành câu lệnh rỗng trong production build.
Ghi chú nhanh
Không nên xóa tay từng dòng printf() trước khi build production. Cách đó vừa mất công, vừa dễ xóa nhầm, lại làm lịch sử Git nhìn rất mệt.
Thay vào đó, nên dùng macro để bật/tắt log theo build configuration:
#ifdef ENABLE_DEBUG_LOG
#define DBG_LOG(fmt, ...) Debug_Printf(fmt, ##__VA_ARGS__)
#else
#define DBG_LOG(fmt, ...) ((void)0)
#endif
Khi tắt ENABLE_DEBUG_LOG, lời gọi DBG_LOG() biến thành câu lệnh rỗng hợp lệ.
Vì sao không nên dùng printf trực tiếp khắp nơi?
Nếu source đầy những dòng như thế này:
printf("state=%d
", state);
thì về sau bạn rất khó kiểm soát:
- build nào được phép có log;
- log đi ra UART hay SWO;
- log có bị loại khỏi production không;
- module nào đang in quá nhiều.
Một macro trung gian như DBG_LOG() giúp bạn đổi backend hoặc tắt toàn bộ log mà không phải đi săn từng dòng printf().
Ví dụ thực tế
Trong debug build:
DBG_LOG("MSR raw bit=%d", bit);
có thể được map sang UART logger.
Trong production build, cùng dòng đó trở thành:
((void)0);
Firmware vẫn compile bình thường, nhưng log không còn tạo timing overhead.
Kết hợp với linker GC
Macro chỉ là nửa đầu câu chuyện. Nếu cấu hình build tốt, linker còn có thể loại bỏ cả những function/data không còn được tham chiếu.
Combo thường dùng:
-ffunction-sections
-fdata-sections
-Wl,--gc-sections
Sau đó kiểm tra file .map để chắc chắn logger không còn nằm trong production image.
Bẫy dễ gặp
Đừng viết macro theo kiểu làm mất kiểm tra cú pháp của argument quá sớm nếu bạn vẫn muốn compiler giúp bắt lỗi trong debug build.
Ngoài ra, cẩn thận với expression có side effect:
DBG_LOG("value=%d", read_sensor_and_clear_flag());
Khi production build tắt log, hàm read_sensor_and_clear_flag() cũng không chạy nữa. Vì vậy argument của log không nên chứa logic quan trọng.
Cách nhớ
Debug log được phép giúp ta nhìn firmware.
Debug log không được phép thay đổi firmware production.
Bài liên quan nên đọc tiếp
- Dead Code Elimination là gì trong embedded C?
- Thiết kế hệ thống UART logging non-blocking trên STM32
Thấy nội dung này hữu ích?
Lưu lại hoặc chia sẻ cho người cũng đang học firmware, BIOS/UEFI và embedded systems.
Nội dung liên quan
Một số bài viết, ghi chú hoặc project có liên quan đến nội dung bạn vừa đọc.
Dead Code Elimination là gì trong embedded C?
Ghi chú về cách GCC và linker loại bỏ function/data không dùng bằng function sections, data sections và gc-sections.
UART non-blocking logger trên STM32 với DMA + Ring Buffer
Thiết kế debug UART non-blocking cho STM32: tránh printf, xử lý mất log với DMA, dùng ring buffer và DMA callback.
Biến note thành bài viết hoàn chỉnh
Notes là nơi ghi nhanh khái niệm.