Я ищу умный способ отследить вызовы функции и возвраты. Я знаю, что могу использовать отладчик, но я хотел бы способ просто иметь его, распечатывают что-то к терминалу при вызывании функции по сравнению с необходимостью ступить через код.
Я думаю, что смог использовать препроцессор, но я не уверен, каков был бы лучший способ пойти об этом.
Или есть ли способ использовать gdb для распечатывания информации, которая была бы полезна, не имея необходимость ступить через код.
Большинство компиляторов позволяют внедрить функцию инструментации до и после вызова функции.
в msvc это _penter и _pexit
.
хорошая статья
http://www.drdobbs.com/184403601
в gcc вы можете использовать -finstrument-functions
http://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/Code-Gen-Options.html
Вы можете использовать отладочные библиотеки или файлы map для получения дополнительной информации.
Довольно интрузивным решением является использование RAII для контроля области видимости функции. Это окажет большое влияние на производительность, но будет достаточно явным в логах, не требуя от пользователя добавления инструментария во все возможные пути кода, которые могут покинуть функцию:
class ScopeLogger {
public:
ScopeLogger( std::string const & msg ) : msg(msg)
{ std::cout << "Enter: " << msg << std::endl; }
~ScopeLogger()
{ std::cout << "Exit: " << msg << std::endl; }
std::string msg;
};
#if DEBUG
#define FUNCTION(x) ScopeLogger l_##x##_scope(x);
#endif
void foo( int value ) {
FUNCTION( __FUNCTION__ );
if ( value > 10 ) throw std::exception;
std::cout << "." << std::endl;
}
int main() {
foo(0); // Enter: foo\n.\nExit: foo
foo(100); // Enter: foo\nExit: foo
}
Если код однопоточный, вы можете даже захотеть добавить статическую переменную с некоторым уровнем отступа к ScopedLogger
, не добавляя слишком много к и без того тяжелому влиянию на производительность:
class ScopeLogger {
public:
ScopeLogger( std::string const & msg ) : msg(msg)
{ std::cout << std::string(indent++,' ') << "Enter: " << msg << std::endl; }
~ScopeLogger()
{ std::cout << std::string(--indent,' ') << "Exit: " << msg << std::endl; }
std::string msg;
static int indent;
};
int ScopeLogger::indent = 0;
#define BEGIN_FUNC(X) printf("Function %s Entered",X)
#define END_FUNC(X) printf("Function %s End",X)
foo()
{
BEGIN_FUNC(__func__);
//Your code here
END_FUNC(__func__);
}
Я думаю, что если вы напишете макрос, как указано выше, и будете использовать его для каждой функции, как описано, то вы сможете получить журналы на терминале.
Вы можете посмотреть на Callgrind Valgrind , который может отслеживать вызовы функций в красивый график. Он будет отображать вызовы функций, но не параметры или возвращаемые значения.
Или есть ли способ использовать gdb для вывода полезной информации, при этом не перебирая код
Да. Установите точку останова только в тех функциях, которые вас действительно интересуют. Используйте "continue", пока не дойдете до этих функций или пока программа не завершится аварийно. Затем используйте "backtrace" (или "bt"), чтобы получить трассировку стека.
Если вам нужно автоматизировать это, вы можете взглянуть на TARGET_ASM_FUNCTION_END_PROLOGUE
и TARGET_ASM_FUNCTION_BEGIN_EPILOGUE
. Это крючки компилятора, которые позволят вам указать части сборки, которые будут выдаваться вместе с обычным прологом/эпилогом функции - в вашем случае, вы бы использовали их для выдачи небольшой сборки для регистрации входа/выхода из рассматриваемой функции. Вы также можете посмотреть на FUNCTION_PROFILE
и/или PROFILE_HOOK
(например, на: http://gcc.gnu.org/onlinedocs/gccint/Function-Entry.html).
Существует макрос __ FUNCTION __
(Ссылка) , используемый для определения того, какой метод (в формате Class :: Method
) вы используете, но это больше ручной процесс.
Однако, когда мне недавно понадобилась та же самая «трассировочная» информация, я не смог найти автоматический метод.