Namespaces

Variants
Actions

Please note that as of October 24, 2014, the Nokia Developer Wiki will no longer be accepting user contributions, including new entries, edits and comments, as we begin transitioning to our new home, in the Windows Phone Development Wiki. We plan to move over the majority of the existing entries over the next few weeks. Thanks for all your past and future contributions.

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

From 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 06:37.
111 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.

×