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.
Ghi chú nhanh
Dead Code Elimination là quá trình compiler/linker loại bỏ code hoặc data không còn được dùng trong binary cuối cùng.
Với GCC embedded, combo thường gặp là:
Compiler:
-ffunction-sections
-fdata-sections
Linker:
-Wl,--gc-sections
Ý tưởng là: mỗi function/data được đặt vào section riêng, rồi linker có thể “quét rác” những section không còn ai tham chiếu.
Vì sao embedded engineer nên quan tâm?
Trên PC, thừa vài KB code có thể không ai để ý. Trên MCU 64 KB flash, vài KB đó đôi khi là cả một buổi chiều đau đầu.
Trong project debug logger, khi tắt log ở production build, ta muốn những thứ này biến mất khỏi binary:
- hàm logger;
- buffer log;
- chuỗi format debug;
- code phụ trợ chỉ dùng cho debug.
Không phải chỉ “không chạy tới” là đủ. Với firmware production, càng ít thứ thừa càng tốt.
Ví dụ thực tế
Giả sử bạn có macro:
#ifdef ENABLE_DEBUG_LOG
#define DBG_LOG(...) Debug_Printf(__VA_ARGS__)
#else
#define DBG_LOG(...) ((void)0)
#endif
Khi ENABLE_DEBUG_LOG bị tắt, các lời gọi DBG_LOG() biến thành câu lệnh rỗng.
Nếu không còn code nào gọi Debug_Printf(), linker có cơ hội loại bỏ luôn function đó và những data chỉ phục vụ cho nó.
Cách kiểm tra có bị loại thật không?
Đừng đoán. Hãy mở file .map.
Tìm các symbol như:
Debug_Printf
Debug_UART_StartDmaIfNeeded
s_log_buffer
Nếu production build vẫn còn những symbol này, có thể:
- vẫn còn nơi nào đó gọi logger;
- function/data không được đặt vào section riêng;
- linker chưa bật
--gc-sections; - biến global bị giữ lại vì có tham chiếu gián tiếp.
Bẫy dễ gặp
Có người nghĩ chỉ cần if (debug_enabled) là đủ:
if (debug_enabled) {
Debug_Printf("value=%d
", value);
}
Cách này có thể vẫn giữ lại chuỗi log và function logger trong binary, tùy compiler tối ưu thế nào và biến debug_enabled có phải compile-time constant hay không.
Nếu muốn production build sạch, nên để điều kiện bật/tắt log xảy ra ở compile time bằng macro.
Cách nhớ
Macro tắt log ở compile time.
Linker GC dọn phần code không còn được gọi.
File .map là nơi kiểm chứng, không phải cảm giác.
Bài liên quan nên đọc tiếp
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.
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.
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.