gcc中__builtin_return_address學習與使用


一、說明

Built-in函數格式

void * __builtin_return_address(unsigned int level)

此函數返回當前函數或其調用者之一的返回地址。 level 參數是向上掃描調用堆棧的幀數。 值 0 產生當前函數的返回地址,值 1 產生當前函數調用者的返回地址,依此類推。 內聯時的預期行為是函數返回函數返回到的地址。 要解決此問題,請使用
noinline 函數屬性。

level 參數必須是一個常量整數。

在某些機器上,可能無法確定除當前函數之外的任何函數的返回地址; 在這種情況下,或者當到達棧頂時,該函數返回 0 或隨機值。 此外,__builtin_frame_address 可用於確定是否已到達棧頂。

可能需要對返回值進行額外的后處理,請參閱 __builtin_extract_return_addr。

使用非零參數調用此函數可能會產生不可預知的影響,包括使調用程序崩潰。 因此,當 -Wframe-address 選項生效時,會診斷出被認為不安全的調用。 此類調用僅應在調試情況下進行。

 

二、內核中使用舉例

基於linux-5.10

static const char *find_and_get_symobls(unsigned long caller_addr)
{
    struct h_node *cur_node = NULL;
    struct h_node *new_node = NULL;
    const char *cur_symbol = NULL;
    unsigned int cur_key = 0;

    cur_key = (unsigned int) caller_addr & 0x1f;
    // Try to find symbols from history records
    hash_for_each_possible(tbl, cur_node, node, cur_key) {
        if (cur_node->addr == caller_addr) {
            cur_symbol = cur_node->symbol;
            break;
        }
    }
    // Symbols not found. Add new records
    if (!cur_symbol) {
        new_node = kzalloc(sizeof(struct h_node), GFP_KERNEL);
        if (!new_node)
            return NULL;
        new_node->addr = caller_addr;
        sprint_symbol(new_node->symbol, caller_addr);
        cur_symbol = new_node->symbol;
        hash_add(tbl, &new_node->node, cur_key);
    }
    return cur_symbol;
}

static void my_freq_qos_update_request(void *data, struct freq_qos_request *req, int value)
{
    int cid = 0;
    const char *caller_info = find_and_get_symobls((unsigned long)__builtin_return_address(1));
    if (caller_info) {
        cid = find_qos_in_cluster(req->qos);
        trace_freq_qos_user_setting(cid, req->type, value, caller_info); //perf_tracker_trace.h中注冊
    }
}

打印格式:

mtkPowerMsgHdl-954     [003] ...1   199.220011: freq_qos_user_setting: cid=0 type=2 value=2000000 caller=store_scaling_max_freq+0x5c/0xa0

caller_info 是個 char* 型指針,trace中打印字符串指針的方法如下:

TRACE_EVENT(freq_qos_user_setting,
    TP_PROTO(
        int cid,
        int type,
        int value,
        const char *caller
    ),

    TP_ARGS(cid, type, value, caller),

    TP_STRUCT__entry(
        __field(int, cid)
        __field(int, type)
        __field(int, value)
        __string(caller, caller)
    ),

    TP_fast_assign(
        __entry->cid = cid;
        __entry->type = type;
        __entry->value = value;
        __assign_str(caller, caller);
    ),

    TP_printk("cid=%d type=%d value=%d caller=%s",
        __entry->cid,
        __entry->type,
        __entry->value,
        __get_str(caller)
    )
);

 

三、使用總結

1. 由於編譯器優化,優化成inline的情況出現,__builtin_return_address(n)與底n層函數調用沒有確切的對應關系了,使用之前需要驗證。

qos_debug_work_func_1 //應該算作__builtin_return_address(1)
    freq_qos_update_request //應該算作__builtin_return_address(0)
        trace_android_vh_freq_qos_update_request //1
            mtk_freq_qos_update_request //2 1和2是trace hook注冊的函數,應該不計為函數調用
                caller_info = find_and_get_symobls((unsigned long)__builtin_return_address(1)); //只是做了個優化,等效於直接%pS打印__builtin_return_address(1)
                trace_freq_qos_user_setting(cid, type, value, caller_info); //caller_info打印的是"qos_debug_work_func_1",而不是freq_qos_update_request

注:find_and_get_symobls() 不單會打印調用函數名,還會打印ko模塊名。

 

2. 使用下面方法可以達到類似的效果,但是只會打印函數名

printk("caller=%pS\n", __builtin_return_address(1)); //會打印 caller=qos_debug_work_func_1+0xc4/0x17c

3. printk 和 trace_printk 都可以使用。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM