前言
相信無(wú)論大佬還是小白,都在開(kāi)發(fā)中遇到過(guò)棧溢出的問(wèn)題,而且因?yàn)闆](méi)有明確日志,難以定位問(wèn)題的根源。Stack Overflow 社區(qū)的命名也由此而生,而到現(xiàn)在雖然Stack Overflow因?yàn)榇竽P鸵呀?jīng)幾乎要涼涼了,但是棧溢出的問(wèn)題仍然困擾著許多開(kāi)發(fā)者。
正好RT-Trace發(fā)布了他們的內(nèi)測(cè)新功能——棧保護(hù),與此同時(shí),采集時(shí)長(zhǎng)也有了大幅提升,在我的板子上甚至可以穩(wěn)定采集 三分鐘。那我們就來(lái)看看他的效果和實(shí)用性吧。
測(cè)試環(huán)境
星火一號(hào)開(kāi)發(fā)板
rt-trace工具
使用方法和效果

可以看到使用方法順延了先前trace功能的配置界面,加上了兩個(gè)框,來(lái)選擇需要保護(hù)的線(xiàn)程和需要被保護(hù)的棧底空間大小,使用起來(lái)還是很簡(jiǎn)單的

配置成功后就可以去trace_view界面測(cè)試了,這里我星火一號(hào)上跑了一個(gè)遞歸爆棧測(cè)試程序,沒(méi)有優(yōu)化
#include#include#include#ifndefRT_USING_NANO#include#include#include#include#endif/* RT_USING_NANO */rt_thread_tstack_thread =NULL;#defineTHREAD_PRIORITY 25#defineTHREAD_STACK_SIZE 512#defineTHREAD_TIMESLICE 5intmain(void){ while(1) { rt_thread_mdelay(500); }}#defineMAX_RECURSION_DEFAULT 5 // 默認(rèn)最大遞歸次數(shù)staticintmax_recursion = MAX_RECURSION_DEFAULT;// 可控制的最大遞歸次數(shù)void*get_stack_top_addr(){ return(void*)((uint32_t)stack_thread->stack_addr + stack_thread->stack_size);}void*get_stack_bottom_addr(){ return(void*)((uint32_t)stack_thread->stack_addr);}/*** 遞歸棧溢出測(cè)試函數(shù)* 每次遞歸僅創(chuàng)建一個(gè)32位變量* @param depth 當(dāng)前遞歸深度*/voidrecursive_stack_overflow(intdepth){ volatileuint32_ta =0x12345678;// 創(chuàng)建一個(gè)32位變量并賦值 staticvoid*last_a_addr =NULL; // 獲取棧邊界地址 void*stack_bottom_addr =get_stack_bottom_addr(); void*stack_top_addr =get_stack_top_addr(); if(depth !=1) { uint32_tstack_used = (uint32_t)last_a_addr - (uint32_t)&a; rt_kprintf("[Depth:%2d] var_a addr:0x%08X,stack_used: %d\n ", depth, &a, stack_used); } else { rt_kprintf("[Depth:%2d] var_a addr:0x%08X\n ", depth, &a); } // 終止條件:達(dá)到最大遞歸次數(shù) if(depth >= max_recursion) { rt_kprintf("[Depth:%2d] 已達(dá)到最大遞歸次數(shù) %d,終止遞歸\n", depth, max_recursion); return; } last_a_addr = (void*)&a; // 短暫延遲,便于觀察輸出 rt_thread_mdelay(10); // 遞歸調(diào)用 recursive_stack_overflow(depth +1);}/*** 棧保護(hù)線(xiàn)程入口函數(shù)* @param p 線(xiàn)程參數(shù)*/voidstack_protect_thread(void*p){ // 獲取棧信息 void*stack_bottom_addr =get_stack_bottom_addr(); void*stack_top_addr =get_stack_top_addr(); uint32_tstack_size = stack_thread->stack_size; // 打印線(xiàn)程啟動(dòng)信息 rt_kprintf("線(xiàn)程啟動(dòng):\n"); rt_kprintf(" 棧底地址: 0x%08X\n", stack_bottom_addr); rt_kprintf(" 棧頂?shù)刂? 0x%08X\n", stack_top_addr); rt_kprintf(" 棧大小: %d 字節(jié)\n", stack_size); rt_kprintf(" 最大遞歸次數(shù): %d\n", max_recursion); rt_kprintf("----------------------------------------\n"); rt_thread_mdelay(20); // 開(kāi)始遞歸測(cè)試 recursive_stack_overflow(1); rt_kprintf("----------------------------------------\n"); rt_kprintf("遞歸測(cè)試結(jié)束\n");}/*** 設(shè)置最大遞歸次數(shù)(外部可調(diào)用)* @param count 最大遞歸次數(shù),<=0 則使用默認(rèn)值?*/void?set_max_recursion(int?argc,?char?**argv){? ? int?count =?atoi(argv[1]);? ? if?(count <=?0)? ? {? ? ? ? max_recursion = MAX_RECURSION_DEFAULT;? ? ? ? rt_kprintf("已設(shè)置最大遞歸次數(shù)為默認(rèn)值: %d\n", MAX_RECURSION_DEFAULT);? ? }? ? else? ? {? ? ? ? max_recursion = count;? ? ? ? rt_kprintf("已設(shè)置最大遞歸次數(shù)為: %d\n", max_recursion);? ? }}MSH_CMD_EXPORT(set_max_recursion, 設(shè)置最大遞歸次數(shù)(參數(shù)為次數(shù)));/**?* 創(chuàng)建棧保護(hù)測(cè)試線(xiàn)程?*/void?create_stack_protect_thread(void){? ? // 創(chuàng)建線(xiàn)程(棧大小2048字節(jié))? ? stack_thread =?rt_thread_create(? ? ? ? "stack_thread", ? ? ? // 線(xiàn)程名稱(chēng)? ? ? ? stack_protect_thread,?// 入口函數(shù)? ? ? ? RT_NULL, ? ? ? ? ? ? ?// 參數(shù)? ? ? ? 512, ? ? ? ? ? ? ? ? ?// 棧大小? ? ? ? 20, ? ? ? ? ? ? ? ? ? // 優(yōu)先級(jí)? ? ? ? 10? ? ? ? ? ? ? ? ? ? // 時(shí)間片? ? );? ? if?(stack_thread != RT_NULL)? ? {? ? ? ? rt_thread_startup(stack_thread);? ? ? ? rt_kprintf("棧保護(hù)線(xiàn)程創(chuàng)建成功\n");? ? }? ? else? ? {? ? ? ? rt_kprintf("棧保護(hù)線(xiàn)程創(chuàng)建失敗\n");? ? }}MSH_CMD_EXPORT(create_stack_protect_thread, 創(chuàng)建棧保護(hù)測(cè)試線(xiàn)程);
經(jīng)過(guò)遞歸三次,五次,八次(第八次溢出)的測(cè)試后,捕獲到的trace圖像是這樣的



0-4s 和 0-8s 遞歸三次以及遞歸五次,都沒(méi)有踩到我們的報(bào)警閾值
8-12s 遞歸八次時(shí),在第6次踩到我們的64字節(jié)報(bào)警線(xiàn)
msh />create_stack_protect_thread棧保護(hù)線(xiàn)程創(chuàng)建成功msh />線(xiàn)程啟動(dòng): 棧底地址:0x20004160 棧頂?shù)刂?0x20004360 棧大小: 512字節(jié) 最大遞歸次數(shù):10----------------------------------------[Depth: 1] var_a addr:0x20004330[Depth: 2] var_a addr:0x20004310,stack_used:32[Depth: 3] var_a addr:0x200042F0,stack_used:32[Depth: 4] var_a addr:0x200042D0,stack_used:32[Depth: 5] var_a addr:0x200042B0,stack_used:32[Depth: 6] var_a addr:0x20004290,stack_used:32[Depth: 7] var_a addr:0x20004270,stack_used:32[Depth: 8] var_a addr:0x20004250,stack_used:32[E/kernel.sched] thread:stack_tstack overflow
從上面的調(diào)試日志可以看到,棧溢出前64字節(jié),正好是第六次遞歸的時(shí)候,這說(shuō)明這個(gè)棧溢出報(bào)警起碼準(zhǔn)確度沒(méi)問(wèn)題。
但是細(xì)心的小伙伴可能發(fā)現(xiàn)了另一個(gè)問(wèn)題,距離棧底似乎還有很大的空間,但是棧溢出“提前發(fā)生了”,我們的報(bào)警也提前了。這是因?yàn)槲覀兊倪f歸函數(shù)中調(diào)用了其他的函數(shù),經(jīng)過(guò)調(diào)試發(fā)現(xiàn),造成棧溢出的直接原因是rt_thread_mdelay,最后棧溢出時(shí),第八次遞歸進(jìn)入后,sp位置在0x20004240,而調(diào)用rt_thread_mdelay最大深度能到0x2000412c,此時(shí)已經(jīng)遠(yuǎn)遠(yuǎn)超過(guò)了我們的棧底,所以溢出和警報(bào)都是正常的。
總結(jié)
體驗(yàn)下來(lái),rt-trace的棧保護(hù)功能確實(shí)能很好的提示我線(xiàn)程棧的使用情況,可調(diào)的閾值也給了用戶(hù)比較大的自由度,這次的升級(jí)trace的采集時(shí)間也大大加長(zhǎng)了,之前只能采12秒,現(xiàn)在可以以分鐘為單位進(jìn)行采集。
不過(guò)還是有些可以繼續(xù)提升的部分,比如現(xiàn)在的棧保護(hù)只能保護(hù)一個(gè)線(xiàn)程,如果能實(shí)時(shí)自動(dòng)保護(hù)所有的線(xiàn)程,可能使用的體驗(yàn)和帶來(lái)的幫助會(huì)更好。
-
嵌入式系統(tǒng)
+關(guān)注
關(guān)注
41文章
3719瀏覽量
133225 -
內(nèi)存
+關(guān)注
關(guān)注
9文章
3174瀏覽量
76168 -
堆棧溢出
+關(guān)注
關(guān)注
0文章
10瀏覽量
8098
發(fā)布評(píng)論請(qǐng)先 登錄
【RT-Trace】功能再升級(jí)!GDB?Server功能?+?Flash一鍵燒錄,嵌入式開(kāi)發(fā)更加便捷!|?技術(shù)集結(jié)
揭秘!基于RT-Thread探究“優(yōu)先級(jí)反轉(zhuǎn)”下的任務(wù)調(diào)度究竟是什么樣的?| 技術(shù)集結(jié)
基于“互聯(lián)網(wǎng)+”與北斗的精準(zhǔn)定位智慧停車(chē)系統(tǒng)
如何使用嵌入式的內(nèi)存分配管理技術(shù)
trace32 for rt-thread support的基本使用及系統(tǒng)插件原理
jvm內(nèi)存溢出該如何定位解決
頂堅(jiān)國(guó)產(chǎn)芯單北斗執(zhí)法儀:精準(zhǔn)定位鐵路隱患,保障行車(chē)安全
重磅預(yù)售!RT-Trace調(diào)試工具
RT-Trace調(diào)試工具正式發(fā)布!
RT-Trace初體驗(yàn)一之使用Trace功能調(diào)試Cortex-M4 | 技術(shù)集結(jié)
【直播預(yù)告】RT-Trace調(diào)試工具V1.1.0版本功能全解析 | 問(wèn)學(xué)直播
【干貨分享】RT-Trace國(guó)產(chǎn)調(diào)試工具 | 技術(shù)集結(jié)
【直播預(yù)告】RT-Trace 全新版本發(fā)布|ITM輸出 MemoryWatch 功能首發(fā)實(shí)測(cè)! | 問(wèn)學(xué)直播

告別 “棧溢出”!用 RT-Trace 工具精準(zhǔn)定位嵌入式系統(tǒng)內(nèi)存隱患 | 技術(shù)集結(jié)
評(píng)論