5
Nov
0

C++ – Tracking/Debuging tools, generate xml from trace

Last post about development for Brainable for a long time. In this post, I present a part of the library used to generate xml report to track program actions.
It consists on basic classes and macro. If you finally do not use the debug class, the macro makes the lib transparent.

The lib uses : BVector, BStack and BString.

TagWrited

#ifndef BTAGWRITER_HPP
#define BTAGWRITER_HPP

#include "BString.hpp"
#include "BStack.h"

/**
* Change Tag Stream Type here
*/
#include <iostream>
typedef std::ostream TagStream;

/**
* @author Berenger
* @version 0.5
* @date 27 octobre 2009
* @file BDebug.hpp
* @package Utils
* @brief Manage tag output
*
* @must You have to configure this class by setting the stream.
* @must The Stream is an address of the object stream.
* @must YOU HAVE TO CLOSE AND DELETE THE STREAM AT THE END
*
* @must you have to specify another stream type if needed
*/

class BTagWriter{
	protected:
	TagStream*		   _ostream; 		/**< The output stream*/
	BStack<BString> 	_tags;			/**< The tag stack*/
	bool				_isANewLine;	/**< True if it is a new line and new to put prefix*/
	BString				_currentType;	/**< In case of current type differed*/

	public:

	/**
	* @brief Protected constructor
	*/
	BTagWriter(){
		_ostream = NULL;
		_isANewLine = true;
	}

	/**
	* @brief Protected constructor
	* @param in_ostream Output stream based on BOStream
	*/
	BTagWriter(TagStream * const in_ostream){
		_ostream = in_ostream;
		_isANewLine = true;
	}

	/**
	* @brief Desctuctor
	*/
	virtual ~BTagWriter(){
	}

	/**
 	* @brief Set the ostream
 	* @param in_ostream Output stream based on BOStream
 	*
 	*/
	void ostream(TagStream * const in_ostream = NULL){
		if(_ostream) _ostream->flush();		// flush
		_ostream = in_ostream;				// then change _ostream
	}

	/**
 	* @brief Ask the ostream to flush
 	* It can be recommanded before high risque area
	* and a buffered ostream
 	*
 	*/
	virtual void Flush(){
		if(_ostream) _ostream->flush();
	}

	/**
 	* @brief Ask the operationnality of the ostream
	* @return true in case of operational object, false else
 	* Simply redirect the query to ostream object and check
 	* if _ostream is null
 	*/
	virtual bool Good() const{
		return _ostream && _ostream->good();
	}

	/**
 	* @brief Tag and open a personnal area
 	* @param in_type Area name.
 	*
	* Equivalent to <Type> in XML
 	*
 	*/
	virtual void OpenNode(const char* const in_type = NULL);
	/**
 	* @brief Tag and close a personnal area
 	* @param in_type Type name.
 	*
 	* Equivalent to </Type> in XML
 	*/
	virtual void CloseNode(const char* const in_type = NULL);
	/**
	* @anchor BTagWriter_OpenCloseNode
 	* @brief Tag and close a personnal area
 	* @param in_type Type name.
 	* @param in_value Value.
 	*
 	* Equivalent to <Type>Value{</Type>} in XML
 	*/
	template<typename T>
	void OpenCloseNode(const char* const in_type,const T& in_value){
		if( _ostream ){
			if(FlushCurrentLine()) Prefix();
			// write <Type
			(*_ostream) << TAG_OP << (in_type?in_type:NONAME);

			// put >Value</Type>\n in stack
			_currentType.clear();
			_currentType.append( TAG_CL );
			_currentType.append( in_value );

			_currentType.append( TAG_OPCL );
			_currentType.append( (in_type?in_type:NONAME) );
			_currentType.append( TAG_CL );
			_currentType.append( TAG_NEWLINE );

			StartLine();
		}
	}

	/**
	* @ref  BTagWriter_OpenCloseNode ["Please refer to the documented method"]
	*/
	void OpenCloseNode(const char* const in_type,const BString& in_value);

	/**
 	* @brief Add a parameter
 	* @param in_param Param name.
 	* @param in_value Value name.
 	*
 	* Equivalent to <{Type} in_param="in_value" {></Type>} in XML
 	*/
	virtual void Property(const char* const in_param,const char* const in_value);

	/**
	* @brief Flush the current line
	*/
	virtual bool FlushCurrentLine();
	/**
	* @brief Prefix each line before writing into stream
	*/
	virtual void Prefix(const unsigned int in_offset = 0);

	/**
	* @brief Set the "is a new line" value to true and write
	* @brief a new line
	*/
	void StartLine();

	// ------------------------ FREE FORMAT ------------------------------ //

	/**
	* @anchor BTagWriter_operator
 	* @brief To print information in debug stream
	* @return The BDebug object
 	* @param in_data The data to add in log file
 	*
 	* Print the in_data into the logged file without using any
	* format, excepted tabulation at the begining of file
	*
	* @example BDEBUGGING( BDebug::Controler.OpenNode("Bizon") );
	* @example BDEBUGGING( BDebug::Controler<< "x = " << 0 << " y = " << 50 );
	* @example BDEBUGGING( BDebug::Controler.CloseNode() );
	*
	* @result <Bizon>
	* @result 		x = 0 y = 50
	* @result </Bizon>
 	*/
	template<typename T>
	BTagWriter & operator<< ( T& in_data ){
		if( _ostream ){
			(*_ostream) << in_data;
		}
		return *this;
	}

	/**
 	* @brief To print information in BTagWriter stream
	* @ref  BTagWriter_operator ["Please refer to the documented method"]
	*/
	BTagWriter & operator<< ( const void * in_data ){
		if( _ostream ){
			(*_ostream) << in_data;
		}
		return *this;
	}

	/**
 	* @brief To print information in BTagWriter stream
	* @ref  BTagWriter_operator ["Please refer to the documented method"]
	*/
	BTagWriter & operator<< ( const BString & in_data ){
		if( _ostream ){
			(*_ostream) << in_data.string();
		}
		return *this;
	}

private:

	/**
	* Macro used to specify the name for undeclared name
	*/
	static const char*  NONAME;
	static const char*  NOVALUES;

	/**
	* Macro used by the tag writer
	*/

	static const char   TAG_OP;
	static const char*  TAG_OPCL;
	static const char   TAG_CL;
	static const char*  TAG_CLCL;
	static const char   TAG_NEWLINE;
	static const char   TAG_EQUALITY;
	static const char*  EMPTY_STACK;

	/**
	* Macro used to specify the tabulation format
	* this macro is used in the BDebug class
	*/
	static const char* TAG_TABULATION;

};

#endif
#include "BTagWriter.hpp"

const char*  BTagWriter::NONAME = "NONAME";
const char*  BTagWriter::NOVALUES = "NOVALUES";

const char   BTagWriter::TAG_OP = '<';
const char*  BTagWriter::TAG_OPCL = "</";
const char   BTagWriter::TAG_CL = '>';
const char*  BTagWriter::TAG_CLCL = "/>";
const char   BTagWriter::TAG_NEWLINE = '\n';
const char   BTagWriter::TAG_EQUALITY = '=';
const char*  BTagWriter::EMPTY_STACK = "EmptyStack";

const char*  BTagWriter::TAG_TABULATION = "    "; //"\t"

// Equivalent to <Type{>'\n'} in XML
void BTagWriter::OpenNode(const char* const in_type){
	if( _ostream ){
		if(FlushCurrentLine()) Prefix();

		// write <Type
		if(in_type){
			(*_ostream) << TAG_OP << in_type ;
			_tags.push(BString(in_type));
		}
		else{
			(*_ostream) << TAG_OP << NONAME ;
			_tags.push(BString(NONAME));
		}

		// put '>' in line
		_currentType.clear();
		_currentType.append( TAG_CL );
		_currentType.append( TAG_NEWLINE );

		StartLine();
	}
}
// Equivalent to </Type>'\n' in XML
void BTagWriter::CloseNode(const char* const in_type){
	if( _ostream ){
		if(FlushCurrentLine()) Prefix(1);		

		// write </Type>\n
		(*_ostream) << TAG_OPCL;

		if(in_type) (*_ostream) << in_type;
		else if(_tags.size() ) (*_ostream) << _tags.top().string();
		else (*_ostream) << EMPTY_STACK;

		(*_ostream) << TAG_CL << TAG_NEWLINE;

		// it is possible to check in_type == _tags.top()
		// but what to do in case of different values ?
		if(_tags.size()) _tags.pop();

		StartLine();
	}
}

void BTagWriter::OpenCloseNode(const char* const in_type,const BString& in_value){
	if( _ostream ){
		if(FlushCurrentLine()) Prefix();

		(*_ostream) << TAG_OP << (in_type?in_type:NONAME);

		_currentType.clear();
		_currentType.append( TAG_CL );
		_currentType.append( in_value );

		_currentType.append( TAG_OPCL );
		_currentType.append( (in_type?in_type:NONAME) );
		_currentType.append( TAG_CL );
		_currentType.append( TAG_NEWLINE );

		StartLine();
	}
}

// Equivalent to <Type] in_param="in_value" [></Type> in XML
void BTagWriter::Property(const char* const in_param,const char* const in_value){
	if( _ostream ){

		(*_ostream) << ' ' << (in_param?in_param:NONAME) << TAG_EQUALITY << '"' <<
					(in_value?in_value:NOVALUES) << '"';

	}
}

// Flush the current line
bool BTagWriter::FlushCurrentLine(){
	if( _ostream && _currentType.lenght()){
		(*_ostream) << _currentType.string();
		_currentType.clear();
		StartLine();
		return true;
	}
	return _isANewLine;
}

// Print tabulations
void BTagWriter::Prefix(const unsigned int in_offset){
	if( _isANewLine && _ostream){
		_isANewLine = false;

		for(unsigned int indexTag = in_offset ; indexTag < _tags.size() ; ++indexTag){
			(*_ostream) << TAG_TABULATION;
		}
	}

}

// Start Line by writing
void BTagWriter::StartLine(){
	_isANewLine = true;
}

Tracker

#ifndef BDEBUG_HPP
#define BDEBUG_HPP

#include "BTagWriter.hpp"

#include "BStack.h"

/**
 * This Macro must be defined or commented
 * depending if you want to enable debugging or not
 * Please refere to the BDebug class for more information
 */
#define BDEBUG

/**
* Macro to clean or use debugging lines
*/
#ifdef BDEBUG
#ifndef _WIN32
	#warning [W] Debugging is enabled
#else
	#pragma message ( "[W] Debugging is enabled" )
#endif
	#define BDEBUGGING(D) (D)
	#define BDEBUGINIT(D) D
#else
	#define BDEBUGGING(D)
	#define BDEBUGINIT(D)
#endif

/**
* @author Berenger
* @version 0.5
* @date 27 octobre 2009
* @file BDebug.hpp
* @package Utils
* @brief Manage debug output
*
* @Warning This class is currently no thread safe
*
* @must You have to configure the class :
* @must		@li Call init method
* @must		@li Define debug in system.hpp
* @must		@li Use a default ostream or create yours
*
* You have to perform configuration depending on your
* preference
*
* @example //Set stream
* @example BFileOStream os("log.txt");
* @example BDebug::Controler.ostream( os );
*
* Then, you need to add data to the debug by calling appropriate
* method
*
* @example void aMethode(Object o){
* @example 		BDebug::Controler.EnterFunction("aMethode");
* @example 		BDebug::Controler.Param("Object", o.ToString());
* @example 		...
* @example 		BDebug::Controler.State("me", this->ToString());
* @example 		...
* @example 		float average = 0.0;
* @example
* @example 		BDebug::Controler.EnterLoop("Average Calcul");
* @example 		for(int i = 0 ; i < 1 ; ++i){
* @example 			BDebug::Controler.Loop();
* @example 			BDebug::Controler.Value("Loop",i);
* @example 			average += i;
* @example 			BDebug::Controler.Value("Current Average" , average);
* @example 		}
* @example 		BDebug::Controler.EndLoop(); // optional param "Average Calcul"
* @example
* @example 		average /= 10;
* @example 		BDebug::Controler.Value("Final Average" , average);
* @example 		...
* @example 		BDebug::Controler.LeaveFunction(); //optional param "aMethode"
* @example }
*
* @result <Function>
* @result 		<Name>aMethode</name>
* @result 		<Param name="Object">Bizon</Param>
* @result 		<State name="Me">Clean</State>
* @result 		<Loop name="Average Calcul">
* @result 			<Iteration count="0">
* @result 				<Value name="Loop">0</Value>
* @result 				<Value name="Current Average">0</Value>
* @result 			</Iteration>
* @result 		</Loop>
* @result 		<Value name="Final Average">0</Value>
* @result </Function>
*
* To make the thing proper, you have to ask if #BDEBUG is define.
* Also, it is possible to use the macro BDEBUGGING()
* This macro can aslo be used for other things
*
* @example BDEBUGGING( BDebug::Controler.Value("Bizon","Mheu?") );
*
* The current BDebug class is based on a XML-like description
* if you want to create you own, you have to create a class
* that herite from BDebug and set the controler to redefine all
* static/singleton access
*
* @example BDebug * db = new MyDebugLoger();
* @example BDebug::Controler = db;
*
*/

class BDebug : public BTagWriter {
	protected:

	BStack<unsigned int> _loops; /**< Current loop*/

	/**
	* @brief Protected constructor
	*/
	BDebug(){
	}

	/**
	* @brief Desctuctor
	*/
	virtual ~BDebug(){
	}

	public:

	static BDebug Controler; /**< Singleton Controler */

	// ------------------------ FORMATED ------------------------------ //

	/**
 	* @brief Tag a entered function
 	* @param in_name Entered function name.
 	*
 	* To mark that a function was entered,
	* it is recommanded to call this method
	* at the first line of your function
 	*
	* @result <Function>
	* @result 		<Name>in_name</name>
 	*/
	virtual void EnterFunction(const char* const in_name = NULL);
	/**
 	* @brief Tag a parameter function
 	* @param in_name Parameter name.
	* @param in_description Parameter description.
 	*
 	* To mark the parameter name and proprierties
	* @example BDebug::Controler.Param("MyBool", (MyBool?"TRUE":"FALSE") );
 	* @result 		<Param name="MyBool">True</param>
 	*/
	virtual void Param(const char* const in_name,const char* const in_description);
	/**
 	* @brief Tag a entered function
 	* @param in_name Current function name.
 	*
 	* To mark that a function was entered,
	* it is recommanded to call this method
	* at the first line of your function
 	*
	* @result </Function>
 	*/
	virtual void LeaveFunction();
	/**
 	* @brief Tag the current function end
 	* @param in_name Return name.
	* @param in_description Return description or value.
 	*
 	* Close the current function tag and print
	* the result
 	*
	* @result <Return name="in_name">in_value</Return>
 	*/
	virtual void Return(const char* const in_name,const char* const in_value);
	/**
 	* @brief Tag the current function end
 	* Close the current function tag and print
	* the result
	* @result <Return name=""></Return>
 	*/
	virtual void Return();

	/**
 	* @brief Tag the current state (different from value)
 	* @param in_name Object name.
	* @param in_state Object state.
 	*
 	* Close the current function tag and print
	* the result
 	*
	* @result <State name="in_name">in_state</State>
 	*/
	virtual void State(const char* const in_name,const char* const in_state);

	/**
 	* @brief Tag the begining of a loop area
 	* @param in_name Loop name.
 	*
 	* Open an loop
 	* @result <Loop name="in_name">
 	*/
	virtual void EnterLoop(const char* const in_name = NULL);
	/**
 	* @brief Tag a loop
 	*
 	* It adds a loop number in the debuging file
 	*
	* @result {</Iteration>}
	* @result <Iteration count="X">
 	*/
	virtual void Loop();
	/**
 	* @brief Tag the end of a loop area
 	* @param in_name Loop name.
 	*
 	* Close the current loop
 	*
	* @result {</Iteration>}
	* @result </Loop>
 	*/
	virtual void EndLoop();

	/**
	* @anchor BDebug_value_bchar_char
 	* @brief Tag a value
 	* @param in_name Object name.
	* @param in_value Object value.
 	*
 	* Just to print a mark
 	*
	* @result <Value name="in_name">in_value</Value>
 	*/
	template<typename T>
	void Value(const char* const in_name, const T& in_value){
		if( _ostream ){
			OpenCloseNode(DEBUG_VALUE,in_value);
			Property(DEBUG_NAME,in_name);
		}
	}

private:
	static const char* const  DEBUG_FUNCTION;
	static const char* const  DEBUG_NAME;
	static const char* const  DEBUG_PARAM;
	static const char* const  DEBUG_PARAM_NAME;
	static const char* const  DEBUG_STATE;
	static const char* const  DEBUG_ITERATION;
	static const char* const  DEBUG_LOOP;
	static const char* const  DEBUG_RETURN;
	static const char* const  DEBUG_VALUE;
	static const char* const  DEBUG_COUNT;

};

#endif
#include "BDebug.hpp"

/**
* @author Berenger
* @version 0.5
* @date 3 november 2009
* @file BDebug.cpp
* @package Utils
* @brief BDebug implementations
*/

const char* const  BDebug::DEBUG_FUNCTION = "Function";
const char* const  BDebug::DEBUG_NAME = "Name";
const char* const  BDebug::DEBUG_PARAM = "Param";
const char* const  BDebug::DEBUG_PARAM_NAME = "name";
const char* const  BDebug::DEBUG_STATE = "State";
const char* const  BDebug::DEBUG_ITERATION = "Iteration";
const char* const  BDebug::DEBUG_LOOP = "Loop";
const char* const  BDebug::DEBUG_RETURN = "Return";
const char* const  BDebug::DEBUG_VALUE = "Value";
const char* const  BDebug::DEBUG_COUNT = "Count";

// Init static attribute
BDebug BDebug::Controler;

void BDebug::EnterFunction(const char* const in_name ){
	if( _ostream ){
		OpenNode(DEBUG_FUNCTION);		// <Function>
		Property(DEBUG_NAME , in_name);	// 		<Name>X</Name>
	}
}

void BDebug::Param(const char* const in_name,const char* const in_description){
	if( _ostream ){

		OpenCloseNode(DEBUG_PARAM,in_description);	// <Param {>in_description</Param>}
		Property(DEBUG_NAME,in_name);				// <Param Name="in_name"{>in_description</Param>}

	}
}

void BDebug::LeaveFunction(){
	CloseNode(DEBUG_FUNCTION);
}

void BDebug::Return(const char* const in_name,const char* const in_value){
	if( _ostream ){

		OpenCloseNode(DEBUG_RETURN,in_value);
		Property(DEBUG_NAME,in_name);

	}
}

void BDebug::Return(){
	if( _ostream ){

		OpenCloseNode(DEBUG_RETURN, "");
		Property(DEBUG_NAME,"");

	}
}

void BDebug::State(const char* const in_name,const char* const in_state){
	if( _ostream ){

		OpenCloseNode(DEBUG_STATE,in_state);
		Property(DEBUG_NAME,in_name);

	}
}

void BDebug::EnterLoop(const char* const in_name ){
	if( _ostream ){

		OpenNode(DEBUG_LOOP);
		Property(DEBUG_NAME,in_name);
		_loops.push(0);

	}
}

void BDebug::Loop(){
	if( _ostream ){
		if( _loops.size() && _loops.top() ){		// if it is not the first loop
			CloseNode(DEBUG_ITERATION);				// there is a node to close
		}
		OpenNode(DEBUG_ITERATION);					// open not for iteration

		if( _loops.size() ){						// write loop number
			BString str(10);
			str.append(_loops.top());
			Property(DEBUG_COUNT,str.string());
			++_loops.top();							// inc loop
		}

	}
}

void BDebug::EndLoop(){
	if( _loops.size() ){
		if( _loops.top() ){
			CloseNode(DEBUG_ITERATION);
		}
		_loops.pop();
	}
	CloseNode();
}

Example

#include <iostream>
#include "BTagWriter.hpp"
#include "BDebug.hpp"
/*
int main(int argc, char *argv[])
{

	BTagWriter tg(&std::cout);
	tg.OpenNode("zer");
	tg.Property("const BChar* in_param","const BChar* in_value");
	tg.OpenNode("zer");
	tg.Property("const BChar* in_param","const BChar* in_value");

	tg.OpenCloseNode("const BChar* in_type","const BFloat& in_value");
	tg.OpenCloseNode("const BChar* in_type",0);
	tg.Property("tata","toto");
	tg.CloseNode();
	tg.CloseNode();

    return 0;
}
*/

void aMethode(){
 		BDEBUGGING( BDebug::Controler.EnterFunction("aMethode") );
 		BDEBUGGING( BDebug::Controler.Param("Object", "random value") );

 		BDEBUGGING( BDebug::Controler.State("me", "youpla") );

 		float average = 0.0;

 		BDEBUGGING( BDebug::Controler.EnterLoop("Average Calcul") );
 		for(int i = 0 ; i < 2 ; ++i){
 			BDEBUGGING( BDebug::Controler.Loop() );
 			BDEBUGGING( BDebug::Controler.Value("Loop",i) );
 			average += i;
 			BDEBUGGING( BDebug::Controler.Value("Current Average" , average) );
 		}
 		BDEBUGGING( BDebug::Controler.EndLoop() ); // optional param "Average Calcul"		

 		average /= 10;
 		BDEBUGGING( BDebug::Controler.Value("Final Average" , average) );

 		BDEBUGGING( BDebug::Controler.LeaveFunction() ); //optional param "aMethode"
 }

int main(){
	BDEBUGGING( BDebug::Controler.ostream( &std::cout ) );

	aMethode();	

	return 0;
}

Output

<Function Name="aMethode">
    <Param Name="Object">random value</Param>
    <State Name="me">youpla</State>
    <Loop Name="Average Calcul">
        <Iteration Count="0">
            <Value Name="Loop">0</Value>
            <Value Name="Current Average">0.0</Value>
        </Iteration>
        <Iteration Count="1">
            <Value Name="Loop">1</Value>
            <Value Name="Current Average">1</Value>
        </Iteration>
    </Loop>
    <Value Name="Final Average">0.10000</Value>
</Function>
Appuyez sur une touche pour continuer...

Download

BDebug

Enjoyed reading this post?
Subscribe to the RSS feed and have all new posts delivered straight to you.

Comments are closed.

Celadon theme by the Themes Boutique