Úrovně logování pro efektivní ladění embedded systémů
Ladění pomocí printf často vede k zahlcení konzole logy, zejména u složitých systémů. Úrovně logování tento problém řeší tím, že umožňují dynamicky řídit úroveň podrobnosti (verbosity) přímo za běhu přes CLI. Jedná se o standardní praxi sahající až do éry Unix SysLog, kde se pro každý modul definují úrovně: critical, error, info, debug, paranoid.
Příkazy jako ll usb debug aktivují ladění pro USB modul, zatímco ll pll info nastaví informativní úroveň pro PLL. Při výpočtu koeficientů PLL se na úrovni info zobrazí pouze výsledek, zatímco na úrovni debug i kroky iteračního algoritmu.
Implementace CLI rozhraní a logovacích maker
Program je sestaven z modulů s unikátními ID typu facility_t. CLI (shell/TUI) zpracovává příkazy LogLevel (ll) pro nastavení úrovně podrobností pro jednotlivé moduly.
V kódu se používají makra LOG_ERROR, LOG_WARNING atd., která fungují podobně jako printf, ale s parametrem facility:
void log_write(log_level_t level, facility_t facility, const char* format, ...) {
#ifdef HAS_STREAM
bool res = log_write_prefix(level, facility);
if(res) {
va_list va;
va_start(va, format);
cli_vprintf(format, va);
va_end(va);
log_write_end();
}
#endif
}
void LOG_WARNING(facility_t facility, const char* format, ...) {
#ifdef HAS_STREAM
if(log_write_prefix(LOG_LEVEL_WARNING, facility)) {
va_list va;
va_start(va, format);
cli_vprintf(format, va);
va_end(va);
log_write_end();
}
#endif
}
void LOG_DEBUG(facility_t facility, const char* format, ...) {
#ifdef HAS_STREAM
if(log_write_prefix(LOG_LEVEL_DEBUG, facility)) {
va_list va;
va_start(va, format);
cli_vprintf(format, va);
va_end(va);
log_write_end();
}
#endif
}
Direktiva HAS_STREAM umožňuje vypnout logy v release verzi bez úpravy kódu – makra se při překladu nahradí prázdným obsahem.
Definice facility a mapování na řetězce
Facility je výčet (enum) s unikátními ID pro moduly (SYS=1, GPIO, SPI atd.), které lze podmíněně kompilovat:
typedef enum {
SYS= 1 ,
UNKNOWN_FACILITY = 2,
#ifdef HAS_ACC
LG_ACC,
#endif
#ifdef HAS_GPIO
GPIO,
#endif
#ifdef HAS_SPI
SPI,
#endif
ALL_FACILITY, /*must be last in enum*/
} facility_t;
Pole FacilityInfo_t převádí ID na řetězce pro logy:
typedef struct{
facility_t facility;
char* name;
}FacilityInfo_t;
#ifdef HAS_MPU
#define MPU_FACILITY_DIAG { .facility = LG_MPU, .name = "Mpu" },
#else
#define MPU_FACILITY_DIAG
#endif
static const FacilityInfo_t FacilityInfo[] = {
// ...
EEPROM_FACILITY_DIAG
MPU_FACILITY_DIAG
// ...
};
Serializátory pro diagnostiku struktur
Pro interpretaci konstant a struktur jsou potřeba serializátory v souborech xxx_diag.c. Převádějí binární hodnoty na čitelné řetězce:
const char* DacLevel2Str(uint8_t code){
const char *name="?";
switch(code){
case DAC_LEV_CTRL_INTERNALY: name="internally"; break;
case DAC_LEV_CTRL_LOW: name="low"; break;
case DAC_LEV_CTRL_MEDIUM: name="medium"; break;
case DAC_LEV_CTRL_HIGH: name="high"; break;
default: name="??"; break;
}
return name;
}
const char* SpiConfigToStr(const SpiConfig_t* const Config) {
strcpy(text,"");
if(Config) {
sprintf(text, "SPI%u", Config->num);
snprintf(text, sizeof(text), "%sRate:%u Hz,", text, Config->bit_rate_hz);
// ... další pole
}
return text;
}
CLI je volá při inicializaci a logování pro výpis konfigurací.
Standardní úrovně log_level_t
Definice úrovní jako enum se zápornými hodnotami pro správné řazení:
typedef enum {
LOG_LEVEL_UNKNOWN = -5,
LOG_LEVEL_PARANOID = -4,
LOG_LEVEL_DEBUG = -3,
LOG_LEVEL_PROTECTED = -2,
LOG_LEVEL_NOTICE = -1,
LOG_LEVEL_INFO = 0,
LOG_LEVEL_WARNING = 1,
LOG_LEVEL_ERROR = 2,
LOG_LEVEL_CRITICAL = 3,
LOG_LEVEL_COVERAGE = 4,
LOG_LEVEL_DISABLE = 5,
LOG_LEVEL_LAST = LOG_LEVEL_DISABLE
} log_level_t;
- PARANOID (-4): Maximální podrobnost, riziko „záplavy“.
— Editorial Team
Zatím žádné komentáře.