一、說明
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 都可以使用。