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.
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;
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
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.