amp 呼叫鏈 談談iOS獲取呼叫鏈

2021-10-13 05:44:35 字數 2434 閱讀 1843

本文由雲+社群發表

ios開發過程中難免會遇到卡頓等效能問題或者死鎖之類的問題,此時如果有呼叫堆疊將對解決問題很有幫助。那麼在應用中如何來實時獲取函式的呼叫堆疊呢?本文參考了網上的一些博文,講述了使用mach thread的方式來獲取呼叫棧的步驟,其中會同步講述到棧幀的基本概念,並且通過對乙個demo的彙編**的講解來方便理解獲取呼叫鏈的原理。

一、棧幀等幾個概念

先丟擲乙個棧幀的概念,解釋下什麼是棧幀。

下面再丟擲幾個概念:

暫存器中的fp,sp,lr,pc。

暫存器是和cpu聯絡非常緊密的一小塊記憶體,經常用於儲存一些正在使用的資料。對於32位架構armv7指令集的arm處理器有16個暫存器,從r0到r15,每乙個都是32位位元。呼叫約定指定他們其中的一些暫存器有特殊的用途,例如:

r0-r3:用於存放傳遞給函式的引數;

r4-r11:用於存放函式的本地引數;

r11:通常用作楨指標fp(frame pointer暫存器),棧幀基址暫存器,指向當前函式棧幀的棧底,它提供了一種追溯程式的方式,來反向跟蹤呼叫的函式。

r12:是內部程式呼叫暫時暫存器。這個暫存器很特別是因為可以通過函式呼叫來改變它;

不同指令集的暫存器數量可能會不同,pc、lr、sp、fp也可能使用其中不同的暫存器。後面我們先忽略r11等暫存器編號,直接用fp,sp,lr來講述

如下圖所示,不管是較早的幀,還是呼叫者的幀,還是當前幀,它們的結構是完全一樣的,因為每個幀都是基於乙個函式,幀伴隨著函式的生命週期一起產生、發展和消亡。在這個過程中用到了上面說的暫存器,fp幀指標,它總是指向當前幀的底部;sp棧指標,它總是指向當前幀的頂部。這兩個暫存器用來定位當前幀中的所有空間。編譯器需要根據指令集的規則小心翼翼地調整這兩個暫存器的值,一旦出錯,引數傳遞、函式返回都可能出現問題。

其實這裡這幾個暫存器會滿足一定規則,比如:

lr總是在上乙個棧幀(也就是呼叫當前棧幀的棧幀)的頂部,而棧幀之間是連續儲存的,所以lr也就是當前棧幀底部的上乙個位址,以此類推就可以推出所有函式的呼叫順序。這裡注意,棧底在高位址,棧向下增長

而由此我們可以進一步想到,通過sp和fp所指出的棧幀可以恢復出母函式的棧幀,不斷遞迴恢復便恢復除了呼叫堆疊。向下面**一樣,每次遞迴pc儲存的*(fp + 1)其實就是返回的位址,它在呼叫者的函式內,利用這個位址我們可以通過符號表還原出對應的方法名稱。

while(fp) while (fp);

5、恢復執行緒thread_resume

thread_resume(main_thread);

6、還原符號表

enumeratesegment(header, [&](struct load_command *command) {

if (command->cmd == lc_symtab) {

struct symtab_command *symcmd = (struct symtab_command *)command;

uint64_t baseaddr = 0;

enumeratesegment(header, [&](struct load_command *command) {

if (command->cmd == lc_segment_64) {

struct segment_command_64 *segcmd = (struct segment_command_64 *)command;

if (strcmp(segcmd->segname, seg_linkedit) == 0) {

baseaddr = segcmd->vmaddr - segcmd->fileoff;

return true;

return false;

if (baseaddr == 0) return false;

nlist_64 *nlist = (nlist_64 *)(baseaddr + slide + symcmd->symoff);

uint64_t strtable = baseaddr + slide + symcmd->stroff;

uint64_t offset = uint64_max;

int best = -1;

for (int k = 0; k < symcmd->nsyms; k++) {

nlist_64 &sym = nlist[k];

uint64_t d = pcslide - sym.n_value;

if (offset >= d) {

offset = d;

best = k;

if (best >= 0) {

nlist_64 &sym = nlist[best];

std::cout << "symbol: " << (char *)(strtable + sym.n_un.n_strx) << std::endl;

return true;

return false;

參考

amp 呼叫鏈 鏈式呼叫方法的實現原理和方法

1.什麼是鏈式呼叫?person person new person setname fog setage 18 set man setjob software engineer 2.優勢和好處 有以上的 好處顯而易見 量大幅度減少,邏輯集中清晰明了,且易於檢視和修改。3.背後的實現原理 一般而言,...

函式呼叫鏈

將不同引數和返回值的函式組織到陣列裡並依次呼叫 目前尚不支援void函式 include include include include include include using namespace std 基類 class linkedfunctionbase protected std sha...

呼叫鏈監控

以前都是單體應用,都在乙個系統內完成。而現在都是微服務,乙個請求進來,需要呼叫多個服務才能完成。出了問題,我們很難定位到底在哪個環節出了問題。1.快速定位問題。通過呼叫鏈監控系統,我們能很快定位到哪個服務出了問題。2.專案拓撲圖。當服務越來越複雜時,我們都無法準確知道服務之間都依賴關係。通過呼叫鏈監...