/******************************Module*Header***********************************\ * * **************** * * SAMPLE CODE * * **************** * * Module Name: debug.cpp * * Content: Miscellaneous Driver Debug Routines * * Copyright (c) 1994-1998 3Dlabs Inc. Ltd. All rights reserved. * Copyright (c) 1995-1999 Microsoft Corporation. All rights reserved. \*****************************************************************************/ #include "precomp.h" #include "gdi.h" #include "log.h" LONG DebugLevel = 0; // Set to '100' to debug initialization code // (the default is '0') DWORD DebugPrintFilter = 0; DWORD DebugFilter = 0; #define ALLOC_TAG ALLOC_TAG_ED2P //------------------------------------------------------------------------------ // // VOID DebugPrint // // Variable-argument level-sensitive debug print routine. // // If the specified debug level for the print statement is lower or equal // to the current debug level, the message will be printed. // // Parameters // DebugPrintLevel----Specifies at which debugging level the string should // be printed // DebugMessage-------Variable argument ascii c string // //------------------------------------------------------------------------------ VOID DebugPrint( LONG DebugPrintLevel, PCHAR DebugMessage, ... ) { va_list ap; va_start(ap, DebugMessage); if ( ((DebugPrintFilter & DebugFilter) && (DebugPrintLevel <= DebugLevel )) || DebugPrintLevel <= 0 ) { EngDebugPrint(STANDARD_DEBUG_PREFIX, DebugMessage, ap); EngDebugPrint("", "\n", ap); } va_end(ap); } // DebugPrint() #if DBG //------------------------------------------------------------------------------ // // VOID vDumpSurfobj // // Dumps using DSPDBG usefull information about the given surface // // Parameters // pso------------surface to dump // //------------------------------------------------------------------------------ void vDumpSurfobj(SURFOBJ* pso) { ULONG * bits; PPDev ppdev; if(pso != NULL) { ULONG width; ULONG height; ULONG stride; ppdev = (PPDev) pso->dhpdev; if(pso->dhsurf == NULL) { bits = (ULONG *) pso->pvScan0; width = pso->sizlBitmap.cx; height = pso->sizlBitmap.cy; stride = pso->lDelta; DISPDBG((0, "GDI managed surface %lx", pso)); } else { Surf * surf = (Surf *) pso->dhsurf; if(surf->flags & SF_SM) { bits = (ULONG *) surf->pvScan0; DISPDBG((0, "device managed SM surface %lx", pso)); } else { bits = (ULONG *) (ppdev->pjScreen + surf->ulByteOffset); DISPDBG((0, "device managed VM surface %lx", pso)); } width = surf->cx; height = surf->cy; stride = surf->lDelta; } DISPDBG((0, "width %d height %d", width, height )); DISPDBG((0, "bits 0x%lx bits[0] 0x%lx stride %ld", bits, bits[0], stride)); } } //------------------------------------------------------------------------------ // // VOID vDumpRect // // Dumps the rectangle description using DISPDBG // // Parameters // prcl-----------rectangle to dump // //------------------------------------------------------------------------------ void vDumpRect(RECTL * prcl) { if(prcl != NULL) DISPDBG((0, "left %d top %d width %d height %d", prcl->left, prcl->top, prcl->right - prcl->left, prcl->bottom - prcl->top)); } //------------------------------------------------------------------------------ // // VOID vDumpSurfobj // // Dumps the point description using DISPDBG // // Parameters // point----------point to dump // //------------------------------------------------------------------------------ void vDumpPoint(POINTL * point) { if(point != NULL) DISPDBG((0, "left %d top %d", point->x, point->y)); } //------------------------------------------------------------------------------ // // DEBUGGING INITIALIZATION CODE // // When you're bringing up your display for the first time, you can // recompile with 'DebugLevel' set to 100. That will cause absolutely // all DISPDBG messages to be displayed on the kernel debugger (this // is known as the "PrintF Approach to Debugging" and is about the only // viable method for debugging driver initialization code). // //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // // THUNK_LAYER // // By Setting THUNK_LAYER equal to 1 you are adding a wrapper call on top of // all DDI rendering functions. In this thunk layer of wrapper calls // several usefull debugging features are enabled. // // Surface checks--which can help catch errant rendering routines // Event logging---which can record rendering evernts to a log file // //------------------------------------------------------------------------------ #if THUNK_LAYER //------------------------------------------------------------------------------ // // BOOL bSurfaceChecks // // By dynamically setting bSurfaceChecks (via debugger) you can turn // surface checking on and off. Surface checking is usefull for catching // errant rendering operations overwritting other surfaces other then the // destination surface. // //------------------------------------------------------------------------------ BOOL bSurfaceChecks = 0; //------------------------------------------------------------------------------ // // ULONG ulCalcSurfaceChecksum // // Calculates a checksum for the given surface // // Parameters // psurf----Surf to be used for checksum // // Retuns checksum for given surface as a ULONG // //------------------------------------------------------------------------------ ULONG ulCalcSurfaceChecksum(Surf* psurf) { ULONG ulChecksum = 0; if( psurf->dt == DT_VM ) { // // Get the real memory address of this psurf // ULONG* ulp = (ULONG*)(psurf->ppdev->pjScreen + psurf->ulByteOffset); // // Get total bytes allocated in this psurf. Here >> 2 is to make // 4 bytes as a unit so that we can use it to do checksum // ULONG ulCount = (psurf->lDelta * psurf->cy) >> 2; // // Sum up the contents of all the bytes we allocated // while( ulCount-- ) { ulChecksum += *ulp++; } } return ulChecksum; }// vCalcSurfaceChecksum() //------------------------------------------------------------------------------ // // VOID vCalcSurfaceChecksums // // Calculates and stores all surface checksums except for the given destination // surface. // // Parameters // psoDst---destination SURFOBJ // psoSrc---source SURFOBJ // //------------------------------------------------------------------------------ VOID vCalcSurfaceChecksums(SURFOBJ * psoDst, SURFOBJ * psoSrc) { PPDev ppdev = NULL; Surf * pdSrcSurf = NULL; Surf * pdDstSurf = NULL; ASSERTDD(psoDst != NULL, "unexpected psoDst == NULL"); pdDstSurf = (Surf *) psoDst->dhsurf; if(psoSrc != NULL) pdSrcSurf = (Surf *) psoSrc->dhsurf; if(pdDstSurf != NULL) ppdev = (PPDev) psoDst->dhpdev; else if(pdSrcSurf != NULL) ppdev = (PPDev) psoSrc->dhpdev; if(ppdev != NULL) { Surf * psurf = ppdev->psurfListHead; while(psurf != ppdev->psurfListTail) { if(psurf != pdDstSurf) psurf->ulChecksum = vCalcSurfaceChecksum(psurf); psurf = psurf->psurfNext; } } } //------------------------------------------------------------------------------ // // VOID vCheckSurfaceChecksums // // Calculates and compares all surface checksums except for the given // destination surface. // // Parameters // psoDst---destination SURFOBJ // psoSrc---source SURFOBJ // //------------------------------------------------------------------------------ VOID vCheckSurfaceChecksums(SURFOBJ * psoDst, SURFOBJ * psoSrc) { PPDev ppdev = NULL; Surf * pdSrcSurf = NULL; Surf * pdDstSurf = NULL; ASSERTDD(psoDst != NULL, "unexpected psoDst == NULL"); pdDstSurf = (Surf *) psoDst->dhsurf; if(psoSrc != NULL) pdSrcSurf = (Surf *) psoSrc->dhsurf; if(pdDstSurf != NULL) ppdev = (PPDev) psoDst->dhpdev; else if(pdSrcSurf != NULL) ppdev = (PPDev) psoSrc->dhpdev; if(ppdev != NULL) { Surf * psurf = ppdev->psurfListHead; while(psurf != ppdev->psurfListTail) { if(psurf != pdDstSurf) { ASSERTDD(psurf->ulChecksum == vCalcSurfaceChecksum(psurf), "unexpected checksum mismatch"); } psurf = psurf->psurfNext; } } } //------------------------------------------------------------------------------ // ULONG ulCallDepth // // Used for keeping track of how many times the DDI layer has been entered. // Some punted calls to the GDI engine will cause callbacks into DDI. This // call depth information is used when event logging. // //------------------------------------------------------------------------------ ULONG ulCallDepth = 0; //------------------------------------------------------------------------------ // // BOOL xDrvBitBlt // // Thunk layer wrapper for DrvBitBlt. // //------------------------------------------------------------------------------ BOOL xDrvBitBlt(SURFOBJ* psoDst, SURFOBJ* psoSrc, SURFOBJ* psoMsk, CLIPOBJ* pco, XLATEOBJ* pxlo, RECTL* prclDst, POINTL* pptlSrc, POINTL* pptlMsk, BRUSHOBJ* pbo, POINTL* pptlBrush, ROP4 rop4) { BOOL bResult; LONGLONG llStartTicks; LONGLONG llElapsedTicks; ulCallDepth++; if(bSurfaceChecks) vCalcSurfaceChecksums(psoDst, psoSrc); EngQueryPerformanceCounter(&llStartTicks); bResult = DrvBitBlt(psoDst, psoSrc, psoMsk, pco, pxlo, prclDst, pptlSrc, pptlMsk,pbo, pptlBrush, rop4); EngQueryPerformanceCounter(&llElapsedTicks); llElapsedTicks -= llStartTicks; vLogBitBlt(psoDst, psoSrc, psoMsk, pco, pxlo, prclDst, pptlSrc, pptlMsk, pbo, pptlBrush, rop4, llElapsedTicks, ulCallDepth); if(bSurfaceChecks) vCheckSurfaceChecksums(psoDst, psoSrc); ulCallDepth--; return bResult; } //------------------------------------------------------------------------------ // // BOOL xDrvCopyBits // // Thunk layer wrapper for DrvCopyBits. // //------------------------------------------------------------------------------ BOOL xDrvCopyBits( SURFOBJ* psoDst, SURFOBJ* psoSrc, CLIPOBJ* pco, XLATEOBJ* pxlo, RECTL* prclDst, POINTL* pptlSrc) { BOOL bResult; LONGLONG llStartTicks; LONGLONG llElapsedTicks; ulCallDepth++; if(bSurfaceChecks) vCalcSurfaceChecksums(psoDst, psoSrc); EngQueryPerformanceCounter(&llStartTicks); bResult = DrvCopyBits(psoDst, psoSrc, pco, pxlo, prclDst, pptlSrc); EngQueryPerformanceCounter(&llElapsedTicks); llElapsedTicks -= llStartTicks; vLogCopyBits(psoDst, psoSrc, pco, pxlo, prclDst, pptlSrc, llElapsedTicks, ulCallDepth); if(bSurfaceChecks) vCheckSurfaceChecksums(psoDst, psoSrc); ulCallDepth--; return bResult; } //------------------------------------------------------------------------------ // // BOOL xDrvTransparentBlt // // Thunk layer wrapper for DrvTransparentBlt. // //------------------------------------------------------------------------------ BOOL xDrvTransparentBlt( SURFOBJ * psoDst, SURFOBJ * psoSrc, CLIPOBJ * pco, XLATEOBJ * pxlo, RECTL * prclDst, RECTL * prclSrc, ULONG iTransColor, ULONG ulReserved) { BOOL bResult; LONGLONG llStartTicks; LONGLONG llElapsedTicks; ulCallDepth++; if(bSurfaceChecks) vCalcSurfaceChecksums(psoDst, psoSrc); EngQueryPerformanceCounter(&llStartTicks); bResult = DrvTransparentBlt(psoDst, psoSrc, pco, pxlo, prclDst, prclSrc, iTransColor, ulReserved); EngQueryPerformanceCounter(&llElapsedTicks); llElapsedTicks -= llStartTicks; vLogTransparentBlt(psoDst, psoSrc, pco, pxlo, prclDst, prclSrc, iTransColor, llElapsedTicks, ulCallDepth); if(bSurfaceChecks) vCheckSurfaceChecksums(psoDst, psoSrc); ulCallDepth--; return bResult; } //------------------------------------------------------------------------------ // // BOOL xDrvAlphaBlend // // Thunk layer wrapper for DrvAlphaBlend. // //------------------------------------------------------------------------------ BOOL xDrvAlphaBlend( SURFOBJ *psoDst, SURFOBJ *psoSrc, CLIPOBJ *pco, XLATEOBJ *pxlo, RECTL *prclDst, RECTL *prclSrc, BLENDOBJ *pBlendObj) { BOOL bResult; LONGLONG llStartTicks; LONGLONG llElapsedTicks; ulCallDepth++; if(bSurfaceChecks) vCalcSurfaceChecksums(psoDst, psoSrc); EngQueryPerformanceCounter(&llStartTicks); bResult = DrvAlphaBlend( psoDst, psoSrc, pco, pxlo, prclDst, prclSrc, pBlendObj); EngQueryPerformanceCounter(&llElapsedTicks); llElapsedTicks -= llStartTicks; vLogAlphaBlend(psoDst, psoSrc, pco, pxlo, prclDst, prclSrc, pBlendObj, llElapsedTicks, ulCallDepth); if(bSurfaceChecks) vCheckSurfaceChecksums(psoDst, psoSrc); ulCallDepth--; return bResult; } //------------------------------------------------------------------------------ // // BOOL xDrvGradientFill // // Thunk layer wrapper for DrvGradientFill. // //------------------------------------------------------------------------------ BOOL xDrvGradientFill( SURFOBJ *psoDst, CLIPOBJ *pco, XLATEOBJ *pxlo, TRIVERTEX *pVertex, ULONG nVertex, PVOID pMesh, ULONG nMesh, RECTL *prclExtents, POINTL *pptlDitherOrg, ULONG ulMode ) { BOOL bResult; LONGLONG llStartTicks; LONGLONG llElapsedTicks; ulCallDepth++; if(bSurfaceChecks) vCalcSurfaceChecksums(psoDst, NULL); EngQueryPerformanceCounter(&llStartTicks); bResult = DrvGradientFill( psoDst, pco, pxlo, pVertex, nVertex, pMesh, nMesh, prclExtents, pptlDitherOrg, ulMode); EngQueryPerformanceCounter(&llElapsedTicks); llElapsedTicks -= llStartTicks; vLogGradientFill(psoDst, pco, pxlo, pVertex, nVertex, pMesh, nMesh, prclExtents, pptlDitherOrg, ulMode, llElapsedTicks, ulCallDepth); if(bSurfaceChecks) vCheckSurfaceChecksums(psoDst, NULL); ulCallDepth--; return bResult; } //------------------------------------------------------------------------------ // // BOOL xDrvTextOut // // Thunk layer wrapper for DrvTextOut. // //------------------------------------------------------------------------------ BOOL xDrvTextOut(SURFOBJ* pso, STROBJ* pstro, FONTOBJ* pfo, CLIPOBJ* pco, RECTL* prclExtra, RECTL* prclOpaque, BRUSHOBJ* pboFore, BRUSHOBJ* pboOpaque, POINTL* pptlBrush, MIX mix) { BOOL bResult; LONGLONG llStartTicks; LONGLONG llElapsedTicks; ulCallDepth++; if(bSurfaceChecks) vCalcSurfaceChecksums(pso, NULL); EngQueryPerformanceCounter(&llStartTicks); bResult = DrvTextOut(pso, pstro, pfo, pco, prclExtra, prclOpaque, pboFore, pboOpaque, pptlBrush, mix); EngQueryPerformanceCounter(&llElapsedTicks); llElapsedTicks -= llStartTicks; vLogTextOut(pso, pstro, pfo, pco, prclExtra, prclOpaque, pboFore, pboOpaque, pptlBrush, mix, llElapsedTicks, ulCallDepth); if(bSurfaceChecks) vCheckSurfaceChecksums(pso, NULL); ulCallDepth--; return bResult; } //------------------------------------------------------------------------------ // // BOOL xDrvLineTo // // Thunk layer wrapper for DrvLineTo. // //------------------------------------------------------------------------------ BOOL xDrvLineTo( SURFOBJ* pso, CLIPOBJ* pco, BRUSHOBJ* pbo, LONG x1, LONG y1, LONG x2, LONG y2, RECTL* prclBounds, MIX mix) { BOOL bResult; LONGLONG llStartTicks; LONGLONG llElapsedTicks; ulCallDepth++; if(bSurfaceChecks) vCalcSurfaceChecksums(pso, NULL); EngQueryPerformanceCounter(&llStartTicks); bResult = DrvLineTo(pso, pco, pbo, x1, y1, x2, y2, prclBounds, mix); EngQueryPerformanceCounter(&llElapsedTicks); llElapsedTicks -= llStartTicks; vLogLineTo(pso, pco, pbo, x1, y1, x2, y2, prclBounds, mix, llElapsedTicks, ulCallDepth); if(bSurfaceChecks) vCheckSurfaceChecksums(pso, NULL); ulCallDepth--; return bResult; } //------------------------------------------------------------------------------ // // BOOL xDrvFillPath // // Thunk layer wrapper for DrvFillPath. // //------------------------------------------------------------------------------ BOOL xDrvFillPath( SURFOBJ* pso, PATHOBJ* ppo, CLIPOBJ* pco, BRUSHOBJ* pbo, POINTL* pptlBrush, MIX mix, FLONG flOptions) { BOOL bResult; LONGLONG llStartTicks; LONGLONG llElapsedTicks; ulCallDepth++; if(bSurfaceChecks) vCalcSurfaceChecksums(pso, NULL); EngQueryPerformanceCounter(&llStartTicks); bResult = DrvFillPath(pso, ppo, pco, pbo, pptlBrush, mix, flOptions); EngQueryPerformanceCounter(&llElapsedTicks); llElapsedTicks -= llStartTicks; vLogFillPath(pso, ppo, pco, pbo, pptlBrush, mix, flOptions, llElapsedTicks, ulCallDepth); if(bSurfaceChecks) vCheckSurfaceChecksums(pso, NULL); ulCallDepth--; return bResult; } //------------------------------------------------------------------------------ // // BOOL xDrvStrokePath // // Thunk layer wrapper for DrvStrokePath. // //------------------------------------------------------------------------------ BOOL xDrvStrokePath( SURFOBJ* pso, PATHOBJ* ppo, CLIPOBJ* pco, XFORMOBJ* pxo, BRUSHOBJ* pbo, POINTL* pptlBrush, LINEATTRS* pla, MIX mix) { BOOL bResult; LONGLONG llStartTicks; LONGLONG llElapsedTicks; ulCallDepth++; if(bSurfaceChecks) vCalcSurfaceChecksums(pso, NULL); EngQueryPerformanceCounter(&llStartTicks); bResult = DrvStrokePath(pso, ppo, pco, pxo, pbo, pptlBrush, pla, mix); EngQueryPerformanceCounter(&llElapsedTicks); llElapsedTicks -= llStartTicks; vLogStrokePath(pso, ppo, pco, pxo, pbo, pptlBrush, pla, mix, llElapsedTicks, ulCallDepth); if(bSurfaceChecks) vCheckSurfaceChecksums(pso, NULL); ulCallDepth--; return bResult; } #endif // THUNK LAYER //----------------------------------------------------------------------------- // //..Add some functions to aid tracking down memory leaks. // Its sole purpose is tracking down leaks, so its not optimized for speed. // WARNING: If two instances of same driver are active at the same time, // it will track the memory allocations of both. // // To keep it simple, we just allocate an array here where we store memory allocations. // There is some simple algorithm to keep track of recently freed entries. Anyway, // to free a piece of memory we have to search through the whole table. So better // use for debugging memory holes. // //----------------------------------------------------------------------------- #if DBG && TRACKMEMALLOC typedef struct tagMemTrackInfo { PVOID pMemory; LONG lSize; PCHAR pModule; LONG lLineNo; //LONGINT save time of allocation? BOOL bStopWhenFreed; BOOL bTemp; } MemTrackInfo, *PMemTrackInfo; #define NEWCHUNKSIZE 256 static PMemTrackInfo pTrackPool=NULL; static LONG lTrackPoolTotalSize=0; static LONG lTrackPoolSize=0; static LONG lInstances=0; static LONG lTotalAllocatedMemory=0; static LONG lNextFreeEntry=0; // glMemTrackerVerboseMode--- set flags according to debug output // 0 no output // 1 print summary for all allocations in same module/LineNo // 2 print all entries LONG glMemTrackerVerboseMode=1; //----------------------------------------------------------------------------- // // MemTrackerAddInstance // // Just count number of active instances of the driver // //----------------------------------------------------------------------------- VOID MemTrackerAddInstance() { lInstances++; } //----------------------------------------------------------------------------- // // MemTrackerRemInstance // // Just count number of active instances of the driver. Free Tracker memory // if last instance is destroyed!!! // //----------------------------------------------------------------------------- VOID MemTrackerRemInstance() { lInstances--; if (lInstances==0) { EngFreeMem(pTrackPool); pTrackPool=NULL; lTrackPoolTotalSize=0; lTrackPoolSize=0; lTotalAllocatedMemory=0; lNextFreeEntry=0; } } //----------------------------------------------------------------------------- // // MemTrackerAllocateMem // // Add memory top be tracked to table. // // p--------address of memory chunk // lSize----Size of memory chunk // pModulo--module name // lLineNo--module line number // bStopWhenFreed--set a breakpoint if this memory is freed (not yet used) // //----------------------------------------------------------------------------- PVOID MemTrackerAllocateMem(PVOID p, LONG lSize, PCHAR pModule, LONG lLineNo, BOOL bStopWhenFreed) { // check for first time allocation if (p==NULL) return p; if (pTrackPool==NULL) { pTrackPool=(PMemTrackInfo)EngAllocMem( FL_ZERO_MEMORY, NEWCHUNKSIZE*sizeof(MemTrackInfo), ALLOC_TAG); if (pTrackPool==NULL) return p; lTrackPoolTotalSize=NEWCHUNKSIZE; lTrackPoolSize=2; lTotalAllocatedMemory=0; lNextFreeEntry=1; pTrackPool[0].pMemory= pTrackPool; pTrackPool[0].lSize= NEWCHUNKSIZE*sizeof(MemTrackInfo); pTrackPool[0].pModule= __FILE__; pTrackPool[0].lLineNo= __LINE__; pTrackPool[0].bStopWhenFreed=FALSE; } if (lTrackPoolSize>=lTrackPoolTotalSize) { // reallocation necessary LONG lNewTrackPoolTotalSize=lTrackPoolTotalSize+NEWCHUNKSIZE; LONG lNewSize; PMemTrackInfo pNewTrackPool=(PMemTrackInfo) EngAllocMem( FL_ZERO_MEMORY, lNewSize=lNewTrackPoolTotalSize*sizeof(MemTrackInfo), ALLOC_TAG); if (pNewTrackPool==NULL) return p; memcpy( pNewTrackPool, pTrackPool, lTrackPoolTotalSize*sizeof(MemTrackInfo)); EngFreeMem( pTrackPool); pTrackPool=pNewTrackPool; lTrackPoolTotalSize=lNewTrackPoolTotalSize; pTrackPool[0].pMemory= pTrackPool; pTrackPool[0].lSize= lNewSize; pTrackPool[0].pModule= __FILE__; pTrackPool[0].lLineNo= __LINE__; pTrackPool[0].bStopWhenFreed=FALSE; } LONG lThisEntry=lNextFreeEntry; lNextFreeEntry=pTrackPool[lThisEntry].lSize; pTrackPool[lThisEntry].pMemory= p; pTrackPool[lThisEntry].lSize= lSize; pTrackPool[lThisEntry].pModule= pModule; pTrackPool[lThisEntry].lLineNo= lLineNo; pTrackPool[lThisEntry].bStopWhenFreed=FALSE; if (lNextFreeEntry==0) { lNextFreeEntry=lTrackPoolSize; lTrackPoolSize++; } lTotalAllocatedMemory += lSize; return p; } //----------------------------------------------------------------------------- // // MemTrackerFreeMem // // remove a memory chunk from table, because it is freed // // p-----address of memory to be removed from table // //----------------------------------------------------------------------------- VOID MemTrackerFreeMem( VOID *p) { for (INT i=1; i