Как получить имя вызывающей функции? - PullRequest
18 голосов
/ 06 июня 2011

Я использую цепочку инструментов GNU.Как я могу во время выполнения найти вызывающего функцию?например, функция B () вызывается многими функциями с помощью указателей на функции.Теперь, когда мне звонят, я хочу напечатать имя звонящего.Мне это нужно для устранения определенной проблемы.

Ответы [ 3 ]

18 голосов
/ 06 июня 2011

Если вы используете GNU, вы можете использовать функции backtrace .На этой странице руководства есть пример использования.

9 голосов
/ 06 июня 2011

Расположение кода вызова вашей функции хранится в gcc во встроенном __builtin_return_address(). Чтобы получить имя для этого, вам нужно проанализировать таблицу символов программы; в то время как это возможно сделать через dladdr(), существуют ограничения на это:

  1. может быть небезопасно вызывать backtrace() / dladdr() (например, из обработчиков сигналов или одновременно в многопоточной программе или из контекстов, где вы не можете вызвать malloc().).
  2. операция не мгновенная, но может потребовать перевода вызывающего потока в спящий режим; это плохо, если в точке вызова удерживаются какие-либо блокировки.
  3. есть пределы тому, насколько "разрешаем" адрес возврата в имени; например если есть встроенные функции, вызывающие вашу функцию, то будет отображаться вызывающая сторона вашего вызывающего (на странице руководства backtrace() это также указано, как и для dladdr() в разделе «BUGS»).
  4. В C ++ существует дополнительная проблема вызовов через con / destructors / initializer, где «вызывающая сторона» вашей функции может в конечном итоге стать неким мета-кодом, сгенерированным компилятором, а фактическое место, которое вас интересует, может быть где-то еще вызывающий абонент или даже глубже в стек вызовов).

Часто это лучший способ отделить отслеживание и разрешение имен функций; то есть просто выведите адреса возврата (как шестнадцатеричные / двоичные), а затем постобработайте полученный журнал с таблицей символов, полученной во время работы программы.

5 голосов
/ 28 ноября 2012

Другой метод, указанный Василом Димовым в ответе на аналогичный вопрос, - заменить вызов функции макросом-оберткой, который сообщает или передает имя вызывающей функции.Это будет работать с встроенными функциями, где обратная трассировка не будет.С другой стороны, это не будет работать, если вы вызываете функцию по ссылке или иным образом берете ее адрес.

Например:

int B(int x){
    ...
}

может стать:

int _B(int x, char *caller){
    printf("caller is %s\n", caller);
    ...
}

#define B(x) _B((x), __func__)

и каждый вызов B () выводит имя звонящего.Васил Димов строит его по-другому, печатая имя непосредственно в макросе и оставляя функцию неизменной.

...