Трассировка вызовов, завершений и сбросов функий
Метаданные
Статья
Зачастую, в процессе отладки полезно знать какие именно функции вызывались, и в каком порядке. Добавление RDebug::Print в начало и конец всех интересующих вас функций может стать весьма утомительным и занять много времени. Помимо этого, использование в функции оператора return может помешать срабатыванию RDebug::Print в конце тела функции. <p>Автоматические переменные (объекты, создаваемые в стеке) уничтожаются, как только выходят из области видимости. Размещение объекта для логгирования в стеке - самый легкий способ определить вызов и завершение работы функции, несмотря на использование в ее теле операторов return.
Функция также может завершить выполнение в результате сброса, и отследить это бывает очень полезным. Начиная с S60 3-й редакции, сбросы реализуются в виде исключений. C++ предоставляет функцию std::uncaught_exception() для того, чтобы определить, произошло ли необработанное исключение.
Значение, возвращаемое функцией, также может быть определено, так как в 32-битной системе оно всегда будет 32-битным значением. Это требует использования вставок на ассемблерного кода, что в свою очередь не обеспечивает портируемость между различными типами сборки проекта (WINSCV, GCCE и т.п.), поэтому для каждой сборки этот код будет разным.
Contents |
Простой пример
Это простой трассировщик, выводящий вызовы, завершения и сбросы функций через RDebug или в файл. Он также может определять значение, возвращаемое функцией. Он реализован в виде обычного T класса, используемого в качестве автоматической переменной функции. В его конструкторе вызывается метод, согнализирующий о начале работы функции, а в деструкторе - сигнализирующий о ее завершении (возможно, в результате сброса). Он может определить значение, возвращаемое функцией, если проект был собран для WINSCW.
Файл: tracer.h
#ifndef TTRACER_H
#define TTRACER_H
#include <e32base.h>
// Определение метода трассировки
// 0 = Логгирование отключено
// 1 = Вывод данных через RDebug
// 2 = Вывод в файл (используется RFileLogger)
#define TRACER_LOG_METHOD 2
// ============================================================================
// Логгирование отключено - определяем пустые макросы
#if TRACER_LOG_METHOD == 0
#define TRACER(func)
#define TRACER_RET(func,format)
#else // Логгирование включено
// Макрос для печати вызова, завершения и сброса функции.
// Пример: TRACER("CMyClass::MyFunction");
#define TRACER(func) TTracer function_tracer( _S(func), _S("") );
// Макрос для печати вызова, завершения и сброса функции, дополнительно
// выводящий возвращенное функцией значение.
// Пример: TRACER_RET("CMyclass::MyFunction", "%d");
#define TRACER_RET(func,format) TTracer func_tracer( _S(func), _S(format) );
#if TRACER_LOG_METHOD == 1 // Вывод через RDebug
#include <e32debug.h>
#define TRACER_PRINT(a) RDebug::Print(a,&iFunc);
#define TRACER_PRINT_RET(a,b) RDebug::Print(a,&iFunc,b);
#elif TRACER_LOG_METHOD == 2 // Вывод в файл
#include <flogger.h>
_LIT( KLogDir, "tracer" ); // Папка для сохранения лога: C:\logs\tracer
_LIT( KLogFile, "tracer.txt" ); // Файл для хранения лога: c:\logs\tracer\tracer.txt
#define TRACER_PRINT(a) RFileLogger::WriteFormat(KLogDir, \
KLogFile,EFileLoggingModeAppend,a,&iFunc);
#define TRACER_PRINT_RET(a,b) RFileLogger::WriteFormat(KLogDir, \
KLogFile,EFileLoggingModeAppend,a,&iFunc,b);
#endif
_LIT( KLogEnter, "%S: ENTER" );
_LIT( KLogExit, "%S: EXIT" );
_LIT( KLogLeave, "%S: LEAVE!" );
_LIT( KLogExitRet, "%S: EXIT, Returning " );
/**
* Простой трейсер для логирования вызова, завершения и сброса функции
*/
class TTracer
{
public:
/**
* inline конструктор для вывода сообщения о вызове функции
*/
TTracer( const TText* aFunc, const TText* aRetFormat )
: iFunc( aFunc )
, iRetFormat( aRetFormat )
{
TRACER_PRINT( KLogEnter );
}
/**
* inline деструктор для вывода сообщения о выходе из функции
* (обычном, или в результате сброса)
*/
~TTracer()
{
if ( std::uncaught_exception() ) // Произошел сброс (исключение)
{
// Функция завершила работу в результате сброса
TRACER_PRINT( KLogLeave );
}
else
{
// Функция завершила работу штатно
if ( iRetFormat.Length() == 0 )
{
TRACER_PRINT( KLogExit );
}
else
{
// Выводим возвращенное функцией значение
#ifdef __WINS__
TInt32 retVal = 0;
// Небольшая ассемблерная вставка. Ее нужно изменить
// при использовании для других целевых сборок.
_asm( mov retVal, ebx );
TBuf<100> format( KLogExitRet );
format.Append( iRetFormat );
TRACER_PRINT_RET( format, retVal );
#else
TRACER_PRINT( KLogExit );
#endif
}
}
}
private:
/**
* Дескриптор-указатель, содержащий имя функции.
*/
TPtrC iFunc;
/**
* Строка, используемая для форматирования возвращаемого функцией значения
*/
TPtrC iRetFormat;
};
#endif // TRACER_LOG_METHOD == 0
#endif // TTRACER_H
Использование
#include "tracer.h"
// Вывод вызова, завершения или сброса функции
void CSomeClass::SomeFunctionL()
{
TRACER( "CSomeClass::SomeFunctionL" );
...
}
// Выводится:
// CSomeClass::SomeFunctionL: ENTER
// CSomeClass::SomeFunctionL: EXIT
// или
// CSomeClass::SomeFunctionL: LEAVE
// Вывод вызова, завершения или сброса функции,
// а также возвращенного ей значения (числа)
TInt CSomeClass::ReturnSomeInt()
{
TRACER_RET( "CSomeClass::ReturnSomeInt", "%d" );
...
return 42;
}
// Выводится:
// CSomeClass::ReturnSomeInt: ENTER
// CSomeClass::ReturnSomeInt: EXIT, Returning 42
// или
// CSomeClass::ReturnSomeInt: LEAVE
// Вывод вызова, завершения или сброса функции,
// а также возвращенного ей значения (дескриптора)
HBufC* CSomeClass::ReturnSomeDescL()
{
TRACER_RET( "CSomeClass::ReturnSomeDescL", "%S" );
return _L("Test data").AllocL();
}
// Выводится:
// CSomeClass::ReturnSomeDescL: ENTER
// CSomeClass::ReturnSomeDescL: EXIT, Returning Test data
// или
// CSomeClass::ReturnSomeDescL: LEAVE
Вспомогательный скрипт
Это простой скрипт, написанный на perl, который считывает содержимое CPP файла и добавляет макрос TRACER в начало каждой функции. Он сохраняет оригинальный файл в качестве резервной копии под другим именем. Скрипт не добавляет макрос TRACER_RET в функции, возвращающие значения - это необходимо сделать самостоятельно.
add_traces.pl
# add_traces.pl (C) Marko Kivijärvi 2006
# Dummy checks
die "Specify an input file!\n" if $ARGV[0] eq "";
die "File not found!\n" unless -e $ARGV[0];
die "Incorrect file extension for a C/C++ file!\n"
if ( $ARGV[0] !~ /(.*)\.(c|cpp)$/ );
# Constants
my $INC_TRACER_H = "#include \"tracer.h\"\n";
my $TRACER = "TRACER";
# Parse output filename from the input filename
my $file = $ARGV[0];
my $origFile = $1."-orig.".$2;
system( "copy $file, $origFile" );
# Reset the input record separator (newline) so the entire file is read at once
undef $/;
# Read the input file
$_ = <>; # All there
# Adds a tracer macro after each function definition
s/
(\b\w*?\b[&*]?)? # Possible function return type
\s+ # One or more empty spaces
(\b\w+?\b) # Class name
\s*? # Possible empty space
:: # ::
\s*? # Possible empty space
(~?\b\w+?\b) # Function name
\s*? # Possible empty space
\( # Opening brace
([^)]*?) # Possible function parameters
\) # Closing brace
\s*? # Possible empty space
(const)? # Possible 'const'
[^{;\/]*? # Possible empty space or constructor
# initializer list
\{ # Opening curly brace
/
Parse($&,$1,$2,$3,$4,$5) # Print the match and add the macro
/gxe; # g = repeat, x = split regex to multiple lines, e = evaluate substitution
open OUT, ">$file" or die "Cannot open file $file $!\n";
print OUT $INC_TRACER_H;
print OUT;
close OUT;
exit 0;
sub Parse {
my $match = shift;
my $ret = shift;
my $class = shift;
my $func = shift;
my $param = shift;
my $const = shift;
foreach ( $ret, $class, $func, $param ) {
s/^\s+|\s+$//g;
s/\n//g;
}
my $debug = $match."\n ";
$debug .= $TRACER."(";
$debug .= $ret." " if defined $ret;
$debug .= $class."::".$func."(";
$debug .= $param if $param ne "";
$debug .= ")";
$debug .= " ".$const if defined $const;
$debug .= ")";
return $debug;
}
Дополнительную информацию об обработке сбросов можно найти здесь: Использование ловушки(TRAP).


(no comments yet)