Страницы

Поиск по вопросам

вторник, 28 января 2020 г.

Как получить название функции, которая совершила вызов данной функции?

#c


Пишу лабораторную работу, нужно логирование, и для того чтобы не запутываться, хотелось
бы иметь в логах что-то вроде:

Error: init_link - not enough memory

Сейчас ничего, кроме передачи в логгер строки-названия функции, в голову не приходит.
Есть ли более изящный метод?
Update:
Вот что я имею в виду:
int foo(int bar)
{
    int foobar = -1;
    check_something(foobar/bar);
    return foobar/bar;
}

Теперь в check_something должен вызваться логгер, в котором сказано о том, что check_something
был вызван из foo    


Ответы

Ответ 1



Вопрос очень интересный, попробую предложить решение, хотя не уверен, то ли это, что видится автору вопроса. На мой взгляд, хороший логгер должен выводить весь путь вызовов к точке вывода сообщения, наподобие вывода команды where в интерактивном отладчике gdb . В принципе, такую информацию можно извлечь, совместно рассматривая стек выполнения (там адреса возврата) и таблицу символов загрузочного модуля (там есть адреса точек входа в функции и их имена (конечно, если модуль не стрипнут). Для лабораторки это, конечно, крутовато. Можно, наверное, предложить 'ручной' вариант. При входе в функцию, имя которой должно появиться в списке логгера, вызовем My_set_fname(fname), а для печати My_logger(message); fname ДОЛЖНА быть локальной переменной (располагаться в стеке программы). My_set_fname(fname) будет вести свой стек (например, связанный список в куче или достаточно большой статический массив (собственно большой - это если My_set_fname() будут вызываться из рекурсии)). В этот стек надо помещать адрес параметра (fname). Здесь надо иметь в виду, что стек исполнения программы растет от больших адресов к меньшим, поэтому при очередном вызове My_set_fname() будем корректировать (путем удаления) верхнюю часть своего стека, так чтобы в нем содержались только адреса больше, чем адрес нашего аргумента. Эти соображения определяют требование к размещению аргумента. Константы и куча не подходят. Ну, логика My_logger(), по-моему, уже очевидна (или скажем так: формат вывода - дело вкуса).

Ответ 2



Есть такой макрос: __FUNCTION__, который возвращает имя функции, в которой вызван. Думаю, можно составить макрос, который неявно этот параметр будет конечной функции и передавать. Можете посмотреть и другие макросы в статье: Predefined Macros.

Ответ 3



Это бактрейс. В стеке - цепочка стековых фреймов (для stdcall-конвенций), в которых адреса возврата из функций. Нормально данная задача не может быть решена без применения графов. Все искомые функции описываются графом, затем получив адрес возврата из стека, находится часть графа, в которой описана инструкция по этому адресу. Далее можно найти начало процедуры и соответственно её имя. Для не stdcall конвенций необходимо вычислять баланс стека при бактрейсе, это если движок, создающий граф, не ведёт список процедур. Для x86 есть готовый движок. В NT такое логгирование используется очень часто, например, бактрейс - это первое действие при анализе крэшдампов. Есть полноценные апи для работы с бактрейсом (напр. RtlLogStackBackTrace() etc.) и утилиты для его просмотра (UMDH, WinDbg etc.).

Ответ 4



Обычно используют макрос __FUNCTION__ (макрос не входит в стандарт ANSI, но поддерживается основными компиляторами). Например, если функция log, выводящая в лог ошибки, подобна printf, то печать такова log("Error: %s - %s", __FUNCTION__, "not enough memory"); Кроме того, стандарт ANSI включает в себя полезные предопределенные макросы: __FILE__ - имя исходного файла, __LINE__ - строка исходного файла.

Ответ 5



Очевидного решения не видится. Можно сделать скрипт (например, на Питоне), который будет проходить перед компиляцией по всему коду и оборачивать все функции особенным образом, так чтобы при вызове любой функции устанавливалась глобальная переменная, куда помещалось бы значение __FUNCTION__. Например, было void outer() { inner(); } А стало: char *previous = NULL; char *last = NULL; void outer() { previous = last; last = __FUNCTION__; inner(); last = previous; } Сама печать вместо __FUNCTION__ будет использовать last; /* * Должна распечатать * Error: outer - not enough memory */ void inner() { printf("Error: %s - not enough memory", last); }

Комментариев нет:

Отправить комментарий