Figure 1   SOURCES


 ########################################################################
 #  John Robbins - Jan '99 Microsoft Systems Journal Bugslayer Column
 ########################################################################
 ########################################################################
 # This is a sample sources file you can use as a template.  This
 #  template includes the extra defines to take advantage of the new
 #  targets in DDKCommon.inc
 #
 ########################################################################
 #
 # To use this sample, DDKCOMMON.INC will need to be in one of the
 #  directories specified in your INCLUDE environment variable.
 #
 ########################################################################
 # Helpful hint from the BUILD.EXE documentation:
 #  There should be no blank spaces between a BUILD keyword and the equal
 #  sign. For example, the following line is invalid:
 #
 #  SOURCES = file.c
 #
 #  while the following line is valid:
 #
 #  SOURCES= file.c
 #
 ########################################################################
 # The first section is nothing but mandatory items.
 ########################################################################
 
 ########################################################################
 # The TARGETNAME.  This is the name of the item being built (without the
 #  extension).  This is mandatory.
 TARGETNAME=
 
 ########################################################################
 # The path where all binaries are built.  This is just the initial part.
 #  BUILD.EXE will put them into the $(TARGETPATH)\$(CPU)\$(DDKBUILDENV)
 #  Directory.
 TARGETPATH=
 
 ########################################################################
 # The type of item that is being built.  This is mandatory.
 # Value         Meaning
 # DYNLINK       - A DLL.
 # DRIVER        - A kernel device driver.
 # EXPORT_DRIVER - A kernel device driver with exports.
 # PROGRAM       - A windows executable.
 # PROGLIB       - A windows library.
 # MINPORT       - A miniport driver.
 # GDI_DRIVER    - A video driver.
 # LIBRARY       - A library (?)
 TARGETTYPE=
 
 ########################################################################
 # All the source files in this project.  This is mandatory.
 SOURCES=
 
 ########################################################################
 # The items below this line are not mandatory, but they are certainly
 #  helpful!
 ########################################################################
 
 ########################################################################
 # If you can, always use precompiled headers to speed up your builds.
 #  The first macro is the name of the precompiled header.  If you are
 #  using .CPP/.CXX extensions, the second is needed as well.
 #PRECOMPILED_INCLUDE=PCH.h
 #PRECOMPILED_CXX=1
 
 ########################################################################
 # The include directories for your project.
 #INCLUDES=..\Include;$(INCLUDE)
 
 ########################################################################
 # Set defines particular to the driver.
 #C_DEFINES=$(C_DEFINES)
 
 ########################################################################
 # Turn on PDB file generation.  This seems to only work for checked
 #  builds as *FULL* source PDB files are not generated for free builds.
 USE_PDB=1
 
 # These are the flags that get *FULL* debugging information turned on
 #  for free builds.  While the BUILD.EXE documentation implies that you
 #  should not process conditional syntax for its "keywords" but this
 #  seems to work fine.  I do not know if it will work in the future.
 !if "$(DDKBUILDENV)"=="free"
 NTDEBUGTYPE=windbg
 NTDEBUG=ntsdnodbg
 LINKER_NOREF=1
 !endif
 
 ########################################################################
 # Set the warning levels to maximum.
 ALPHA_WARNING_LEVEL=/W4
 I386_WARNING_LEVEL=/W4
 
 ########################################################################
 # Set the linker to produce a map file.  A must have in emergency
 #  situations.
 LINKER_FLAGS=$(LINKER_FLAGS) -MAP -MAPINFO:EXPORTS -MAPINFO:LINES
 
 ########################################################################
 # If you need to link against some libs that you have, you can specify
 #  them here.
 #TARGETLIBS=$(TARGETPATH)\*\$(DDKBUILDENV)\Track.lib
 
 ########################################################################
 # User Mode program settings.
 
 # The type, console or windows.
 #UMTYPE=console
 #UMTYPE=windows
 
 # The base address.  For EXE's, this needs to be 0x400000.
 #UMBASE=0x400000
 
 # Use MSVCRT.DLL instead of the NT CRT DLL.  Always use this when doing
 # console programs.
 #USE_MSVCRT=1
 
 ########################################################################
 # This puts all checked build .OBJ files into the .\objd directory
 #  to keep the free and checked apart.  The final binaries still got to
 #  the free and checked output directories.
 CHECKED_ALT_DIR=1
 
 ########################################################################
 # Things to be built after the initial scan by BUILD.EXE.  Always put
 #  MakeDirs first so the DDKHelper DDKCOMMON.MAK gets pulled in and the
 #  output directories get made.
 NTTARGETFILE0=MakeDirs
 
 ########################################################################
 # If you want to automatically create the .DBG file for your driver, you
 #  can uncomment the following macro lines.  You need to set the
 #  CHECKED_DBGDIRS and FREE_DBGDIR to your check and free symbols
 #  directories, respectively.  If you want to set the base address for
 #  your driver, you can use the DEVBASE macro.  If you don't set it, the
 #  default is 0x10000.
 #DEVBASE=0x10000
 #CHECKED_DBGDIR=e:\Checked\Symbols
 #FREE_DBGDIR=e:\WinNT\Symbols
 #NTTARGETFILES=CreateDBG

Figure 2   Better BUILD_DEFAULT Settings


-F = When displaying errors/warnings to stdout, print the full path
-w = Shows warnings on screen
-e = Generates BUILD.LOG, BUILD.WRN, and BUILD.ERR files
-b = Displays full error message text

Figure 3   DrvDiagnostics.h


 /*----------------------------------------------------------------------
    John Robbins - Jan '99 Microsoft Systems Journal Bugslayer Column
 ------------------------------------------------------------------------
 VERY useful driver diagnostic routines.
 ----------------------------------------------------------------------*/
 
 #ifndef _DRVDIAGNOSTICS_H
 #define _DRVDIAGNOSTICS_H
 
 #if DBG !=0
 #ifndef _DEBUG
 #define _DEBUG
 #endif
 #endif
 
 ////////////////////////////////////////////////////////////////////////
 // The usual trace macros.
 #ifdef _DEBUG
 
 // The trace macros that work in C and C++.
 #define TRACE0(x)       DbgPrint ( x )
 #define TRACE1(x,y)     DbgPrint ( x , y )
 #define TRACE2(x,y,z)   DbgPrint ( x , y , z )
 #define TRACE3(x,y,z,a) DbgPrint ( x , y , z , a )
 
 // A VERIFY macro.
 #define VERIFY(x) ASSERT(x)
 
 // The C++ specific trace statements.
 #ifdef __cplusplus
 #define TRACE           DbgPrint
 #endif   // __cplusplus
 
 #ifdef __cplusplus
 // The C++ BreakIfKdPresent function.
 inline void BreakIfKdPresent ( void )
 {
     // Wind through the IAT and grab the real value.
     PULONG * pData = (PULONG*)(&KdDebuggerEnabled) ;
     if ( 1 == **pData )
     {
         DbgBreakPoint ( ) ;
     }
 } ;
 
 #else
 // The C BreakIfKdPresent macro.
 #define BreakIfKdPresent()                                  \
     {                                                       \
         PULONG * pData = (PULONG*)(&KdDebuggerEnabled) ;    \
         if ( 1 == **pData )                                 \
         {                                                   \
             DbgBreakPoint ( ) ;                             \
         }                                                   \
     }
 #endif
 
 // The C/C++ ASSERTIRQL define.  This checks that the IRQL is equal to
 //  that specified.  If it is not, then the assert fires.
 #define ASSERTIRQL(x)                                       \
             ASSERTMSG ( "KeGetCurrentIrql ( ) == "#x ,      \
                          KeGetCurrentIrql ( ) == x    ) ;
 
 // For consistency....
 #define ASSERTIRQL_EQ(x)    ASSERTIRQL ( x )
 
 // Is the IRQL less than x?
 #define ASSERTIRQL_LT(x)                                    \
             ASSERTMSG ( "KeGetCurrentIrql ( ) < "#x ,       \
                          KeGetCurrentIrql ( )< x     ) ;
 
 // Is the IRQL less than or equal to x?
 #define ASSERTIRQL_LE(x)                                    \
             ASSERTMSG ( "KeGetCurrentIrql ( ) <= "#x ,      \
                          KeGetCurrentIrql ( ) <= x    ) ;
 
 // Is the IRQL greater than or equal to x?
 #define ASSERTIRQL_GE(x)                                    \
             ASSERTMSG ( "KeGetCurrentIrql ( ) >= "#x ,      \
                          KeGetCurrentIrql ( ) >= x    ) ;
 
 // Is the IRQL greater than x?
 #define ASSERTIRQL_GT(x)                                    \
             ASSERTMSG ( "KeGetCurrentIrql ( ) > "#x ,       \
                          KeGetCurrentIrql ( ) > x    ) ;
 
 // Is the IRQL not equal to x?
 #define ASSERTIRQL_NE(x)                                    \
             ASSERTMSG ( "KeGetCurrentIrql ( ) != "#x ,      \
                          KeGetCurrentIrql ( ) != x    ) ;
 
 #else   // _DEBUG is not defined.
 
 #define TRACE0(x)
 #define TRACE1(x,y)
 #define TRACE2(x,y,z)
 #define TRACE3(x,y,z,a)
 
 #define BreakIfKdPresent()
 
 #define ASSERTIRQL(x)
 #define ASSERTIRQL_EQ(x)
 #define ASSERTIRQL_LE(x)
 #define ASSERTIRQL_GE(x)
 #define ASSERTIRQL_GT(x)
 #define ASSERTIRQL_NE(x)
 
 #define VERIFY(x) x
 
 #ifdef __cplusplus
 inline void _cdecl FakeDbgPrint ( PCH , ... ) { }
 #define TRACE        1 ? (void)0 : ::FakeDbgPrint
 #endif   // __cplusplus
 
 #endif      // _DEBUG
 
 
 #endif      // _DRVDIAGNOSTICS_H

Figure 4   Track Functions

Track Type
Function
Memory
ExAllocatePool
ExAllocatePoolWithTag
ExAllocatePoolWithQuota
ExAllocatePoolWithQuotaTag
ExFreePool
MmAllocateContiguousMemory
MmAllocateNonCachedMemory
MmFreeContiguousMemory
MmFreeNonCachedMemory
General Resource
IoCreateDevice
IoDeleteDevice
RtlAnsiStringToUnicodeString
RtlFreeUnicodeString
RtlUnicodeStringToAnsiString
RtlFreeAnsiString
MmMapIoSpace
MmUnmapIoSpace
IoAllocateMdl
IoBuildPartialMdl
IoFreeMdl
MmMapLockedPages
MmUnmapLockedPages
Handle
ZwClose
IoCreateNotificationEvent
IoCreateSynchronizationEvent
ZwCreateDirectoryObject
ZwCreateFile
ZwOpenKey
ZwOpenSection


Figure 5   Track_MmMapIoSpace and Track_MmUnmapIoSpace


 PVOID Track_MmMapIoSpace ( IN PHYSICAL_ADDRESS  PhysicalAddress,
                            IN ULONG  NumberOfBytes,
                            IN MEMORY_CACHING_TYPE CacheType,
                            char * szFile,
                            ULONG  uLine  )
 {
     // Make the call no matter what.
     PVOID pRet = MmMapIoSpace ( PhysicalAddress ,
                                 NumberOfBytes   ,
                                 CacheType        ) ;
     if ( ( TRUE == InternalInitialized ( ) ) &&
          ( NULL != pRet                    )    )
     {
         // Add away.
         InternalAddSimpleAlloction ( TRACKCLASS_GENERALRESOURCES  ,
                                      eMmMapIoSpace        ,
                                      (ULONG)pRet          ,
                                      szFile               ,
                                      uLine                 ) ;
         g_GeneralResourcesStats.ulTotalCalls++ ;
     }
     return ( pRet ) ;
 }
 
 VOID Track_MmUnmapIoSpace ( IN PVOID  BaseAddress,
                             IN ULONG  NumberOfBytes,
                             char * szFile,
                             ULONG uLine )
 {
     if ( FALSE == InternalInitialized ( ) )
     {
         MmUnmapIoSpace ( BaseAddress , NumberOfBytes ) ;
         return ;
     }
 
     ASSERT ( TRUE == MmIsAddressValid ( BaseAddress ) ) ;
     // Grab the spinlock so the information can removed from the list.
     KIRQL kOldIrql ;
     // A boolean that tells me if I can MmUnmapIoSpace.
     BOOLEAN bCallFunction = TRUE ;
     __try
     {
         __try
         {
             InternalAcquireSpinLock ( &kOldIrql ) ;
 
             PTRACKALLOCATION pTemp =
                     FindGeneralResourcesRecord ( eMmUnmapIoSpace    ,
                                                  (ULONG)BaseAddress ,
                                                  szFile             ,
                                                  uLine               ) ;
             if ( NULL != pTemp )
             {
                 // Is this from the matching allocator?
                 if ( eMmMapIoSpace != pTemp->sID )
                 {
                     // Got a little problem.
                     TRACE ( k_TRACK_ERROR ) ;
                     TRACE ( "The object created in %s, line %d "
                             "cannot be deleted with "
                             "MmUnmapIoSpace.\n" ,
                             pTemp->szSource ,
                             pTemp->lLine ) ;
                     TRACE ( k_TRACK_ERROR ) ;
                     ASSERTMSG ( "Invalid delete" , FALSE ) ;
                     bCallFunction = FALSE ;
                 }
                 else
                 {
                     bCallFunction = TRUE ;
                     InternalRemoveAllocation ( pTemp ) ;
                 }
             }
             else
             {
                 bCallFunction = FALSE ;
             }
         }
         __except ( EXCEPTION_EXECUTE_HANDLER )
         {
             ASSERTMSG ( "Track_MmUnmapIoSpace had an access "
                         "violation!\n"                       ,
                         FALSE                                 ) ;
         }
     }
     __finally
     {
         // Always release the spinlock and drop the IRQL so that I can
         //  call MmUnmapIoSpace.
         InternalReleaseSpinLock ( kOldIrql ) ;
         if ( TRUE == bCallFunction )
         {
             MmUnmapIoSpace ( BaseAddress , NumberOfBytes ) ;
         }
     }
 }