輸出調試日志是調試程序的一種常見方法,尤其是處理那些難以捉摸的多線程錯誤、隨機崩潰等問題時。通過在合適的位置輸出調試日志,可以幫助我們更快地定位問題所在。對于不熟悉的代碼,經常打日志也有助于快速理解代碼的執行流程和功能。在大型項目中,通常會先實現一套自己的調試日志框架,主要有兩個目的:
- 統一日志風格和存儲:確保日志格式一致,并且有統一的存儲方式,這有助于用戶更容易地報告問題。
- 方便開發人員:開發人員能夠輕松地記錄日志,從而快速調試問題。
Wine 的調試日志實現
調試通道(debug channel)Wine 定義了調試通道的概念來分類日志,將日志的記錄和實際的輸出分離,無需重新編譯 Wine,就能動態地靈活控制 Wine 運行時的日志輸出。
- 每個調試通道有一個唯一的名字, 長度不超過14個可見的 ASCII 字符, 一般一個模塊至少定義了一個調試通道,比如 gdi32.dll 模塊,有一個名稱叫 gdi 的調試通道。
- 復雜的模塊,為了細分日志定義了多個調試通道,比如 gdi32.dll 模塊,還定義了clipping、region、font、bitmap、print、bitblt 等調試通道。
-
調試通道在代碼里面來看實際是一個
__wine_debug_channel
的結構體,剛好16個字節,非常符合 UNIX 簡單原則的哲學理念:
struct__wine_debug_channel { unsignedcharflags; charname[15]; };1. 包含
include/wine/debug.h
;2. 然后用WINE_DEFAULT_DEBUG_CHANNEL
或WINE_DEFAULT_DEBUG_CHANNEL
宏來聲明。- 日志一次只發送給一個調試通道。
- 代碼里增加一個新的調試通道,非常簡單:
-
要知道一個模塊定義了哪些調試通道,只需這樣搜索該模塊的所有源碼:
git grep _DEBUG_CHANNEL
。
向調試通道發送日志Wine 把日志分成了4個級別,從高到低依次是:fixme/ err(or)/ warn/ trace,對應的提供了4個宏來輸出不同級別的日志到調試通道:FIXME/ ERR/WARN/ TRACE,非常簡單、清晰。
#define__WINE_DPRINTF(dbcl,dbch) do{if(__WINE_GET_DEBUGGING(dbcl,(dbch))){ struct__wine_debug_channel*const__dbch=(dbch); constenum__wine_debug_class__dbcl=__WINE_DBCL##dbcl; __WINE_DBG_LOG #define__WINE_DBG_LOG(args...) wine_dbg_log(__dbcl,__dbch,__FUNCTION__,args);}}while(0) #defineTRACE__WINE_DPRINTF(_TRACE,__wine_dbch___default) #defineTRACE_(ch)__WINE_DPRINTF(_TRACE,&__wine_dbch_##ch) #defineTRACE_ON(ch)__WINE_IS_DEBUG_ON(_TRACE,&__wine_dbch_##ch) #defineWARN__WINE_DPRINTF(_WARN,__wine_dbch___default) #defineWARN_(ch)__WINE_DPRINTF(_WARN,&__wine_dbch_##ch) #defineWARN_ON(ch)__WINE_IS_DEBUG_ON(_WARN,&__wine_dbch_##ch) #defineFIXME__WINE_DPRINTF(_FIXME,__wine_dbch___default) #defineFIXME_(ch)__WINE_DPRINTF(_FIXME,&__wine_dbch_##ch) #defineFIXME_ON(ch)__WINE_IS_DEBUG_ON(_FIXME,&__wine_dbch_##ch) #defineERR__WINE_DPRINTF(_ERR,__wine_dbch___default) #defineERR_(ch)__WINE_DPRINTF(_ERR,&__wine_dbch_##ch) #defineERR_ON(ch)__WINE_IS_DEBUG_ON(_ERR,&__wine_dbch_##ch)最終都是調用函數 wine_dbg_log 來打日志:
intwine_dbg_log(enum__wine_debug_classcls,struct__wine_debug_channel*channel, constchar*func,constchar*format,...) { intret; va_listvalist; if(!(__wine_dbg_get_channel_flags(channel)&(1<return-1; va_start(valist,format); ret=funcs.dbg_vlog(cls,channel,func,format,valist); va_end(valist); returnret; }其中的 funcs.dbg_vlog 初始化時會指向 default_dbg_vlog:
staticintdefault_dbg_vlog(enum__wine_debug_classcls,struct__wine_debug_channel*channel, constchar*func,constchar*format,va_listargs)在Wine線程創建成功后 funcs.dbg_vlog 會指向 ntdll/debugtools.c 的
staticintNTDLL_dbg_vlog(enum__wine_debug_classcls,struct__wine_debug_channel*channel, constchar*function,constchar*format,va_listargs)
程序運行前開啟調試通道
用這樣的格式定義環境變量:WINEDEBUG=[class][+/-]channel[,[class2][+/-]channel2]
其中:
-
class:是代表 fixme/ err/ warn/ trace 這4個日志級別的一個單詞,如果沒有指定就開關所有的日志級別。
-
channel:就是要開關的調試通道的名稱,all 代表所有的通道。
-
+:就是開啟指定調試通道的對應的日志級別。
-
-:就是關閉指定調試通道的對應的日志級別。
WINEDEBUG=warn+all WINEDEBUG=warn+dll,+heap WINEDEBUG=fixme-all,warn+cursor,+relay如果沒有定義
WINEDEBUG
環境變量,發給每個調試通道的 fixme 和 err 級別的日志都會輸出;Wine 默認同時開啟運行的調試通道是 256個,由這個宏 MAX_DEBUG_OPTIONS
決定。關鍵代碼如下:enum__wine_debug_class { __WINE_DBCL_FIXME, __WINE_DBCL_ERR, __WINE_DBCL_WARN, __WINE_DBCL_TRACE, __WINE_DBCL_INIT=7/*lazyinitflag,bit7*/ }; staticunsignedchardefault_flags=(1<if(opt)returnopt->flags; } /*nooptionforthischannel*/ if(channel->flags&(1
僅標記作用的調試通道
- pid:在每個日志的前面插入當前進程的 ID號,格式:%04x。
- tid:在每個日志的前面插入當前線程的 ID號,格式:%04x。
- timestamp:在每個日志的前面插入時間戳,相對系統啟動的時間、單位秒、保留3位小數。
比較特殊的高級調試通道
- seh:記錄所有的異常情況,快速定位程序崩潰地址。
0009seh:raise_exceptioncode=c00002b5flags=0addr=0xc4194beip=0c4194betid=0009 0009seh:raise_exceptioninfo[0]=00000000 0009seh:raise_exceptioneax=00000006ebx=0b6f4f58ecx=08b44020edx=0033d15cesi=0dfde520edi=0df80020 0009seh:raise_exceptionebp=0033d0d0esp=0033d0c0cs=0023ds=002bes=002bfs=0063gs=006bflags=00210206
- relay:無需修改代碼,記錄程序調用 Wine 實現的所有 API 的詳細參數和返回值。
... 0017:CallKERNEL32.CreateFileA(7ea8e936"CONIN$",c0000000,00000003,00000000,00000003,00000000,00000000)ret=7ea323fd 0017:RetKERNEL32.CreateFileA()retval=00000023ret=7ea323fd ...
- snoop:無需修改代碼,記錄程序對第三方 native 模塊的所有導出函數的調用參數和返回值。Snoop 是自己檢查 stack 數據和反匯編來探測函數調用約定、參數和返回地址的,如果探測錯了會影響程序的穩定,甚至導致程序崩潰,建議僅在非常規情況下使用。
... traceSNOOP_SetupDLLhmod=0x4ae90000,name=gdiplus.dll 0043:CALLMSVCR100_CLR0400.wcscpy_s(04b7c808,0000000c,0033d188L"gdiplus.dll")ret=79203d6b 0043:RETMSVCR100_CLR0400.wcscpy_s()retval=00000000ret=79203d6b 0043:CALLMSVCR100_CLR0400.memset(0033dc9c,00000000,00000010)ret=792bd727 0043:RETMSVCR100_CLR0400.memset()retval=0033dc9cret=792bd727 0043:CALLgdiplus.GdiplusStartup()ret=04b8e775 0043:RETgdiplus.GdiplusStartup(03c00ae0,0033dc9c,0033dcec)retval=00000000ret=04b8e775 ... 0043:CALLgdiplus.GdipCreateFromHWND()ret=04b8e8b3 0043:RETgdiplus.GdipCreateFromHWND(0004003a,0033e988)retval=00000000ret=04b8e8b3 ...Relay 和 snoop 的缺點是記錄的日志巨大導致程序反應非常慢,只建議在沒有任何思路、一籌莫展時使用。
程序運行中動態開關調試通道1. set + channel:開啟指定通道的所有 fixme/ err/ warn/ trace 日志。2. set - channel:關閉指定通道的所有 fixme/ err/ warn/ trace 日志。3. set class + channel:開啟指定通道的fixme/ err/ warn/ trace日志中的某一類。class 替換為fixme/ err/ warn/ trace這4個單詞中的任意一個。4. set class - channel:關閉指定通道的fixme/ err/ warn/ trace日志中的某一類。Winedbg 的 set 命令也只能設置在
WINEDEBUG
已經開啟了的調試通道。如果沒有在WINEDEBUG
里面定義的,就會提示: Unable to find debug channel xxx
。-
方法1:運行任務管理器(wine taskmgr),打開“進程”標簽頁,右鍵選中進程,在右鍵菜單里面選中“編輯調試頻道”。這個方法只能開關事先在
WINEDEBUG
環境變量里面列出的調試通道。 - 方法2:在 Winedbg 里 attach 指定的 Wine 進程,然后用 set 命令:
-
方法3:在Winedbg 里attach 指定的 Wine 進程,手動修改
debug_options
和nb_debug_options
的數據。因為debug_options
是按照調試通道名稱字符串比較排序的,所以開啟多個通道需要手動排序。這個方法適合運行程序時忘記設置WINEDEBUG
,但是想查看某個調試通道日志時又不想重新運行程序的時候使用。
Wine-dbg>set+win Unabletofinddebugchannelwin Wine-dbg>pdebug_options[0] {flags=0,name=""} Wine-dbg);setdebug_options[0].flags=0xf Wine-dbg>pdebug_options[0] {flags=f,name=""} Wine-dbg>setdebug_options[0].name[0]='w' Wine-dbg>setdebug_options[0].name[1]='i' Wine-dbg>setdebug_options[0].name[2]='n' Wine-dbg>setdebug_options[0].name[3]=0 Wine-dbg>pdebug_options[0] {flags=f,name="楷n"} Wine-dbg>p&debug_options[0] 0xf77092d0 Wine-dbg>x/s0xf77092d1 win Wine-dbg>pnb_debug_options 0 Wine-dbg>setnb_debug_options=1
-
方法4:一般正式發布的 libwine.so 沒有調試符號,就只能反匯編定位
debug_options
和nb_debug_options
的地址。
- 先查詢 __wine_dbg_get_channel_flags 的偏移,readelf -s libwine.so.1.0 | grep __wine_dbg :
109:00005110206FUNCGLOBALDEFAULT12__wine_dbg_set_channel_fl@@WINE_1.0 166:00005030223FUNCGLOBALDEFAULT12__wine_dbg_get_channel_fl@@WINE_1.0 172:00005390253FUNCGLOBALDEFAULT12__wine_dbg_set_functions@@WINE_1.0
- 再查詢 libwine.so 的基地址得到 __wine_dbg_get_channel_flags 的地址:
Wine-dbg>infoshare ModuleAddressDebuginfoName(23modules) ... ELFf75d5000-f778c000Dwarflibwine.so.1@,/opt/cxoffice/bin/../lib/libwine.so.1 ...
- 接著看 __wine_dbg_get_channel_flags 反匯編:
Wine-dbg>disass0xf75d5000+0x5030 0xf75da030__wine_dbg_get_channel_flagsinlibwine.so.1:pushebp 0xf75da031__wine_dbg_get_channel_flags+0x1inlibwine.so.1:movebp,esp 0xf75da033__wine_dbg_get_channel_flags+0x3inlibwine.so.1:pushedi 0xf75da034__wine_dbg_get_channel_flags+0x4inlibwine.so.1:pushesi 0xf75da035__wine_dbg_get_channel_flags+0x5inlibwine.so.1:pushebx 0xf75da036__wine_dbg_get_channel_flags+0x6inlibwine.so.1:call0xf75d81a0 0xf75da03b__wine_dbg_get_channel_flags+0xbinlibwine.so.1:addebx,0x19dfc5 0xf75da041__wine_dbg_get_channel_flags+0x11inlibwine.so.1:subesp,0x1c 0xf75da044__wine_dbg_get_channel_flags+0x14inlibwine.so.1:movecx,[ebx+0x134]-->nb_debug_options 0xf75da04a__wine_dbg_get_channel_flags+0x1ainlibwine.so.1:cmpecx,0xffffffff 0xf75da04d__wine_dbg_get_channel_flags+0x1dinlibwine.so.1:jz0xf75da0f0 0xf75da053__wine_dbg_get_channel_flags+0x23inlibwine.so.1:testecx,ecx 0xf75da055__wine_dbg_get_channel_flags+0x25inlibwine.so.1:jz0xf75da0c0 0xf75da057__wine_dbg_get_channel_flags+0x27inlibwine.so.1:moveax,[ebp+0x8] 0xf75da05a__wine_dbg_get_channel_flags+0x2ainlibwine.so.1:movedi,ecx 0xf75da05c__wine_dbg_get_channel_flags+0x2cinlibwine.so.1:movdword[ebp-0x1c],0x0 0xf75da063__wine_dbg_get_channel_flags+0x33inlibwine.so.1:addeax,0x1 0xf75da066__wine_dbg_get_channel_flags+0x36inlibwine.so.1:mov[ebp-0x24],eax 0xf75da069__wine_dbg_get_channel_flags+0x39inlibwine.so.1:leaeax,[ebx+0x260]-->debug_options 0xf75da06f__wine_dbg_get_channel_flags+0x3finlibwine.so.1:mov[ebp-0x28],eax 0xf75da072__wine_dbg_get_channel_flags+0x42inlibwine.so.1:jmp0xf75da07f__wine_dbg_get_channel_flags+0x4finlibwine.so.1 0xf75d3074__wine_dbg_get_channel_flags+0x44inlibwine.so.1:leaesi,[esi] ... 0xf75d81a0:movebx,[esp] 0xf75d81a3:retGCC 習慣通過 ebx 寄存器來引用全局變量,所以
nb_debug_options
的地址是:0xf75d303b+0x19dfc5+0x134
;debug_options
的地址是:0xf75d303b+0x19dfc5+0x260
;然后參考方法 2 的 set 命令修改內存即可。Relay 調試通道實現原理
在 LoadLibrary 內部,如果檢查到已經開啟了 relay 通道,并且已加載 Wine 內建的 DLL 文件,那么就調用 RELAY_SetupDLL 來解析 DLL 的導出函數表(IMAGE_DIRECTORY_ENTRY_EXPORT)。對于導出表中的 AddressOfFunctions 數組中的每個條目,先備份原始值,然后將每個條目值修改為可跳轉 relay_call 函數的 hack 函數地址。hack 函數在 faked PE 模塊中,固定為 24 字節大小,形式如下:0x7e7a4210:pushesp 0x7e7a4211:push0x50000 0x7e7a4216:call0x7e7a6a40__wine_spec_get_pc_thunk_eaxingdi32 0x7e7a421b:leaeax,[eax+0xadd31] 0x7e7a4221:pusheax 0x7e7a4222:calldword[eax+0x4]-->relay_call 0x7e7a4225:ret0x14不同的 API 變化的只是里面的數字常量。在 GetProcAddress 內部檢查是否開啟了 relay 通道,如已開啟就調用 RELAY_GetProcAddress 返回 hack 的函數地址。以 user32 模塊的GetMenu 舉例,返回的 hack 的 GetMenu 函數如下:
FARPROCRELAY_GetProcAddress(HMODULEmodule,constIMAGE_EXPORT_DIRECTORY*exports, DWORDexp_size,FARPROCproc,DWORDordinal,constWCHAR*user) { structrelay_private_data*data; conststructrelay_descr*descr=(conststructrelay_descr*)((constchar*)exports+exp_size); if(descr->magic!=RELAY_DESCR_MAGIC||!(data=descr->private))returnproc;/*norelaydata*/ if(!data->entry_points[ordinal].orig_func)returnproc;/*notarelayedfunction*/ if(check_from_module(debug_from_relay_includelist,debug_from_relay_excludelist,user)) returnproc;/*wewanttorelayit*/ returndata->entry_points[ordinal].orig_func; } Wine-dbg>disassproc 0x7e8d7cf0:pushesp 0x7e8d7cf1:push0x10130 0x7e8d7cf6:call0x7e8dae44__wine_spec_get_pc_thunk_eaxinuser32 0x7e8d7cfb:leaeax,[eax+0xd9fcd] 0x7e8d7d01:pusheax 0x7e8d7d02:calldword[eax+0x4] 0x7e8d7d05:ret0x4 Wine-dbg>p0x7e8d7cfb+0xd9fcd+0x4 0x7e9b1ccc Wine-dbg>x/4x0x7e9b1ccc 0x7e9b1ccc:7bc76c2c7bc77270001246407e8d615d Wine-dbg>disass0x7bc76c2c 0x7bc76c2crelay_callinntdll:pushebp 0x7bc76c2drelay_call+0x1inntdll:movebp,esp 0x7bc76c2frelay_call+0x3inntdll:pushesi 0x7bc76c30relay_call+0x4inntdll:pushedi 0x7bc76c31relay_call+0x5inntdll:pushecx 0x7bc76c32relay_call+0x6inntdll:pushdword[ebp+0x10] 0x7bc76c35relay_call+0x9inntdll:pushdword[ebp+0xc] 0x7bc76c38relay_call+0xcinntdll:pushdword[ebp+0x8] 0x7bc76c3brelay_call+0xfinntdll:call0x7bc76835relay_trace_entry[../wine_git/dlls/ntdll/relay.c:334]inntdll 0x7bc76c40relay_call+0x14inntdll:movzxecx,byte[ebp+0xe] ...原始的 GetMenu 地址:
Wine-dbg>infolocal 0x7bc772f4RELAY_GetProcAddress+0x75:(0033eac8) HMODULEmodule=0x7e8d0000(parameter[EBP+8]) IMAGE_EXPORT_DIRECTORY*exports=0x7e9ad19c(parameter[EBP+12]) DWORDexp_size=0x4b2c(parameter[EBP+16]) FARPROCproc=0x7e8d7cf0(parameter[EBP+20]) DWORDordinal=0x130(parameter[EBP+24]) WCHAR*user=0x0(nil)(parameter[EBP+28]) structrelay_private_data*data=0x124640(local[EBP-12]) structrelay_descr*descr=0x7e9b1cc8(local[EBP-16]) Wine-dbg>p*descr {magic=0xdeb90001,relay_call=0x7bc76c2c,relay_call_regs=0x7bc77270, private=0x124640,entry_point_base="恌怲h",entry_point_offsets=0x7e9744f8,arg_types=0x7e975048} Wine-dbg>x/14x0x124640 0x00124640:7e8d0000000000017265737500003233 0x00124650:00000000000000000000000000000000 0x00124660:00000000000000000000000000000000 0x00124670:7e9130107e9aee17 Wine-dbg>x/10x0x00124670+0x130*8 0x00124ff0:7e9288907e9b018c7e92c6d07e9b0194 0x00125000:7e927fc07e9b01a37e92cc307e9b01be 0x00125010:7e92b6807e9b01d3 Wine-dbg>x/10c0x7e9b018c 0x7e9b018c:GetMenuGe Wine-dbg>disass0x7e928890 0x7e928890GetMenu[../wine_git/dlls/user32/menu.c:4208]inuser32:leaecx,[esp+0x4] 0x7e928894GetMenu+0x4[../wine_git/dlls/user32/menu.c:4208]inuser32:andesp,0xfffffff0 0x7e928897GetMenu+0x7[../wine_git/dlls/user32/menu.c:4208]inuser32:pushdword[ecx-0x4] 0x7e92889aGetMenu+0xa[../wine_git/dlls/user32/menu.c:4208]inuser32:pushebp 0x7e92889bGetMenu+0xb[../wine_git/dlls/user32/menu.c:4208]inuser32:movebp,esp 0x7e92889dGetMenu+0xd[../wine_git/dlls/user32/menu.c:4208]inuser32:pushedi 0x7e92889eGetMenu+0xe[../wine_git/dlls/user32/menu.c:4208]inuser32:pushesi 0x7e92889fGetMenu+0xf[../wine_git/dlls/user32/menu.c:4208]inuser32:pushebx 0x7e9288a0GetMenu+0x10[../wine_git/dlls/user32/menu.c:4208]inuser32:pushecx 0x7e9288a1GetMenu+0x11[../wine_git/dlls/user32/menu.c:4208]inuser32:call0x7e8d5b60__x86.get_pc_thunk.bxinuser32 ...Relay_call 里面調用 relay_trace_entry/relay_trace_exit 來記錄函數的進和出,以及調用真實的API。
Snoop 調試通道的實現原理
- 在 LoadLibrary 內部檢查到已開啟 snoop 通道并且加載了 native 的 DLL,就調用 SNOOP_SetupDLL 解析 DLL 的導出函數,對每個導出函數動態分配一塊可讀寫和執行的 hack 內存。
- 在 GetProcAddress 內部檢查到已開啟 snoop 通道,就調用 SNOOP_GetProcAddress,對 hack 內存填充一個可以跳到 SNOOP_Entry 函數的 jmp 指令,然后返回這個 hack 內存塊的首地址。
- SNOOP_Entry 探測函數的返回地址、調用約定、調用參數,打印出來,然后把當前 EIP 設置成真實的導出函數,把返回地址設置為 SNOOP_Return。
結束
解決實際問題的時候, 我們先收集日志,然后重點看 err:、fixme:、seh: 的日志,一般能從中找到問題的相關線索。
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。
舉報投訴
-
調試
+關注
關注
7文章
582瀏覽量
33971 -
程序
+關注
關注
117文章
3791瀏覽量
81153
原文標題:Wine 開發系列——如何使用 Wine 日志調試問題
文章出處:【微信號:linux_deepin,微信公眾號:深度操作系統】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
Wine原理介紹和開發教程
說起 Wine,稍微資深一點的 Linux 用戶應該都聽過,但是真要說起 Wine 到底是怎么回事,可能大多數人不見得說得清。這篇文章會簡單地介紹 Wine 的工作原理,以及如何開始 Wine
1個工具4類日志,幫你解決99%的問題
——[LuaTools]多功能下載調試工具,簡單又高效。 LuaTools新版下載/使用教程: https://docs.openluat.com/Luatools/ 本文特別分享LuaTools日志相關內容。 一
780E開發板之errDump錯誤日志上報,操作方法解析
?# 一、errDump功能 LuatOS-Air錯誤日志上報功能模塊名叫:errDump,errDump對“量產投放市場的設備,遠程調試初步定位問題”至關重要,強烈建議客戶一定要使用此功能
InDTU300系列產品如何輸出實時日志?
電腦連接InDTU維護串口/串口2,登錄后在配置工具設置界面,選擇高級模式。
找到“其他配置(應用擴展配置)”,”是否為調試模式“項選擇“是(串口2)”,調試模式等級選擇顯示詳細日志。
點擊右下角
發表于 07-25 06:05
鴻蒙開發系統基礎能力:ohos.hilog 日志打印
hilog日志系統,使應用/服務可以按照指定級別、標識和格式字符串輸出日志內容,幫助開發者了解應用/服務的運行狀態,更好地調試程序。
esp32s2的JTAG調試不行是什么原因導致的?
調試功能好像和我的應用有關,我使能了一些自己的應用功能之后,直接復位運行正常,JTAG調試不行。
現在調試問題已經明顯影響了開發效率。
openocd和debugger consol
發表于 06-24 07:36
奇怪!應用的日志呢??
1. 問題回顧 問題背景 是在進行中臺應用中間件遷移過程中,發現存在 項目啟動失敗 或者 項目正常啟動 (jsf正常掛載并正常運行,mq正常發送和消費)但是 無任何日志打印 現象。 更奇怪 的是不打
ElfBoard ELF 1開發板-putty保存日志的方法
開發板2.標題欄右鍵選擇Change Settings...3.打開設置窗口,點擊Session->Logging,右邊選擇All session output,點擊Browse選擇保存路徑,點擊Apply4.在會話窗口輸入ls、df等命令進行簡單測試5.在日志保存
發表于 02-29 17:04
突破!清華大學在電子鼻傳感器仿生嗅聞方向取得新進展
近日,清華大學機械系在電子鼻仿生嗅聞研究中取得新進展,相關研究成果以“Sniffing Like a Wine Taster: Multiple Overlapping Sniffs (MOSS
DevEco Studio 4.1帶來多種調試能力,助力鴻蒙原生應用開發高效調試
,HUAWEI DevEco Studio不斷挖掘、汲取開發者的需求和建議,經過持續打磨和系列升級實現新突破,將全力支持鴻蒙原生應用開發。 為助力高效開發,快速定位問題,DevEco
單片機斷電記憶日志 多串口助手波形圖像彩色日志 Stm32嵌入式FLASH保存日志管理工具
):僅在開發期間有用的調試消息,級別較低,在正式發布時一般不顯示。
Buffer(陣列):以直接顯示級別與Debug相同的陣列數值。
Fixer(固定位置):固定位置顯示日志,不保存到日志
發表于 01-19 11:28
評論