Я использую цепочку инструментов gnu. Как я могу во время выполнения найти функцию функции? например, функция B() вызывается многими функциями с помощью указателей функций. Теперь, когда вызывается B, я хочу напечатать имя вызывающего абонента. Мне нужно это для отладки определенной проблемы.
Как получить имя вызывающей функции?
Ответ 1
Если вы используете GNU, вы можете использовать функции backtrace. Вот пример использования этой справочной страницы.
Ответ 2
Расположение кода вызова вашей функции поддерживается gcc в __builtin_return_address()
. Чтобы получить имя для этого, вам необходимо проанализировать таблицу символов программы; в то время как это возможно, через dladdr()
, есть ограничения на это:
- во всех контекстах небезопасно вызывать
backtrace()
/dladdr()
(например, из обработчиков сигналов или одновременно в многопоточной программе или из контекстов, где вы не можете вызватьmalloc()
.). - операция не мгновенная, но может потребовать, чтобы вызывающий поток спал; это плохо, если в момент вызова блокируются.
- существует ограничение того, насколько хорошо "разрешимый" обратный адрес относится к имени; например если есть встроенные функции, вызывающие вашу функцию, тогда будет отображаться вызывающая сторона вашего вызывающего абонента (в man-странице
backtrace()
), также как и дляdladdr()
в разделе "ОШИБКИ" ). - В С++ существует дополнительная проблема вызовов через con/destructors/initializers, где "вызывающий" из вашей функции может оказаться метакомпотом, созданным компилятором, и фактическое место, которое вас интересует, может быть в другом месте ( вызывающего абонента или даже глубже в стеке вызовов).
Это часто лучший способ разделить трассировку и определение имени функции; то есть просто вывести обратные адреса (как hex/binary), а затем обработать полученный журнал в соответствии с таблицей символов, полученной при запуске программы.
Ответ 3
Другой метод, обозначенный Василем Димовым в ответ на аналогичный вопрос, заключается в замене вызова функции макросом-обертки, который сообщает или передает в вызывающей функции имя. Это будет работать с встроенными функциями, где backtrace не будет. С другой стороны, это не сработает, если вы вызываете функцию по ссылке или иным образом берете ее адрес.
Например:
int B(int x){
...
}
может стать:
int _B(int x, char *caller){
printf("caller is %s\n", caller);
...
}
#define B(x) _B((x), __func__)
и каждый вызов B() будет печатать имя вызывающего абонента. Василь Димов строит его по-другому, печатая имя непосредственно в макросе и оставляя функцию неизменной.