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) 编辑 收藏 举报