本發(fā)明涉及一種開發(fā)人員追蹤和定位系統(tǒng)異常的方法,尤其涉及一種跟蹤嵌入式系統(tǒng)函數調用軌跡的方法。
背景技術:
當嵌入式系統(tǒng)出現某些不可恢復或者致命的錯誤時,系統(tǒng)會崩潰,常稱異常或者Trap,而此時的輸出也稱為Trap輸出,通常包括異常類型、處理器當前運行環(huán)境(CPU各寄存器)、當前任務的棧內容、函數調用軌跡等,如Linux操作系統(tǒng)發(fā)生Oops時的輸出。Trap輸出是開發(fā)人員用于追蹤和定位異常發(fā)生原因的必不可少的信息,尤其是函數調用軌跡用得最多,它能形象的反映出系統(tǒng)發(fā)生Trap時的函數執(zhí)行順序,甚至精確到觸發(fā)Trap的函數名字,同時它相對其他輸出也更易理解。
獲取函數調用軌跡的方法,通常是通過掃描格式化的當前任務的棧幀,并從中提取出包含函數返回地址,以及上一個棧幀的地址等信息,然后逐層回溯直到結束。此方法比較通用,適用于大多數體系結構,但是其缺點也是明顯的,主要在于當棧受到破壞時,是無法從中提取出函數調用軌跡的。比如軟件實現是嚴重依賴處理器體系結構的,包括其各寄存器用途,指令集(ISA,Instruction Set Architecture)用法,函數調用的壓棧、出棧方式,棧幀格式等,因此回溯的實現方法難度便大大增加了,也不容易維護、升級。
技術實現要素:
為解決上述技術問題,本發(fā)明的目的是提供一種回溯的實現方法較為簡單且操控性較高、方便開發(fā)人員診斷的跟蹤嵌入式系統(tǒng)函數調用軌跡的方法。
本發(fā)明的一種跟蹤嵌入式系統(tǒng)函數調用軌跡的方法,包括以下步驟:
1)編譯器分析程序代碼,并向每個非內聯(lián)的函數插入調用跟蹤函數的代碼;
2)當函數被執(zhí)行時調用跟蹤函數,跟蹤函數用于獲取和保存當前函數的返回地址;
3)直接輸出各函數返回地址,或者通過調試工具將各函數返回地址解析轉 化為函數名字并輸出。
進一步的,步驟1)中調用跟蹤函數的代碼被插入在函數的函數體中語句部分之前。
進一步的,編譯器內設置有編譯選項,所述編譯選項用于選擇是否開啟向每個非內聯(lián)的函數插入調用跟蹤函數的代碼這一功能。
進一步的,步驟2)中跟蹤函數將當前函數的返回地址保存為數組、鏈表或隊列。
具體的,跟蹤函數將當前函數的返回地址保存為數組。
具體的,跟蹤函數保存當前函數的返回地址時,按順序逐條由數組的頭部至尾部循環(huán)記錄在數組中。
具體的,連續(xù)相同的函數被執(zhí)行時,跟蹤函數僅保存一次該函數的返回地址。
借由上述方案,本發(fā)明至少具有以下優(yōu)點:當發(fā)生異常時,可以提供開發(fā)人員詳細明了的函數調用軌跡,回溯的實現方法較為簡單且操控性較高,方便開發(fā)人員診斷。
上述說明僅是本發(fā)明技術方案的概述,為了能夠更清楚了解本發(fā)明的技術手段,并可依照說明書的內容予以實施,以下以本發(fā)明的較佳實施例并配合附圖詳細說明如后。
附圖說明
圖1是本發(fā)明跟蹤嵌入式系統(tǒng)函數調用軌跡的方法的流程圖;
圖2是本發(fā)明實施例中增加編譯選項前的輸出結果圖;
圖3是本發(fā)明實施例中增加編譯選項后的輸出結果圖;
圖4是本發(fā)明中解析函數返回地址并轉化為函數名的輸出結果圖。
具體實施方式
下面結合附圖和實施例,對本發(fā)明的具體實施方式作進一步詳細描述。以下實施例用于說明本發(fā)明,但不用來限制本發(fā)明的范圍。
實施例一
參見圖1,本發(fā)明一較佳實施例所述的一種跟蹤嵌入式系統(tǒng)函數調用軌跡的方法,包括以下步驟:
1)編譯器分析程序代碼,并向每個非內聯(lián)的函數插入調用跟蹤函數的代碼;由于插入了代碼,使用該功能后,函數的代碼和占用的空間都變大了,同時多了一個函數調用的開銷,甚至函數的運行時間也變長了,為了方便軟件開發(fā)人員的使用,編譯器內設置有編譯選項,編譯選項用于選擇是否開啟向每個非內聯(lián)的函數插入調用跟蹤函數的代碼這一功能,換言之,這一功能可以選擇性的打開和關閉,比如可以在Debug版本打開,在Release版本關閉;在調試Trap時打開,在調試完后關閉。
本步驟需要對編譯器進行開發(fā)以提供支持,以提供一個編譯選項,比如當開啟時,向編譯器增加選項-DEBUG:stack_check=4,此時編譯器會向每個非內聯(lián)的函數中插入一條指令,該指令會調用并跳轉到一個跟蹤函數,如__stackcheck。
應當說明的是,選項-DEBUG:stack_check=4只是編譯選項的一種,本發(fā)明僅以選項-DEBUG:stack_check=4為例進行說明,凡是能夠實現步驟1)中選擇是否開啟向每個非內聯(lián)的函數插入調用跟蹤函數的代碼這一功能的編譯選項,均應落入本發(fā)明的保護范圍。
例如下面的Test_Func函數,在打開該選項后,其函數體多了調用__stackcheck的指令(如圖2和圖3所示),當然也多了為了調用函數__stackcheck而壓棧、出棧等動作。
應當說明的是,__stackcheck只是跟蹤函數的一種,本發(fā)明僅以__stackcheck為例進行說明,凡是能夠實現步驟2)中跟蹤函數功能的函數,均應落入本發(fā)明的保護范圍。
2)當函數被執(zhí)行時調用跟蹤函數,跟蹤函數用于獲取和保存當前函數的返 回地址(Return Address,RA);步驟1)中調用跟蹤函數的代碼被插入在函數的函數體中語句部分之前。
2.1在跟蹤函數中獲取函數返回地址
相對于現有技術中常見獲取函數調用軌跡的方法,其過分依賴CPU體系結構,本方法只有在跟蹤函數__stackcheck中去獲取函數返回地址時,才依賴CPU體系結構。例如在本發(fā)明的驗證體系結構中,使用以下的內聯(lián)匯編來獲得RA寄存器值。
RA寄存器中存放的是跟蹤函數__stackcheck退出后,返回到的地址,通常是調用跟蹤函數__stackcheck指令的下一條指令地址,例如在圖3中,是0x60011194地址,當__stackcheck運行結束,將RA值賦值給程序計數器PC,則函數__stackcheck返回。
2.2在跟蹤函數中保存函數返回地址
獲得函數返回地址后,需要將其保存到一塊預先開辟的空間,如數組、鏈表、或者隊列,本發(fā)明中,僅以數組為例詳細說明,鏈表和隊列的方式,本發(fā)明不再贅述。
全局變量:
其中,
func_trace_disable:表示是否停止跟蹤函數調用軌跡,默認為0;系統(tǒng)運行過程中可隨時將其置1,停止跟蹤,此時不會去獲得和存儲函數返回地址;重新置0表示繼續(xù)跟蹤。
header_idx:表示數組中最新記錄應該存儲到的位置下標。
FUNCTRACE_ITEMS_MAX:表示函數返回地址記錄的最大條數。
func_trace_entry:表示函數返回地址記錄的數組,能存儲的最大條數為 FUNCTRACE_ITEMS_MAX。
函數返回地址會按順序逐條記錄到數組中,越晚的記錄代表越新的函數調用。由于數組的存儲空間有限,而函數返回地址則是隨著系統(tǒng)的運行,不斷產生的;所以當記錄條數大于FUNCTRACE_ITEMS_MAX時,會從數組頭開始繼續(xù)記錄,換言之,先前記錄在數組頭的函數返回地址會被覆蓋。
同時,為了節(jié)約有限的存儲空間,連續(xù)相同的函數調用記錄,只被存儲一次。保存的過程如下:
3)直接輸出各函數返回地址,或者通過調試工具將各函數返回地址解析轉化為函數名字并輸出。
換言之,對于跟蹤函數調用軌跡的應用,可以存在以下方式:a)系統(tǒng)出現異常時,直接將各函數返回地址打印出來用于輔助調試;b)系統(tǒng)出現異常時,通過調試工具將各函數返回地址解析轉化為函數名字,得到函數調用軌跡,打印出來用于輔助調試;c)系統(tǒng)正常運行時,調試工具也可以通過分析這些函數調用軌跡,輔助分析系統(tǒng)的運行狀態(tài)。
根據步驟2)中記錄存儲的特性,先輸出舊的記錄,再輸出新的記錄,其中比header_idx越大的下標屬于越舊的記錄,并用0表示無效記錄。輸出過程如下:
通過工具鏈中的nm工具,將編譯后生成的ELF文件中的符號的地址和名字映射表,解析出來,并通過相應的過濾、篩選,如圖4所示,得到如下格式的輸出:
函數起始地址+函數代碼大小+類型+函數名字
所以,比RA寄存器值小的第一個函數起始地址,其對應的函數就是該RA記錄對應的,被調用的函數名。比如在圖4的例子中,RA記錄值0x60011190表示函數Test_Func被調用,0x 600111c4表示函數Test_Func_C被調用。
實施例二
本發(fā)明還提供一個具體實施過程,如下所示:
現有如下代碼,函數Test_Func_A調用Test_Func_B,Test_Func_B調用Test_Func_C,Test_Func_C調用Test_Func,Test_Func訪問了非法地址0。
當系統(tǒng)運行到此段代碼時,觸發(fā)非法地址存取Trap并崩潰,打印如下函數調用軌跡輸出:
對應的符號的地址和名字映射表如下,
由此可以發(fā)現,在調用完函數Test_Func后系統(tǒng)就發(fā)生了異常,再回去重新查看其代碼,就容易定位錯誤了。
以上所述僅是本發(fā)明的優(yōu)選實施方式,并不用于限制本發(fā)明,應當指出,對于本技術領域的普通技術人員來說,在不脫離本發(fā)明技術原理的前提下,還可以做出若干改進和變型,這些改進和變型也應視為本發(fā)明的保護范圍。