×
Namespaces

Variants
Actions

Трассировка вызовов, завершений и сбросов функий

From Nokia Developer Wiki
Jump to: navigation, search
Article Metadata

Статья
Перевод:
truf
Последнее редактирование: hamishwillee (09 Dec 2011)

Зачастую, в процессе отладки полезно знать какие именно функции вызывались, и в каком порядке. Добавление 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).

This page was last modified on 9 December 2011, at 00:52.
32 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×