Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / programming / debugging

A C++ Code Function Tracing Class

5.00/5 (20 votes)
9 Apr 2024CPOL3 min read 22.6K   719  
Trace your function calls to the Output window.
A simple plug in class that can be used to trace the function calls as your code is run.

Introduction

The code presented in this tip is several years old. I first came up with it when I was developing my TileGrid Control. As part of the debugging and streamlining process, I would try to figure out which functions were getting called, in what order, and how often. The built in debugger call stack is way too limited as it only shows the current call stack, it does not show all the functions that were called in between.

At first, I would do it by constantly stepping through the code using the F10 (step over), F11 (step into) and <shift>F11 (step out) buttons on the keyboard. That got very tedious really fast.

My next idea to make it easier was to put TRACE statements at the start of every function. Well, that kind of worked, I did get the function calls listed in the debug output window. But there was no easy way to tell which function was calling which function. There was no call hierarchy visible.

It was then that I came up with the FunctionTraceClass. It does exactly what I needed it to do.

Here are the outputs from the call stack and from the function trace class when I click on the add tile button in the demo app for the above mentioned TileGrid control with a breakpoint set in the CTileGrid::SelectTile() function.

First, the call stack:

TileGridDialog.exe!CTileGrid::SelectTile(CTileBase * pTile, bool bSelect) Line 1185    C++
TileGridDialog.exe!CTileGrid::SetFocusedTile(CTileBase * pTile) Line 1268    C++
TileGridDialog.exe!CTileGridDialogDlg::OnBnClickedButtonaddtile() Line 445    C++
[External Code]
TileGridDialog.exe!CAutoRepeatButton::OnLButtonUp
               (unsigned int nFlags, CPoint point) Line 105    C++
[External Code]
TileGridDialog.exe!CTileGridDialogApp::InitInstance() Line 75    C++
[External Code]
TileGridDialog.exe!wWinMain(HINSTANCE__ * hInstance,
  HINSTANCE__ * hPrevInstance, wchar_t * lpCmdLine, int nCmdShow) Line 26    C++
[External Code]

As you can see, it is not very useful as it does not show the complete picture. Now, here is the output from the function trace class. It is long, but very informative.

CTileGrid::AddTile(struct CRuntimeClass *,bool)
+--CTileGrid::AddTile(class CTileBase *,bool,bool)
|  +--CTileGrid::SCN::SCN(class CTileGrid *,bool)
|  +--CTileGrid::GetTileAtIndex(int)
|  |  +--CTileGrid::GetTileCount(void)
|  +--CTileGrid::ClearSelections(class CTileBase *,class CTileBase *)
|  |  +--CTileGrid::GetTileAtIndex(int)
|  |  |  +--CTileGrid::GetTileCount(void)
|  +--CTileGrid::GetTileCount(void)
|  +--CTileGrid::CTileGrid_NotifyParent(unsigned int,class CTileBase *,struct tagNMHDR *)
|  +--CTileGrid::GetTileCount(void)
|  +--CTileGrid::GetTileCount(void)
|  +--CTileGrid::GetTileAtIndex(int)
|  |  +--CTileGrid::GetTileCount(void)
|  +--CTileGrid::GetTileAtIndex(int)
|  |  +--CTileGrid::GetTileCount(void)
|  +--CTileGrid::CTileGrid_NotifyParent(unsigned int,class CTileBase *,struct tagNMHDR *)
|  |  +--CTileGrid::GetTileCount(void)
|  |  +--CTileGrid::GetSelectionCount(void)
|  |  |  +--CTileGrid::GetTileAtIndex(int)
|  |  |  |  +--CTileGrid::GetTileCount(void)
|  +--CTileGrid::IsTileFullyVisible(class CTileBase *)
|  |  +--CTileGrid::GetTileAtIndex(int)
|  |  |  +--CTileGrid::GetTileCount(void)
|  |  +--CTileGrid::GetTileRect(class CTileBase *)
|  |  |  +--CTileGrid::GetTileAtIndex(int)
|  |  |  |  +--CTileGrid::GetTileCount(void)
|  +--CTileGrid::IsTileVisible(class CTileBase *)
|  |  +--CTileGrid::GetTileAtIndex(int)
|  |  |  +--CTileGrid::GetTileCount(void)
|  |  +--CTileGrid::GetTileRect(class CTileBase *)
|  |  |  +--CTileGrid::GetTileAtIndex(int)
|  |  |  |  +--CTileGrid::GetTileCount(void)
|  +--CTileGrid::RedrawTile(class CTileBase *)
|  |  +--CTileGrid::GetTileAtIndex(int)
|  |  |  +--CTileGrid::GetTileCount(void)
|  |  +--CTileGrid::IsTileVisible(class CTileBase *)
|  |  |  +--CTileGrid::GetTileAtIndex(int)
|  |  |  |  +--CTileGrid::GetTileCount(void)
|  |  |  +--CTileGrid::GetTileRect(class CTileBase *)
|  |  |  |  +--CTileGrid::GetTileAtIndex(int)
|  |  |  |  |  +--CTileGrid::GetTileCount(void)
|  |  +--CTileGrid::GetTileRect(class CTileBase *)
|  |  |  +--CTileGrid::GetTileAtIndex(int)
|  |  |  |  +--CTileGrid::GetTileCount(void)
|  +--CTileGrid::SCN::~SCN(void)
|  |  +--CTileGrid::CTileGrid_NotifyParent(unsigned int,class CTileBase *,struct tagNMHDR *)
|  |  |  +--CTileGrid::GetSelectionCount(void)
|  |  |  |  +--CTileGrid::GetTileAtIndex(int)
|  |  |  |  |  +--CTileGrid::GetTileCount(void)

CTileGrid::SetFocusedTile(class CTileBase *)
+--CTileGrid::GetTileAtIndex(int)
|  +--CTileGrid::GetTileCount(void)
+--CTileGrid::GetTileAtIndex(int)
|  +--CTileGrid::GetTileCount(void)
+--CTileGrid::SCN::SCN(class CTileGrid *,bool)
+--CTileGrid::SelectTile(class CTileBase *,bool)
|  +--CTileGrid::GetTileAtIndex(int)
|  |  +--CTileGrid::GetTileCount(void)

Using the Code

The code is very easy to use. Add the files, FunctionTrace.h and FunctionTrace.cpp to your project. These files are in available via the download link at the top of this tip.

In the first line of any function you want to trace, you place the FUNC_TRACE macro.

If there are any functions you do not want to trace, you would place the NO_FUNC_TRACE macro. The NO_FUNC_TRACE macro disables all FUNC_TRACE macros that are under it in the call hierarchy.

To toggle function tracing on or off for your entire project, you have to go into the FunctionTrace.h file and uncomment or comment out line 5 to determine if the ENABLEFUNCTIONTRACE macro is defined or not defined.

//#define  ENABLEFUNCTIONTRACE  // uncomment this line to enable debug function tracing

Or, if you want to use this in a single source code file you need to simply #define ENABLEFUNCTIONTRACE before you #include "FunctionTrace.h"".

#define ENABLEFUNCTIONTRACE
#include "FunctionTrace.h"

Update

There was a comment made in the discussion board below this article that I initially dismissed, but the more I thought about the more sense it made. As a result I have updated the code so that the tracing output can be sent either to the console or to a text file. The alternate output methods should solve the problem of having other debugging messages interspersed between the function tracing output.

The update is an addition of two new function macros. They are FUNC_TRACE_TO_CONSOLE and FUNC_TRACE_TO_FILE(filename). Just place one of these macros just after your #include "FunctionTrace.h" line.

#define ENABLEFUNCTIONTRACE
#include "FunctionTrace.h"
FUNC_TRACE_TO_CONSOLE;    // Send the output to the console
// FUNC_TRACE_TO_FILE ("MyTestFile.txt");  // Send the output to the file "MyTestFile.txt"

The default output method, which is used if neither of these two macros are used, is still the debugger output window.

You only need to call one of these macros in a single source file in your project, it makes no difference where you call it. If you call these macros multiple times in your code, only the very first one that is executed will have any effect on the output.

The download also includes a demonstration console application file:

#include "FunctionTrace.h"

#define ENABLEFUNCTIONTRACE
#include "FunctionTrace.h"
FUNC_TRACE_TO_CONSOLE
//FUNC_TRACE_TO_FILE ("MyTestFile.txt")

class Test
{
public:
    void FuncA()
    {
        FUNC_TRACE;
        FuncB();
        FuncC();
    }

    void FuncB()
    {
        NO_FUNC_TRACE;
        FuncC();
    }

    void FuncC()
    {
        FUNC_TRACE;
    }

    Test()
    {
        FUNC_TRACE;
    }
};

int main()
{
    FUNC_TRACE;
    Test t;
    t.FuncA();
    t.FuncB();
    t.FuncC();
  }

The output of the demo file is:

main(void)
+--Test::Test(void)
+--Test::FuncA(void)
|  +--Test::FuncC(void)
+--Test::FuncC(void)

History

  • 5th March, 2024 - Posted to CodeProject hoping that maybe someone may find this interesting and/or useful
  • 8th March, 2024 - Updated the file so it can be used in a single source file. Was previously used project wide.
  • 9th April, 2024 - Added the FUNC_TRACE_TO_CONSOLE and FUNC_TRACE_TO_FILE(filename) macros.

License

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