DebugHook: Unobtrusive COM Tracing
Copyright (c) 1998, Chris Sells and Keith Brown
All rights reserved.
No warranties.
Comments to csells@sellsbrothers.com or
kbrown@develop.com.
This source code is a set of objects to trace COM calls between
apartments and machines as well as in-process. It is broken up
into several cooperating components.
Modules
DebugHook.dll
This server holds the TraceHook class and the StackTrace component
as well as some helper functions for accessing the current StackTrace.
To build the DebugHook.dll, use the debughook.dsw.
TraceLog.exe
This is a COM server that is used to show the COM call tracing
information held in the StackTrace component. It must be started
manually. When it starts, it creates a single object (the TraceLogger),
and puts it into the ROT. A StackTrace is dumped by using the GetActiveObject
function using CLSID_TraceLogger and IID_ILogStackTrace. To build the
TraceLog.exe, use the debughook.dsw.
client.exe (in the root directory)
This is a test of the StackTrace object. It simulates a nested COM
call and dumps it to the TraceLogger. It is for testing only. To build
client.exe, use the debughook.dsw.
dhtest.exe (in the dhtest sub-directory)
This is an out-of-proc server for use with testing the call
tracing framework. It contains a single class, CLSID_DhTest, with
a single interface, IID_DhTest1, with a single method, Test(long nDepth).
The test method will create other instances of itself on the same machine
for nDepth-1 instances. To build dhtest.exe, use the dhtest.dsw in the
dhtest sub-directory.
client.exe (in the dhtest sub-directory)
This is a client for testing the call tracing framework. It creates
an instance of CLSID_DhTest and calls Test. When this call returns,
and the TraceLogger is running, a new hierarchy of calls of whatever
depth was passed to the Test() method should appear in the TraceLogger.
Components
StackTrace
The StackTrace object is used to hold COM call entries. Each entry
maintains things like the call level, the time, server, class, interface,
method name and result. This call information is build as a COM call
progresses and dumped when the call has been completed. An interface
pointer to the current StackTrace object is maintained in TLS and
passed via a channel hook as the call progress from one apartment
to another. The StackTrace object implements IMarshal so that the
object is passed by value instead of by reference. The interesting
StackTrace code is kept in StackTrace.cpp. The helper functions
for accessing the current StackTrace is kept in DebugHook.cpp.
TraceHook
The TraceHook object is used to capture notifications of COM calls
entering and exiting apartments in each process. When the DebugHook.dll
is loaded, the DllMain function creates an instance of a TraceHook
object and registers it as a channel hook for that process. The TraceHook
accesses TLS looking for an instance of the StackTrace object and if it
finds one, will marshal in into the RPCTHIS packets and out of the
RPCTHAT packets. The interesting code for the TraceHook is kept in
TraceHook.cpp. The channel hook registration code is kept in DebugHook.cpp.
CoDelegator
The tracing blind delegator implementation. This code uses the
helper functions below to pull the current StackTrace object out
of TLS and log the current call as well as the result. An instance
of the delegator can be wrapped around any interface pointer with
a call to CreateTraceDelegator or a call to a higher-level function,
ComTrace or it's helpful macro equivilent, COMTRACE.
DhTest1
This is simple a test component to call when testing COM tracing. The
interesting code is kept in DhTest1.cpp.
Helper Functions
This helper functions wrap accessing the current StackTrace object in
TLS as well as provide helpers for adding entries to the current
stack trace object. These helpers are used by the TraceHook and the
delegator. These functions are declared in StackTraceAccess.h and
implemented in DebugHook.cpp.
GetStackTrace
This function gets the current StackTrace object out of TLS
or returns S_FALSE to indicate that there is no current StackTrace
object. This function also removes the current StackTrace object out
of TLS. The TraceHook uses this function to marshal the StackTrace
object, if one is present, or to send nothing.
SetStackTrace
Puts a StackTrace object into TLS. If there is already a StackTrace
object in TLS, it will release the interface.
StackTracePreCall
This function will access the currently set StackTrace, bump the
level for a new call and get the current time (which it returns
to the caller). If no StackTrace object is currently set, this
function will create one, starting the COM tracing process. The
delegator uses this function before forwarding the call to the
appropriate object method.
StackTracePostCall
This function appends a new stack entry to the current StackTrace
component and decrements the level. If the level gets to zero,
the COM call is completed and the current StackTrace object is
sent to the TraceLogger (if the TraceLogger is running). This
function is used by the delegator after the result of the call
it forwarded is known, i.e. the call has returned.
DumpStack
This function will access the current TraceLogger and, if it
is available, pass it the StackTrace object for logging.
CreateTraceDelegator
Creates an instance of the trace blind delegator around an interface.
ComTrace
A simplified wrapper for calling CreateTraceDelegator and
the only global function exposed from the DebugHook.dll.
COMTRACE
A macro that calls ComTrace if _DEBUG is defined or falls away if it isn't.