Figure 1 BugslayerExt.cpp
/*----------------------------------------------------------------------
Microsoft Systems Journal - Bugslayer Column - January 2000
John Robbins - www.jprobbins.com
----------------------------------------------------------------------*/
// Include the usual suspects.
#include <windows.h>
// Include the WinDBG extension API file. The WinDBG documentation
// does not mention the 64 bit interface at all. However, if you use
// any of the 32-bit structures or definitions WinDBG complains.
// Therefore, I guess that means you are supposed to use them.
#define KDEXT_64BIT
// Turn off some warnings that happen with WinDBG extensions.
// Unreferenced formal parameter.
#pragma warning ( disable : 4100 )
// Signed/unsigned mismatches.
#pragma warning ( disable : 4211 )
// More signed/unsigned mismatches.
#pragma warning ( disable : 4245 )
#include <WdbgExts.h>
// The entire WinDBG API must be undecorated names. Since this is a C++
// file, I will just wrap the whole thing in an extern "C".
#ifdef __cplusplus
extern "C" {
#endif
/*//////////////////////////////////////////////////////////////////////
Definitions and Typedefs
/////////////////////////////////////////////////////////////////////*/
// The timer structure for each individual timer.
typedef struct tag_TIMERDATA
{
// TRUE if the timer is active.
BOOL bIsActive ;
// The start time for this timer.
DWORD dwStartTime ;
} TIMERDATA , * PTIMERDATA ;
// The number of timers.
const int k_NUMTIMERS = 4 ;
/*//////////////////////////////////////////////////////////////////////
File Scope Globals Definitions
/////////////////////////////////////////////////////////////////////*/
// The structure that contains all the WinDBG functions. This is filled
// in with the WinDbgExtensionDllInit function. If you want to use the
// wrapper macros in WdbgExts.h, the extension must have this name.
static WINDBG_EXTENSION_APIS64 ExtensionApis ;
// The version information supplied to WinDbgExtensionDllInit.
static USHORT g_usMajorVersion ;
static USHORT g_usMinorVersion ;
// The version for this extension. I don't know if the first two
// numbers are important, but the third one must be
// EXT_API_VERSION_NUMBER64 or WinDBG says that this extension is an
// old 32-bit version.
static EXT_API_VERSION g_stVersion =
{
3 ,
5 ,
EXT_API_VERSION_NUMBER64 ,
0
} ;
// The individual timers.
static TIMERDATA g_aTimers [ k_NUMTIMERS ] =
{
{ FALSE , 0 } ,
{ FALSE , 0 } ,
{ FALSE , 0 } ,
{ FALSE , 0 }
} ;
/*//////////////////////////////////////////////////////////////////////
Implementation Starts Here
/////////////////////////////////////////////////////////////////////*/
// A DllMain in case I need it for resources and stuff.
BOOL APIENTRY DllMain ( HINSTANCE hInst ,
DWORD ul_reason_for_call ,
LPVOID /*lpReserved*/ )
{
switch ( ul_reason_for_call )
{
case DLL_PROCESS_ATTACH :
// Shrink the working set a bit...
DisableThreadLibraryCalls ( hInst ) ;
break ;
case DLL_THREAD_ATTACH :
case DLL_THREAD_DETACH :
case DLL_PROCESS_DETACH :
break ;
}
return ( TRUE ) ;
}
// The initialization function.
VOID WDBGAPI
WinDbgExtensionDllInit ( PWINDBG_EXTENSION_APIS64 lpExtensionApis ,
USHORT usMajorVersion ,
USHORT usMinorVersion )
{
// Do a little structure copy action.
ExtensionApis = *lpExtensionApis ;
g_usMajorVersion = usMajorVersion ;
g_usMinorVersion = usMinorVersion ;
}
// WinDBG calls this to get the extension version.
LPEXT_API_VERSION WDBGAPI ExtensionApiVersion ( void )
{
return ( &g_stVersion ) ;
}
// The "echo" command. This prints out the arguments to the Command
// window. This command is very useful when you are dumping memory in a
// breakpoint command. The breakpoint commands do not print the
// commands as they are executing.
// The echo command also handles "\n" as a carraige return/linefeed so
// can space out the display if you are interested.
// The echo command with no arguments will just do a CR/LF to the
// Command window.
DECLARE_API64 ( echo )
{
// Is there anything in the argument parameter?
if ( ( NULL != args ) && ( '\0' != args[ 0 ] ) )
{
char szBuff[ MAX_PATH ] ;
int iBuffCurr = 0 ;
int iArgCurr = 0 ;
while ( '\0' != args[ iArgCurr ] )
{
if ( '\\' == args[ iArgCurr ] )
{
if ( 'n' == args[ iArgCurr + 1 ] )
{
if ( 0 != iBuffCurr )
{
szBuff[ iBuffCurr ] = '\0' ;
dprintf ( "%s" , szBuff ) ;
iBuffCurr = 0 ;
// Do the CR/LF
dprintf ( "\n" ) ;
}
else
{
dprintf ( "\n" ) ;
}
iArgCurr +=2 ;
}
else
{
szBuff[ iBuffCurr ] = args[ iArgCurr ] ;
iBuffCurr++ ;
iArgCurr++ ;
}
}
else
{
szBuff[ iBuffCurr ] = args[ iArgCurr ] ;
iBuffCurr++ ;
iArgCurr++ ;
}
}
if ( 0 != iBuffCurr )
{
szBuff[ iBuffCurr ] = '\0' ;
dprintf ( szBuff ) ;
}
}
else
{
dprintf ( "" ) ;
}
}
// The helper function all the timer functions will call to show help.
static void ShowTimerHelp ( void )
{
dprintf ( "WinDBG Timer Extensions\n" ) ;
dprintf ( "Usage : starttimer <timer id>\n" ) ;
dprintf ( " elapsetime <timer id>\n" ) ;
dprintf ( " stoptimer <timer id>\n" ) ;
dprintf ( " starttimer - Starts a timer going\n" ) ;
dprintf ( " elapsetime - Reports elapsed milliseconds since timer"
" start\n" ) ;
dprintf ( " stoptimer - Reports the time and ends the timer\n" ) ;
dprintf ( "<timer id> - A timer value between 1 and %d\n" ,
k_NUMTIMERS ) ;
}
static BOOL ConvertParamToIndex ( int & iIndex , PCSTR args )
{
// Are there any arguments?
if ( ( NULL == args ) && ( '\0' == args[ 0 ] ) )
{
ShowTimerHelp ( ) ;
return ( FALSE ) ;
}
// Try to convert the argument into a number between 1 and
// k_NUMTIMERS.
iIndex = atol ( args ) ;
if ( ( iIndex <= 0 ) || ( iIndex > k_NUMTIMERS ) )
{
dprintf ( "Invalid argument -- %s\n" , args ) ;
ShowTimerHelp ( ) ;
return ( FALSE ) ;
}
// Bump the value down to the index range.
iIndex-- ;
return ( TRUE ) ;
}
// The timer system.
// starttimer <timer id>
DECLARE_API64 ( starttimer )
{
int iIndex ;
if ( TRUE == ConvertParamToIndex ( iIndex , args ) )
{
// Is this timer item already in use?
if ( TRUE == g_aTimers[ iIndex ].bIsActive )
{
dprintf ( "Replacing active timer number %d\n" ,
iIndex + 1 ) ;
}
g_aTimers[ iIndex ].bIsActive = TRUE ;
g_aTimers[ iIndex ].dwStartTime = GetTickCount ( ) ;
}
}
// elapsedtime <timer id>
DECLARE_API64 ( elapsedtime )
{
int iIndex ;
if ( TRUE == ConvertParamToIndex ( iIndex , args ) )
{
if ( TRUE == g_aTimers[ iIndex ].bIsActive )
{
DWORD dwElapsed = GetTickCount ( ) -
g_aTimers[ iIndex ].dwStartTime ;
dprintf ( "Elapsed time (%d) = %d ms\n" ,
iIndex + 1 ,
dwElapsed ) ;
}
else
{
dprintf ( "Timer %d was not started\n" , iIndex ) ;
}
}
}
// stoptimer <timer id>
DECLARE_API64 ( stoptimer )
{
int iIndex ;
if ( TRUE == ConvertParamToIndex ( iIndex , args ) )
{
if ( TRUE == g_aTimers[ iIndex ].bIsActive )
{
DWORD dwElapsed = GetTickCount ( ) -
g_aTimers[ iIndex ].dwStartTime ;
dprintf ( "Timer (%d) ran %d ms\n" ,
iIndex + 1 ,
dwElapsed ) ;
g_aTimers[ iIndex ].bIsActive = FALSE ;
}
else
{
dprintf ( "Timer %d was not started\n" , iIndex ) ;
}
}
}
// The help command for this extension. WinDBG will also call this if
// the user uses the "?" command.
DECLARE_API64 ( help )
{
dprintf ( "Bugslayer WinDBG Extension DLL\n" ) ;
dprintf ( "Echo output\n" ) ;
dprintf ( "Usage : echo <text>\n" ) ;
dprintf ( " Prints the text to the command window.\n" ) ;
dprintf ( " The text can contain '\\n' to delineate CR/LF\n" ) ;
ShowTimerHelp ( ) ;
}
// This closes the extern "C" from above. This must be the last thing
// in the file.
#ifdef __cplusplus
}
#endif