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 都可以使用。

 

四、补充

1. 向打印第n层栈回溯就使用__builtin_return_address(n)

//include/linux/ftrace.h

#define ftrace_return_address(n) __builtin_return_address(n)

#define CALLER_ADDR0 ((unsigned long)ftrace_return_address0)
#define CALLER_ADDR1 ((unsigned long)ftrace_return_address(1))
#define CALLER_ADDR2 ((unsigned long)ftrace_return_address(2))
#define CALLER_ADDR3 ((unsigned long)ftrace_return_address(3))
#define CALLER_ADDR4 ((unsigned long)ftrace_return_address(4))
#define CALLER_ADDR5 ((unsigned long)ftrace_return_address(5))
#define CALLER_ADDR6 ((unsigned long)ftrace_return_address(6))

注:见 preemptirq_long.c 中的使用。

 

posted on 2022-02-12 18:24  Hello-World3  阅读(2596)  评论(0编辑  收藏  举报

导航