×
Namespaces

Variants
Actions

如何跟踪函数入口,退出或异常

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

文章
翻译:
hoolee
最后由 hamishwillee 在 14 Sep 2012 编辑

调试程序时如果知道函数何时被调用,按照什么顺序调用是很有用的。但是使用RDebug::Prints到函数开头或末尾却比较耗时和低效。而且如果返回很快将导致Exit打印被跳过

自动变量(基于stack的对象)将在函数越界后被销毁,所以可以将logging对象放在堆栈中以便防止函数在过早退出时错过Exit打印。

这个函数也可以在异常发生时退出,从中获得log打印也非常有用。在S60第三版及后继版本中,异常是作为exception来处理的,C++提供了一个函数std::uncaught_exception(),我们可以用来检查是否有exception发生。

我们可以打印函数返回值,因为在32位系统中总是为32位值。这将导致一些内联编码。这样做不利的一面是代码的跨平台性 有了缺陷(如winscw或ARM)因此我们需要不同的实现。

函数名可以使用__PRETTY_FUNCTION__自动获取,编译器将提供类名,函数名以及打印log时所需要信息

Contents

示例代码

这里是一个简单的tracer将函数的Enter, ExitLeave信息输出到RDebug或文件中。它也可以记录函数的返回值,它定义了一个简单的T类可以在函数中作为自动变量访问。在构造时它会记录函数Enter,在析构时会记录下ExitLeave,但只能在winscw平台下记录函数返回值。

头文件: tracer.h

#ifndef TTRACER_H
#define TTRACER_H
 
#include <e32base.h>
 
// Define tracer logging method
// 0 = Logging off
// 1 = Log to RDebug
// 2 = Log to file (RFileLogger)
#define TRACER_LOG_METHOD 2
 
// ============================================================================
 
// Logging off, define empty macros and skip all the rest
#if TRACER_LOG_METHOD == 0
 
#define TRACER(func)
#define TRACER_RET(func,format)
 
#else // Logging on
 
// Macro to print function entry, exit and leave.
// Example: TRACER("CMyClass::MyFunction");
#define TRACER(func) TTracer function_tracer( _S(func), _S("") );
 
// Macro to print function return value in addition to entry, exit
// and leave conditions Second parameter is a formatting string used
// to print the return value Example to print an integer return value:
// TRACER_RET("CMyclass::MyFunction", "%d");
#define TRACER_RET(func,format) TTracer func_tracer( _S(func), _S(format) );
 
// Macro to print function entry, exit and leave.
// Gets the function name automatically
// Example: TRACER_AUTO;
// Substract 1 from MaxLength() because PtrZ() adds the zero terminator
#define TRACER_AUTO \
TPtrC8 __ptr8((const TUint8*)__PRETTY_FUNCTION__); \
TBuf<150> __buf; \
__buf.Copy( __ptr8.Left( __buf.MaxLength() - 1 ) ); \
TTracer function_tracer( __buf.PtrZ(), _S("") )

 
// Macro to print function entry, exit and leave.
// Gets the function name automatically
// Example: TRACER_AUTO_RET("%d");
// Substract 1 from MaxLength() because PtrZ() adds the zero terminator
#define TRACER_AUTO_RET(format) \
TPtrC8 __ptr8((const TUint8*)__PRETTY_FUNCTION__); \
TBuf<150> __buf; \
__buf.Copy( __ptr8.Left( __buf.MaxLength() - 1 ) ); \
TTracer function_tracer( __buf.PtrZ(), _S(format) )

 
#if TRACER_LOG_METHOD == 1 // Print to 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 // Print to file
 
#include <flogger.h>
_LIT( KLogDir, "tracer" ); // Log directory: C:\logs\tracer
_LIT( KLogFile, "tracer.txt" ); // Log file: 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 " );
 
/**
* Simple tracer class that logs function enter, exit, or leave
*/

class TTracer
{
public:
 
/**
* inline constructor to write log of entering a function
*/

TTracer( const TText* aFunc, const TText* aRetFormat )
: iFunc( aFunc )
, iRetFormat( aRetFormat )
{
TRACER_PRINT( KLogEnter );
}
 
/**
* inline destructor to write log of exiting a function
* normally or with a leave
*/

~TTracer()
{
if ( std::uncaught_exception() ) // Leave is an exception
{
// The function exited with a leave
TRACER_PRINT( KLogLeave );
}
else
{
// The function exited normally
if ( iRetFormat.Length() == 0 )
{
TRACER_PRINT( KLogExit );
}
else
{
// Log the return value
#ifdef __WINS__
TInt32 retVal = 0;
 
// The assembly bit. This needs to be reimplemented
// for every target.
_asm( mov retVal, ebx );
 
TBuf<100> format( KLogExitRet );
format.Append( iRetFormat );
TRACER_PRINT_RET( format, retVal );
#else
TRACER_PRINT( KLogExit );
#endif
}
}
}
 
private:
 
/**
* Pointer descriptor to function signature that is to be logged.
*/

TPtrC iFunc;
 
/**
* Formatting string used to print the function return value
*/

TPtrC iRetFormat;
 
};
 
#endif // TRACER_LOG_METHOD == 0
 
#endif // TTRACER_H

如何使用

#include "tracer.h"
 
// Log Enter, Exit or Leave
void CSomeClass::SomeFunctionL()
{
TRACER( "CSomeClass::SomeFunctionL" );
...
}
// Prints:
// CSomeClass::SomeFunctionL: ENTER
// CSomeClass::SomeFunctionL: EXIT
// or
// CSomeClass::SomeFunctionL: LEAVE
 
// Log Enter, Exit or Leave and the integer return value
TInt CSomeClass::ReturnSomeInt()
{
TRACER_RET( "CSomeClass::ReturnSomeInt", "%d" );
...
return 42;
}
// Prints:
// CSomeClass::ReturnSomeInt: ENTER
// CSomeClass::ReturnSomeInt: EXIT, Returning 42
// or
// CSomeClass::ReturnSomeInt: LEAVE
 
// Log Enter, Exit or Leave and the descriptor return value
HBufC* CSomeClass::ReturnSomeDescL()
{
TRACER_RET( "CSomeClass::ReturnSomeDescL", "%S" );
return _L("Test data").AllocL();
}
// Prints:
// CSomeClass::ReturnSomeDescL: ENTER
// CSomeClass::ReturnSomeDescL: EXIT, Returning Test data
// or
// CSomeClass::ReturnSomeDescL: LEAVE
 
// Log Enter, Exit or Leave without specifying function name
void CSomeClass::SomeFunctionL( TBool aFlag )
{
TRACER_AUTO;
...
}
// Prints:
// CSomeClass::SomeFunctionL(int): ENTER
// CSomeClass::SomeFunctionL(int): EXIT
// or
// CSomeClass::SomeFunctionL(int): LEAVE
 
// Log Enter, Exit or Leave and the integer return value without specifying function name
TInt CSomeClass::ReturnSomeInt( TInt aArg )
{
TRACER_AUTO_RET( "%d" );
...
return 42;
}
// Prints:
// CSomeClass::ReturnSomeInt(int): ENTER
// CSomeClass::ReturnSomeInt(int): EXIT, Returning 42
// or
// CSomeClass::ReturnSomeInt(int): 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;
}

参考Using TRAP获得更多log时发生异常的信息

This page was last modified on 14 September 2012, at 09:37.
92 page views in the last 30 days.
×