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.