在嵌入式開(kāi)發(fā)中,日志模塊在日常開(kāi)發(fā)和調(diào)試定位中扮演著非常重要的角色,好的日志工具能輔助工程師快速了解固件運(yùn)行狀態(tài)、錯(cuò)誤定位、流程跟蹤等。本文將介紹一個(gè)新的日志框架:defmt(deferred formatting,延遲格式化)
。
眾所周知,GDB 是許多嵌入式開(kāi)發(fā)人員的首選開(kāi)發(fā)和調(diào)試工具,但是在許多實(shí)時(shí)性較高、或需要長(zhǎng)期調(diào)試運(yùn)行的場(chǎng)景則顯得不那么方便,因此固件的狀態(tài)通常通過(guò)串口或網(wǎng)絡(luò)實(shí)時(shí)發(fā)送給主機(jī),如嵌入式 C 工程師經(jīng)常使用的EasyLogger
就能使用多種單片機(jī)外設(shè)或存儲(chǔ)日志,也能顯示日志級(jí)別,格式化輸出等。但是如果大量使用日志來(lái)輸出運(yùn)行狀態(tài),則會(huì)占用一些的 flash 空間以及 CPU 時(shí)間。
defmt
則做了非常棒的優(yōu)化,defmt
使用單片機(jī)內(nèi)部 rtt 外設(shè)來(lái)作為輸出端。同時(shí)能節(jié)省大量的格式化帶來(lái)的空間浪費(fèi)和時(shí)間浪費(fèi)。
有哪些優(yōu)勢(shì)?
不占用太多空間
defmt
優(yōu)化了格式化字符串和代碼路徑字符串等只讀數(shù)據(jù)段名稱(chēng),這些字符串會(huì)被編譯到 elf 文件但不會(huì)包含到 bin 文件,因此格式化顯示的過(guò)程在主機(jī)段完成,因此需要解析固件的 elf 文件才能正常打印出。同時(shí)單片機(jī)無(wú)需發(fā)送額外的格式化的字符串,只需打印顯示的數(shù)據(jù)內(nèi)容以及當(dāng)前日志的格式化索引即可。因此對(duì)于重度依賴(lài)日志的系統(tǒng)可以大量使用而不會(huì)消耗太多的flash空間。
不消耗額外時(shí)間
defmt
的日志流是輸出到內(nèi)存緩存中,有主機(jī)主動(dòng)讀取,因此不會(huì)消耗單片機(jī)太多的 IO 讀寫(xiě)資源。因此同時(shí)由于每條日志僅僅只需輸出日志索引和需要打印的變量?jī)?nèi)容,無(wú)需再輸出格式化字符串和代碼位置字符串,因此不會(huì)消耗太多的時(shí)間,但是極高優(yōu)化了日志的傳輸,讓日志生產(chǎn)端和日志消費(fèi)端都能快速讀寫(xiě)。對(duì)于高實(shí)時(shí)性的場(chǎng)景,不會(huì)再出現(xiàn)添加日志則無(wú)法復(fù)現(xiàn)異常 bug 或添加日志會(huì)導(dǎo)致其他 bug 的問(wèn)題。
日志級(jí)別設(shè)置
defmt
有 5 個(gè)日志等級(jí),分別是trace
,debug
,info
,warn
,error
,覆蓋所有等級(jí)的日志輸出,通過(guò)環(huán)境變量RUST_LOG
或DEFMT_LOG
指定日志的等級(jí)。
顏色顯著提示
時(shí)間戳展示
可以使能#[timestamp]
屬性開(kāi)啟時(shí)間戳打印功能。方便定位日志間代碼運(yùn)行的所號(hào)的時(shí)間
日志位于文件中的位置標(biāo)記
再 debug 模式中,每套日志都會(huì)顯示該條日志所在的文件名路徑以及行數(shù),再 vscode 中點(diǎn)擊該字符串能快速定位到該行代碼,快速查看日志的上下文環(huán)境。
輕松集成到項(xiàng)目
基于 Rust 的 cargo 管理工具,只需再 toml 中添加依賴(lài)即可,無(wú)需其他移植工作。
支持中途在線查看日志
通過(guò)命令probe-rs attach
即可查看日志,無(wú)需重啟單片機(jī),且能立即查看到歷史日志,對(duì)于緊急查看日志狀態(tài) 時(shí),需要了解之前的日志分析 bug 非常有用。