Click here to Skip to main content
15,880,972 members
Articles / Programming Languages / C++

A Simple C++ Function Call Stack Trace Utility

Rate me:
Please Sign up or sign in to vote.
3.02/5 (23 votes)
10 Feb 2006CPOL1 min read 233.3K   1.9K   39   21
How to record your function call stack for debug purposes

Introduction

Under UNIX/Linux platform, program coredump generates a core file, we can analyze the core file and determine what's wrong in our program. The core file records the function call stack (e.g. under gdb you can use bt command to see backtrace), that is mostly information for us to troubleshooting.

When I debug my program, I try to record the function call stack in program debug log to help me diagnose the program's defect. Fortunately, C++ gives us a simple way to implement this.

Rational

Just use C++ constructor/destructor semantic, we can make our own trace class. At the function entrance, we declare a local variable, it will invoke the constructor immediately, when leaving the function, the local variable will be destroyed and call destructor. That's all. what we need to do is implement a class's constructor and destructor. This rational is simple enough, but this way sometimes can help us a lot.

The complete code is as follows:

C++
// trace.hpp
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdarg.h>
#include <string>

#define CM_TRACE_FILE(trace_file)	cm::Trace::LogToFile(trace_file)

#define CM_TRACE_FUNC(func_name)    cm::Trace __CM_TRACE__(func_name, "()")
#define CM_TRACE_FUNC_ARG1(func_name, argfmt, arg)   \
            cm::Trace __CM_TRACE__(func_name, argfmt, arg)
#define CM_TRACE_FUNC_ARG2(func_name, argfmt, arg1, arg2)   \
            cm::Trace __CM_TRACE__(func_name, argfmt, arg1, arg2)
// more macros define....

namespace	cm
{
    class	Trace
    {
    public:
    	explicit Trace(char *func_name, const char* argsfmt, ...)
    	{
            char fmt[256] ={0};
            sprintf(fmt, "%s%s", func_name, argsfmt);
    	    va_list arglist;
    	    va_start(arglist, argsfmt);
    		LogMsg(depth_, depth_ * 2, fmt,  arglist);
    		va_end(arglist);
    		++depth_;
    	}

    	~Trace()
    	{
    		--depth_;
    	}

    	/// special the global log file.
    	void static LogToFile(const char *trace_file)
    	{
    		trace_file_ = trace_file;
    	}

    private:
    	void LogMsg(int depth, int align, const char *fmt, va_list args)
    	{
    		FILE	*fp = fopen(trace_file_.c_str(), "a+");
    		if (fp == NULL)
    		{
    			return;
    		}


    		time_t		curTime;
    		time(&curTime);

    		char	timeStamp[32] = { 0 };
    		strftime(timeStamp, sizeof(timeStamp), 
				"%Y%m%d.%H%M%S", localtime(&curTime));

    		// only log the timestamp when the time changes
    		unsigned int len = fprintf( fp, "%s %*.*s> (%d)",
    				(last_invoke_time_ != curTime) ? 
				timeStamp : "               ",
    				2 * depth,
    				2 * depth,
    				nest_,
    				depth);
    		last_invoke_time_ = curTime;
    		len += vfprintf(fp, fmt, args);
    		len += fwrite("\n", 1, 1, fp);
    		fflush(fp);
    		fclose(fp);
    	}

    private:
    	// the debug trace filename
    	static std::string	trace_file_;

    	// function call stack depth
    	static int			depth_;
    	static const char*  nest_;
    	static time_t       last_invoke_time_;
    };

    std::string Trace::trace_file_  = "";
    int         Trace::depth_       = 0;

    // arbitrarily support nesting 34 deep for no particular reason
    const char* Trace::nest_        = 
	"| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | ";
    time_t      Trace::last_invoke_time_ = 0;
}	// end namespace cm
#endif // CM_TRACE_20060209_HPP

Using the Code

First you need to call CM_TRACE_FILE() to special debug log filename, then at function entrance, call CM_TRACE(). Here is an example.

C++
#include "cm/trace.hpp"

void foo()
{
	CM_TRACE_FUNC("foo");
}

void bar(int x)
{
	CM_TRACE_FUNC_ARG1("bar", "(%d)", x);
	foo();
}

void foobar(int x, const char* s)
{
    CM_TRACE_FUNC_ARG2("foobar", "(%d, %s)", x, s);
    bar(789);
}

void foobar3(int x, int y, double z)
{
    CM_TRACE_FUNC_ARG3("foobar3", "(%d, %d, %f)", x, y, z);
    foobar(123, "4546");
}

int main()
{
    CM_TRACE_FILE("./trace.log");
    CM_TRACE_FUNC("main");
    foo();
    bar(23);
    foobar(33, "char");
    foobar3(12, 23, 34.45);
    return 0;
}

Then open `trace.log', use your favourite editor. At the end of file, you will see the following:

20060211.132431 > (0)main()
                | > (1)foo()
                | > (1)bar(23)
                | | > (2)foo()
                | > (1)foobar(33, char)
                | | > (2)bar(789)
                | | | > (3)foo()
                | > (1)foobar3(12, 23, 34.450000)
                | | > (2)foobar(123, 4546)
                | | | > (3)bar(789)
                | | | | > (4)foo()

Conclusion

The rational in this article is simple, but we can write simple code do useful things.

Revision History

  • v1.00 Feb 4, 2005. Mockey
    • Original article
  • v1.01 Feb 11 2005. Mockey
    • Change fstream to FILE*
    • Change string to sprintf for more efficiency. Thanks Jon Wold's comment
    • Rename CM_TRACE to CM_TRACE_FUN and add CM_TRACE_FUN_ARGx macros.
    • Add feature to log the function call arguments.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
China China
Mockey is a C++ programmer, experienced with C/C++ program under UNIX platform, his primarily research field is communication program for telecom. He works for one of china's biggest telecom equipment supplier until Dec 2006.

He's current interest is Linux kernel programming, especial in networking hack.

Mockey lives in Chengdu, China. To contact Mockey, email at mockey.chen@gmail.com

Comments and Discussions

 
Questioncan any one explain me what is that __CM_TRACE__ Pin
online@ilovec.in22-Jan-13 19:51
online@ilovec.in22-Jan-13 19:51 
AnswerRe: can any one explain me what is that __CM_TRACE__ Pin
Member 1112462910-Mar-15 9:00
Member 1112462910-Mar-15 9:00 
Generalany idea for win32 C dll trace log. Pin
fredguo1-Mar-09 6:33
fredguo1-Mar-09 6:33 
Generalbug.. Pin
dheerajkhajuria8-Jul-07 6:17
dheerajkhajuria8-Jul-07 6:17 
GeneralRe: bug.. Pin
Mockey Chen8-Jul-07 15:29
Mockey Chen8-Jul-07 15:29 
GeneralCM_TRACE_FUNC_ARG3("foobar3", "(%d, %d, %s)", x, y, 100); Pin
Pham Thanh Toan3-Jan-07 6:57
Pham Thanh Toan3-Jan-07 6:57 
GeneralRe: CM_TRACE_FUNC_ARG3("foobar3", "(%d, %d, %s)", x, y, 100); Pin
Mockey Chen6-Jan-07 0:48
Mockey Chen6-Jan-07 0:48 
Generalsprintf is declared as deprecated Pin
DevianT (rus)21-Feb-06 13:27
DevianT (rus)21-Feb-06 13:27 
GeneralRe: sprintf is declared as deprecated Pin
Nemanja Trifunovic21-Feb-06 13:46
Nemanja Trifunovic21-Feb-06 13:46 
GeneralRe: sprintf is declared as deprecated Pin
DevianT (rus)21-Feb-06 20:21
DevianT (rus)21-Feb-06 20:21 
Generalsome advice Pin
thog14-Feb-06 16:27
thog14-Feb-06 16:27 
GeneralRe: some advice Pin
Gavriloaie Andrei26-Oct-07 22:54
Gavriloaie Andrei26-Oct-07 22:54 
Generalscope logger Pin
Paulius Maruka14-Feb-06 14:17
Paulius Maruka14-Feb-06 14:17 
GeneralNot a stack trace Pin
Scope12-Feb-06 16:08
Scope12-Feb-06 16:08 
Generalminor comments ... Pin
Jon Wold7-Feb-06 5:44
Jon Wold7-Feb-06 5:44 
GeneralRe: minor comments ... Pin
Mockey Chen10-Feb-06 18:53
Mockey Chen10-Feb-06 18:53 
GeneralAnd throwing in a couple of VC++ macros... Pin
eFotografo7-Feb-06 0:19
professionaleFotografo7-Feb-06 0:19 
GeneralRe: And throwing in a couple of VC++ macros... Pin
Mockey Chen7-Feb-06 4:32
Mockey Chen7-Feb-06 4:32 
GeneralRe: And throwing in a couple of VC++ macros... Pin
Pavel Vozenilek13-Feb-06 18:54
Pavel Vozenilek13-Feb-06 18:54 
GeneralCool Pin
FocusedWolf6-Feb-06 12:46
FocusedWolf6-Feb-06 12:46 
I'm still learning my way around c++, which once you know the semantics' really means re-inventing stuff like this, so thx for teaching me what a stack trace is :P

I wonder if theirs anyway to just have a class inherit some "stack trace" class to make it easier to log stuff without worrying about which functions are added in the future. Also would this work if their were multiple classes in differant files... would they all log to the same file, or is multiple logs the desired effect?
GeneralRe: Cool Pin
Mockey Chen7-Feb-06 12:18
Mockey Chen7-Feb-06 12:18 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.