Logging Call Stacks with CeLog
Other versions of this page are also available for the following:
8/27/2008
You can use the CeLog functions together with kernel functions to log call stack information for any thread. Note that Kernel Tracker does not store call stack events. If you are logging call stack events, you should use CeLogFlush.exeor OSCapture.exeto flush the data.
To log a call stack
Turn on CELZONE_PROCESS and CELZONE_LOADER to make sure that CeLog records the memory locations for processes and DLLs.
If CELZONE_PROCESS and CELZONE_LOADER are turned off, CeLog does not record the process and loader addresses. As a result, the CeLog tools cannot map process and loader addresses to symbol addresses. In order to look up the symbols that make sense of the callstack data, these zones must be enabled.
Retrieve the call stack of the desired thread using GetThreadCallStack.
Call CeLogDatato log the call stack information gathered in step 2, using the event ID CELID_CALLSTACK.
To minimize performance degradation
To minimize the performance degradation that querying the stack might cause, consider logging only after verifying that CeLog is loaded, using IsCeLogStatus. If CeLog is not loaded, the code takes no action.
- or -
Log only after determining that a specific zone such as CELZONE_MISC is enabled, using IsCeLogZoneEnabled with (CELZONE_xxx). If the specified zone is not enabled, the code takes no action.
To guard against modifications
- To guard against modifications that GetThreadCallStack might make, preserve the LastError value. Call GetLastError to get the value before calling GetThreadCallStack, and SetLastError to restore the value after you are done.
Example
The following example uses CeLogDataand kernel functions to retrieve stack frame information for a particular thread, and logs the information to the CeLog buffer.
#include "pkfuncs.h"
#include "celog.h"
__inline static void
MyLogStackFunc(
{
\\ Check to see if CeLog is enabled. If not, stop now
if (IsCeLogStatus(CELZONE_ENABLED_ANY)) {
\\ Set up the structure that holds the stack.
\\ This example uses 20 frames to keep stack usage low.
#define MY_STACK_BUFFER_LEN 20
CallSnapshot cs[MY_STACK_BUFFER_LEN];
\\## IMPORTANT ## Preserve lasterr value while capturing callstack
DWORD dwLastError = GetLastError();
DWORD dwNumFrames, dwFramesLogged;
dwFramesLogged = 0;
do {
\\ Get the stack for the current thread
dwNumFrames = GetThreadCallStack(
(HANDLE) GetCurrentThreadId(),
MY_STACK_BUFFER_LEN, cs, 0, dwFramesLogged);
if (dwNumFrames) {
\\ Log the stack information as a CeLog event
CeLogData(TRUE, CELID_CALLSTACK, (PVOID) cs,
(WORD)(dwNumFrames * sizeof(CallSnapshot)),
0, CELZONE_ALWAYSON, 0, FALSE);
dwFramesLogged += dwNumFrames;
}
// Keep looping if there are more than 20 stack frames,
// logging a callstack event for every 20 frames.
} while (dwNumFrames == MY_STACK_BUFFER_LEN);
SetLastError(dwLastError);
}
}
To resolve symbols and have Readlog show the functions that correspond to each address in the call stack, use Readlog with the -v
command-line parameter. For more information, see Readlog Command-Line Options.
The following example shows Readlog output from MyLogStackFunc. Callstack events are displayed following a header line that contains the words Callstack (from previous event).
Because the call stack for this module was larger than the 20-frame limit set in MyLogStackFunc, the stack is logged as two events.
0:23:51.823.311 : Callstack (from previous event):
0x03F6B658 coredll.dll!xxx_GetThreadCallStack
0x09A05530 afd.dll!MyLogStackFunc
0x09A05370 afd.dll!DeleteSocket
0x09A054BC afd.dll!SockDeref
0x09A06C4C afd.dll!AfdClose
0x8014B18C NK.EXE!DoCloseAPIHandle
0x03061834 wspm.dll!WSPCloseSocket
0x03AE8338 dtpt_lsp.dll!?WSPCloseSocket@DPROVIDER
0x03AE4934 dtpt_lsp.dll!?WSPCloseSocket@CLSPSocketPassthrough
0x03AE2284 dtpt_lsp.dll!?WSPCloseSocket
0x03857428 autobind_lsp.dll!?WSPCloseSocket
0x03857CD4 autobind_lsp.dll!?WSPCloseSocket@CLSPSocketPassthrough
0x03852344 autobind_lsp.dll!?WSPCloseSocket
0x03304048 ssllsp.dll!?WSPCloseSocket
0x03301DFC ssllsp.dll!?WSPCloseSocket
0x02E02410 ws2.dll!closesocket
0x02E07460 ws2.dll!IsIp6Running
0x02E07CB8 ws2.dll!getaddrinfo
0x03472C4C ipv6hlp.dll!GetAddrInfoW
0x03473CAC ipv6hlp.dll!SetIsatapRouterAddress
0:23:51.825.524 : Callstack (from previous event):
0x03474E0C ipv6hlp.dll!Add6to4Address
0x03475430 ipv6hlp.dll!ProcessInterfaceStateChange
0x03475778 ipv6hlp.dll!OnChangeInterfaceInfo
0x03471DCC ipv6hlp.dll!WaitForSingleObjectThread
0x03F6CD14 coredll.dll!ThreadBaseFunc
See Also
Other Resources
CallSnapshot
GetLastError
GetThreadCallStack
GetCurrentThreadId
CeLogData
CeLog Event Identifiers
CeLog Zones