mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2561 lines
55 KiB
2561 lines
55 KiB
/*++
|
|
|
|
Copyright (c) 1998-2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
debug.c
|
|
|
|
Abstract:
|
|
|
|
This module contains debug support routines.
|
|
|
|
Author:
|
|
|
|
Keith Moore (keithmo) 10-Jun-1998
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include "precomp.h"
|
|
#include "debugp.h"
|
|
|
|
|
|
#if DBG
|
|
|
|
|
|
#undef ExAllocatePool
|
|
#undef ExFreePool
|
|
|
|
|
|
//
|
|
// Private constants.
|
|
//
|
|
|
|
#define NUM_THREAD_HASH_BUCKETS 31
|
|
#define RANDOM_CONSTANT 314159269UL
|
|
#define RANDOM_PRIME 1000000007UL
|
|
|
|
#define HASH_SCRAMBLE(hash) \
|
|
(ULONG_PTR)((((ULONG_PTR)(hash)) * (ULONG_PTR)RANDOM_CONSTANT) \
|
|
% (ULONG_PTR)RANDOM_PRIME)
|
|
|
|
#define HASH_FROM_THREAD(thrd) \
|
|
((ULONG)(HASH_SCRAMBLE(thrd) % (ULONG_PTR)NUM_THREAD_HASH_BUCKETS))
|
|
|
|
#define SET_RESOURCE_OWNED_EXCLUSIVE( pLock ) \
|
|
(pLock)->pExclusiveOwner = PsGetCurrentThread()
|
|
|
|
#define SET_RESOURCE_NOT_OWNED_EXCLUSIVE( pLock ) \
|
|
(pLock)->pPreviousOwner = (pLock)->pExclusiveOwner; \
|
|
(pLock)->pExclusiveOwner = NULL
|
|
|
|
#define SET_SPIN_LOCK_OWNED( pLock ) \
|
|
do { \
|
|
(pLock)->pOwnerThread = PsGetCurrentThread(); \
|
|
(pLock)->OwnerProcessor = (ULONG)KeGetCurrentProcessorNumber(); \
|
|
} while (FALSE)
|
|
|
|
#define SET_SPIN_LOCK_NOT_OWNED( pLock ) \
|
|
do { \
|
|
(pLock)->pOwnerThread = NULL; \
|
|
(pLock)->OwnerProcessor = (ULONG)-1L; \
|
|
} while (FALSE)
|
|
|
|
|
|
//
|
|
// Private types.
|
|
//
|
|
|
|
typedef struct _UL_POOL_HEADER {
|
|
PSTR pFileName;
|
|
USHORT LineNumber;
|
|
SIZE_T Size;
|
|
ULONG Tag;
|
|
} UL_POOL_HEADER, *PUL_POOL_HEADER;
|
|
|
|
typedef struct _UL_THREAD_HASH_BUCKET
|
|
{
|
|
UL_SPIN_LOCK BucketSpinLock;
|
|
LIST_ENTRY BucketListHead;
|
|
|
|
} UL_THREAD_HASH_BUCKET, *PUL_THREAD_HASH_BUCKET;
|
|
|
|
|
|
//
|
|
// Private prototypes.
|
|
//
|
|
|
|
VOID
|
|
UlpDbgUpdatePoolCounter(
|
|
IN OUT PLARGE_INTEGER pAddend,
|
|
IN SIZE_T Increment
|
|
);
|
|
|
|
PSTR
|
|
UlpDbgFindFilePart(
|
|
IN PSTR pPath
|
|
);
|
|
|
|
PUL_DEBUG_THREAD_DATA
|
|
UlpDbgFindThread(
|
|
BOOLEAN OkToCreate,
|
|
PSTR pFileName,
|
|
USHORT LineNumber
|
|
);
|
|
|
|
VOID
|
|
UlpDbgDereferenceThread(
|
|
IN PUL_DEBUG_THREAD_DATA pData
|
|
REFERENCE_DEBUG_FORMAL_PARAMS
|
|
);
|
|
|
|
//
|
|
// Private macros.
|
|
//
|
|
#define ULP_DBG_FIND_THREAD() \
|
|
UlpDbgFindThread(FALSE, (PSTR)__FILE__, (USHORT)__LINE__)
|
|
|
|
#define ULP_DBG_FIND_OR_CREATE_THREAD() \
|
|
UlpDbgFindThread(TRUE, (PSTR)__FILE__, (USHORT)__LINE__)
|
|
|
|
#define ULP_DBG_DEREFERENCE_THREAD(pData) \
|
|
UlpDbgDereferenceThread((pData) REFERENCE_DEBUG_ACTUAL_PARAMS)
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#if DBG
|
|
#pragma alloc_text( INIT, UlDbgInitializeDebugData )
|
|
#pragma alloc_text( PAGE, UlDbgTerminateDebugData )
|
|
#pragma alloc_text( PAGE, UlDbgAcquireResourceExclusive )
|
|
#pragma alloc_text( PAGE, UlDbgAcquireResourceShared )
|
|
#pragma alloc_text( PAGE, UlDbgReleaseResource )
|
|
#pragma alloc_text( PAGE, UlDbgConvertExclusiveToShared)
|
|
// #pragma alloc_text( PAGE, UlDbgTryToAcquireResourceExclusive)
|
|
#pragma alloc_text( PAGE, UlDbgResourceOwnedExclusive )
|
|
#pragma alloc_text( PAGE, UlDbgResourceUnownedExclusive )
|
|
#endif // DBG
|
|
#if 0
|
|
NOT PAGEABLE -- UlDbgAllocatePool
|
|
NOT PAGEABLE -- UlDbgFreePool
|
|
NOT PAGEABLE -- UlDbgInitializeSpinLock
|
|
NOT PAGEABLE -- UlDbgAcquireSpinLock
|
|
NOT PAGEABLE -- UlDbgReleaseSpinLock
|
|
NOT PAGEABLE -- UlDbgAcquireSpinLockAtDpcLevel
|
|
NOT PAGEABLE -- UlDbgReleaseSpinLockFromDpcLevel
|
|
NOT PAGEABLE -- UlDbgSpinLockOwned
|
|
NOT PAGEABLE -- UlDbgSpinLockUnowned
|
|
NOT PAGEABLE -- UlDbgExceptionFilter
|
|
NOT PAGEABLE -- UlDbgInvalidCompletionRoutine
|
|
NOT PAGEABLE -- UlDbgStatus
|
|
NOT PAGEABLE -- UlDbgEnterDriver
|
|
NOT PAGEABLE -- UlDbgLeaveDriver
|
|
NOT PAGEABLE -- UlDbgInitializeResource
|
|
NOT PAGEABLE -- UlDbgDeleteResource
|
|
NOT PAGEABLE -- UlDbgAllocateIrp
|
|
NOT PAGEABLE -- UlDbgFreeIrp
|
|
NOT PAGEABLE -- UlDbgCallDriver
|
|
NOT PAGEABLE -- UlDbgCompleteRequest
|
|
NOT PAGEABLE -- UlDbgAllocateMdl
|
|
NOT PAGEABLE -- UlDbgFreeMdl
|
|
NOT PAGEABLE -- UlpDbgUpdatePoolCounter
|
|
NOT PAGEABLE -- UlpDbgFindFilePart
|
|
NOT PAGEABLE -- UlpDbgFindThread
|
|
NOT PAGEABLE -- UlpDbgDereferenceThread
|
|
#endif
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
|
|
//
|
|
// Private globals.
|
|
//
|
|
|
|
UL_THREAD_HASH_BUCKET g_DbgThreadHashBuckets[NUM_THREAD_HASH_BUCKETS];
|
|
LONG g_DbgThreadCreated;
|
|
LONG g_DbgThreadDestroyed;
|
|
UL_SPIN_LOCK g_DbgSpinLock;
|
|
LIST_ENTRY g_DbgGlobalResourceListHead;
|
|
|
|
|
|
//
|
|
// Public functions.
|
|
//
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Initializes global debug-specific data.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlDbgInitializeDebugData(
|
|
VOID
|
|
)
|
|
{
|
|
ULONG i;
|
|
CHAR spinLockName[sizeof("g_DbgThreadHashBuckets[00000].BucketSpinLock")];
|
|
|
|
//
|
|
// Initialize the lock lists.
|
|
//
|
|
|
|
UlInitializeSpinLock( &g_DbgSpinLock, "g_DbgSpinLock" );
|
|
InitializeListHead( &g_DbgGlobalResourceListHead );
|
|
|
|
//
|
|
// Initialize the thread hash buckets.
|
|
//
|
|
|
|
for (i = 0 ; i < NUM_THREAD_HASH_BUCKETS ; i++)
|
|
{
|
|
sprintf(
|
|
spinLockName,
|
|
"g_DbgThreadHashBuckets[%lu].BucketSpinLock",
|
|
i
|
|
);
|
|
|
|
UlInitializeSpinLock(
|
|
&g_DbgThreadHashBuckets[i].BucketSpinLock,
|
|
spinLockName
|
|
);
|
|
|
|
InitializeListHead(
|
|
&g_DbgThreadHashBuckets[i].BucketListHead
|
|
);
|
|
}
|
|
|
|
} // UlDbgInitializeDebugData
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Undoes any initialization performed in UlDbgInitializeDebugData().
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlDbgTerminateDebugData(
|
|
VOID
|
|
)
|
|
{
|
|
ULONG i;
|
|
|
|
//
|
|
// Ensure the thread hash buckets are empty.
|
|
//
|
|
|
|
for (i = 0 ; i < NUM_THREAD_HASH_BUCKETS ; i++)
|
|
{
|
|
ASSERT( IsListEmpty( &g_DbgThreadHashBuckets[i].BucketListHead ) );
|
|
}
|
|
|
|
//
|
|
// Ensure the lock lists are empty.
|
|
//
|
|
|
|
ASSERT( IsListEmpty( &g_DbgGlobalResourceListHead ) );
|
|
|
|
} // UlDbgTerminateDebugData
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Prettyprints a buffer to DbgPrint output. More or less turns it back
|
|
into a C-style string.
|
|
|
|
CODEWORK: produce a Unicode version of this helper function
|
|
|
|
Arguments:
|
|
|
|
Buffer - Buffer to prettyprint
|
|
|
|
BufferSize - number of bytes to prettyprint
|
|
|
|
Return Value:
|
|
|
|
ULONG - number of prettyprinted characters sent to DbgPrint,
|
|
excluding inserted newlines.
|
|
|
|
--***************************************************************************/
|
|
ULONG
|
|
UlDbgPrettyPrintBuffer(
|
|
const UCHAR* pBuffer,
|
|
ULONG BufferSize
|
|
)
|
|
{
|
|
int i;
|
|
CHAR OutputBuffer[200];
|
|
PCHAR pOut = OutputBuffer;
|
|
BOOLEAN CrLfNeeded = FALSE, JustCrLfd = FALSE;
|
|
ULONG cch = 0;
|
|
|
|
if (pBuffer == NULL || BufferSize == 0)
|
|
return 0;
|
|
|
|
for (i = 0; i < (int)BufferSize; ++i)
|
|
{
|
|
UCHAR ch = pBuffer[i];
|
|
|
|
if ('\r' == ch) // CR
|
|
{
|
|
*pOut++ = '\\'; *pOut++ = 'r';
|
|
if (i + 1 == BufferSize || pBuffer[i + 1] != '\n')
|
|
CrLfNeeded = TRUE;
|
|
}
|
|
else if ('\n' == ch) // LF
|
|
{
|
|
*pOut++ = '\\'; *pOut++ = 'n';
|
|
CrLfNeeded = TRUE;
|
|
}
|
|
else if ('\t' == ch) // TAB
|
|
{
|
|
*pOut++ = '\\'; *pOut++ = 't';
|
|
}
|
|
else if ('\0' == ch) // NUL
|
|
{
|
|
*pOut++ = '\\'; *pOut++ = '0';
|
|
}
|
|
else if ('\\' == ch) // \ (backslash)
|
|
{
|
|
*pOut++ = '\\'; *pOut++ = '\\';
|
|
}
|
|
else if (ch < 0x20 || ch == 127) // control chars
|
|
{
|
|
const UCHAR HexString[] = "0123456789abcdef";
|
|
|
|
*pOut++ = '\\'; *pOut++ = 'x';
|
|
*pOut++ = HexString[ch >> 4]; *pOut++ = HexString[ch & 0xf];
|
|
}
|
|
else
|
|
{
|
|
*pOut++ = ch;
|
|
}
|
|
|
|
if (pOut - OutputBuffer >= sizeof(OutputBuffer) - 4) // strlen("\xAB")
|
|
CrLfNeeded = TRUE;
|
|
|
|
if (CrLfNeeded)
|
|
{
|
|
*pOut++ = '\n'; *pOut = '\0';
|
|
DbgPrint(OutputBuffer);
|
|
cch += (ULONG) (pOut - 1 - OutputBuffer);
|
|
pOut = OutputBuffer;
|
|
CrLfNeeded = FALSE;
|
|
JustCrLfd = TRUE;
|
|
}
|
|
else
|
|
{
|
|
JustCrLfd = FALSE;
|
|
}
|
|
}
|
|
|
|
if (! JustCrLfd)
|
|
{
|
|
*pOut++ = '\n'; *pOut = '\0';
|
|
DbgPrint(OutputBuffer);
|
|
cch += (ULONG) (pOut - 1 - OutputBuffer);
|
|
}
|
|
|
|
return cch;
|
|
} // UlDbgPrettyPrintBuffer
|
|
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Debug memory allocator. Allocates a block of pool with a header
|
|
containing the filename & line number of the caller, plus the
|
|
tag for the data.
|
|
|
|
Arguments:
|
|
|
|
PoolType - Supplies the pool to allocate from. Must be either
|
|
NonPagedPool, NonPagedPoolMustSucceed, or PagedPool.
|
|
|
|
NumberOfBytes - Supplies the number of bytes to allocate.
|
|
|
|
Tag - Supplies a four-byte tag for the pool block. Useful for
|
|
debugging leaks.
|
|
|
|
pFileName - Supplies the filename of the caller.
|
|
function.
|
|
|
|
LineNumber - Supplies the line number of the caller.
|
|
|
|
Return Value:
|
|
|
|
PVOID - Pointer to the allocated block if successful, NULL otherwise.
|
|
|
|
--***************************************************************************/
|
|
PVOID
|
|
UlDbgAllocatePool(
|
|
IN POOL_TYPE PoolType,
|
|
IN SIZE_T NumberOfBytes,
|
|
IN ULONG Tag,
|
|
IN PSTR pFileName,
|
|
IN USHORT LineNumber
|
|
)
|
|
{
|
|
PUL_POOL_HEADER pHeader;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( PoolType == NonPagedPool ||
|
|
PoolType == NonPagedPoolMustSucceed ||
|
|
PoolType == NonPagedPoolCacheAligned ||
|
|
PoolType == PagedPool );
|
|
|
|
ASSERT( IS_VALID_TAG( Tag ) );
|
|
|
|
//
|
|
// Allocate the block with additional space for the header.
|
|
//
|
|
|
|
pHeader = (PUL_POOL_HEADER)(
|
|
ExAllocatePoolWithTag(
|
|
PoolType,
|
|
NumberOfBytes + sizeof(*pHeader),
|
|
Tag
|
|
)
|
|
);
|
|
|
|
if (pHeader == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Initialize the header.
|
|
//
|
|
|
|
pHeader->pFileName = pFileName;
|
|
pHeader->LineNumber = LineNumber;
|
|
pHeader->Size = NumberOfBytes;
|
|
pHeader->Tag = Tag;
|
|
|
|
//
|
|
// Fill the body with garbage.
|
|
//
|
|
|
|
RtlFillMemory( (PVOID)(pHeader + 1), NumberOfBytes, '\xcc' );
|
|
|
|
//
|
|
// Update the statistics.
|
|
//
|
|
|
|
InterlockedIncrement(
|
|
&g_UlDebugStats.TotalAllocations
|
|
);
|
|
|
|
UlpDbgUpdatePoolCounter(
|
|
&g_UlDebugStats.TotalBytesAllocated,
|
|
NumberOfBytes
|
|
);
|
|
|
|
//
|
|
// Return a pointer to the body.
|
|
//
|
|
|
|
return (PVOID)(pHeader + 1);
|
|
|
|
} // UlDbgAllocatePool
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Frees memory allocated by UlDbgAllocatePool(), ensuring that the tags
|
|
match.
|
|
|
|
Arguments:
|
|
|
|
pPointer - Supplies a pointer to the pool block to free.
|
|
|
|
Tag - Supplies the tag for the block to be freed. If the supplied
|
|
tag does not match the tag of the allocated block, an assertion
|
|
failure is generated.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlDbgFreePool(
|
|
IN PVOID pPointer,
|
|
IN ULONG Tag
|
|
)
|
|
{
|
|
PUL_POOL_HEADER pHeader;
|
|
|
|
//
|
|
// Get a pointer to the header.
|
|
//
|
|
|
|
pHeader = (PUL_POOL_HEADER)pPointer - 1;
|
|
|
|
//
|
|
// Update the statistics.
|
|
//
|
|
|
|
InterlockedIncrement(
|
|
&g_UlDebugStats.TotalFrees
|
|
);
|
|
|
|
UlpDbgUpdatePoolCounter(
|
|
&g_UlDebugStats.TotalBytesFreed,
|
|
pHeader->Size
|
|
);
|
|
|
|
//
|
|
// Validate the tag.
|
|
//
|
|
|
|
if( pHeader->Tag == Tag )
|
|
{
|
|
ASSERT( IS_VALID_TAG( Tag ) );
|
|
pHeader->Tag = MAKE_FREE_TAG( Tag );
|
|
}
|
|
else
|
|
{
|
|
ASSERT( !"Invalid tag" );
|
|
}
|
|
|
|
//
|
|
// Actually free the block.
|
|
//
|
|
|
|
MyFreePoolWithTag(
|
|
(PVOID)pHeader,
|
|
Tag
|
|
);
|
|
|
|
} // UlDbgFreePool
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Initializes an instrumented spinlock.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlDbgInitializeSpinLock(
|
|
IN PUL_SPIN_LOCK pSpinLock,
|
|
IN PSTR pSpinLockName,
|
|
IN PSTR pFileName,
|
|
IN USHORT LineNumber
|
|
)
|
|
{
|
|
//
|
|
// Initialize the spinlock.
|
|
//
|
|
|
|
RtlZeroMemory( pSpinLock, sizeof(*pSpinLock) );
|
|
pSpinLock->pSpinLockName = pSpinLockName;
|
|
KeInitializeSpinLock( &pSpinLock->KSpinLock );
|
|
SET_SPIN_LOCK_NOT_OWNED( pSpinLock );
|
|
|
|
//
|
|
// Update the global statistics.
|
|
//
|
|
|
|
InterlockedIncrement( &g_UlDebugStats.TotalSpinLocksInitialized );
|
|
|
|
} // UlDbgInitializeSpinLock
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Acquires an instrumented spinlock.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlDbgAcquireSpinLock(
|
|
IN PUL_SPIN_LOCK pSpinLock,
|
|
OUT PKIRQL pOldIrql,
|
|
IN PSTR pFileName,
|
|
IN USHORT LineNumber
|
|
)
|
|
{
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( !UlDbgSpinLockOwned( pSpinLock ) );
|
|
|
|
//
|
|
// Acquire the lock.
|
|
//
|
|
|
|
KeAcquireSpinLock(
|
|
&pSpinLock->KSpinLock,
|
|
pOldIrql
|
|
);
|
|
|
|
//
|
|
// Mark it as owned by the current thread.
|
|
//
|
|
|
|
ASSERT( UlDbgSpinLockUnowned( pSpinLock ) );
|
|
SET_SPIN_LOCK_OWNED( pSpinLock );
|
|
|
|
//
|
|
// Update the statistics.
|
|
//
|
|
|
|
pSpinLock->Acquisitions++;
|
|
pSpinLock->pLastAcquireFileName = pFileName;
|
|
pSpinLock->LastAcquireLineNumber = LineNumber;
|
|
|
|
InterlockedIncrement(
|
|
&g_UlDebugStats.TotalAcquisitions
|
|
);
|
|
|
|
} // UlDbgAcquireSpinLock
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Releases an instrumented spinlock.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlDbgReleaseSpinLock(
|
|
IN PUL_SPIN_LOCK pSpinLock,
|
|
IN KIRQL OldIrql,
|
|
IN PSTR pFileName,
|
|
IN USHORT LineNumber
|
|
)
|
|
{
|
|
//
|
|
// Mark it as unowned.
|
|
//
|
|
|
|
ASSERT( UlDbgSpinLockOwned( pSpinLock ) );
|
|
SET_SPIN_LOCK_NOT_OWNED( pSpinLock );
|
|
|
|
//
|
|
// Update the statistics.
|
|
//
|
|
|
|
InterlockedIncrement(
|
|
&g_UlDebugStats.TotalReleases
|
|
);
|
|
|
|
pSpinLock->Releases++;
|
|
pSpinLock->pLastReleaseFileName = pFileName;
|
|
pSpinLock->LastReleaseLineNumber = LineNumber;
|
|
|
|
//
|
|
// Release the lock.
|
|
//
|
|
|
|
KeReleaseSpinLock(
|
|
&pSpinLock->KSpinLock,
|
|
OldIrql
|
|
);
|
|
|
|
} // UlDbgReleaseSpinLock
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Acquires an instrumented spinlock while running at DPC level.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlDbgAcquireSpinLockAtDpcLevel(
|
|
IN PUL_SPIN_LOCK pSpinLock,
|
|
IN PSTR pFileName,
|
|
IN USHORT LineNumber
|
|
)
|
|
{
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( !UlDbgSpinLockOwned( pSpinLock ) );
|
|
|
|
//
|
|
// Acquire the lock.
|
|
//
|
|
|
|
KeAcquireSpinLockAtDpcLevel(
|
|
&pSpinLock->KSpinLock
|
|
);
|
|
|
|
//
|
|
// Mark it as owned by the current thread.
|
|
//
|
|
|
|
ASSERT( !UlDbgSpinLockOwned( pSpinLock ) );
|
|
SET_SPIN_LOCK_OWNED( pSpinLock );
|
|
|
|
//
|
|
// Update the statistics.
|
|
//
|
|
|
|
pSpinLock->AcquisitionsAtDpcLevel++;
|
|
pSpinLock->pLastAcquireFileName = pFileName;
|
|
pSpinLock->LastAcquireLineNumber = LineNumber;
|
|
|
|
InterlockedIncrement(
|
|
&g_UlDebugStats.TotalAcquisitionsAtDpcLevel
|
|
);
|
|
|
|
} // UlDbgAcquireSpinLockAtDpcLevel
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Releases an instrumented spinlock acquired at DPC level.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlDbgReleaseSpinLockFromDpcLevel(
|
|
IN PUL_SPIN_LOCK pSpinLock,
|
|
IN PSTR pFileName,
|
|
IN USHORT LineNumber
|
|
)
|
|
{
|
|
//
|
|
// Mark it as unowned.
|
|
//
|
|
|
|
ASSERT( UlDbgSpinLockOwned( pSpinLock ) );
|
|
SET_SPIN_LOCK_NOT_OWNED( pSpinLock );
|
|
|
|
//
|
|
// Update the statistics.
|
|
//
|
|
|
|
InterlockedIncrement(
|
|
&g_UlDebugStats.TotalReleasesFromDpcLevel
|
|
);
|
|
|
|
pSpinLock->ReleasesFromDpcLevel++;
|
|
pSpinLock->pLastReleaseFileName = pFileName;
|
|
pSpinLock->LastReleaseLineNumber = LineNumber;
|
|
|
|
//
|
|
// Release the lock.
|
|
//
|
|
|
|
KeReleaseSpinLockFromDpcLevel(
|
|
&pSpinLock->KSpinLock
|
|
);
|
|
|
|
} // UlDbgReleaseSpinLockAtDpcLevel
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Acquires an instrumented in-stack-queue spinlock.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlDbgAcquireInStackQueuedSpinLock(
|
|
IN PUL_SPIN_LOCK pSpinLock,
|
|
OUT PKLOCK_QUEUE_HANDLE pLockHandle,
|
|
IN PSTR pFileName,
|
|
IN USHORT LineNumber
|
|
)
|
|
{
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( !UlDbgSpinLockOwned( pSpinLock ) );
|
|
|
|
//
|
|
// Acquire the lock.
|
|
//
|
|
|
|
KeAcquireInStackQueuedSpinLock(
|
|
&pSpinLock->KSpinLock,
|
|
pLockHandle
|
|
);
|
|
|
|
//
|
|
// Mark it as owned by the current thread.
|
|
//
|
|
|
|
ASSERT( UlDbgSpinLockUnowned( pSpinLock ) );
|
|
SET_SPIN_LOCK_OWNED( pSpinLock );
|
|
|
|
//
|
|
// Update the statistics.
|
|
//
|
|
|
|
pSpinLock->Acquisitions++;
|
|
pSpinLock->pLastAcquireFileName = pFileName;
|
|
pSpinLock->LastAcquireLineNumber = LineNumber;
|
|
|
|
InterlockedIncrement(
|
|
&g_UlDebugStats.TotalAcquisitions
|
|
);
|
|
|
|
} // UlDbgAcquireInStackQueuedSpinLock
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Releases an instrumented in-stack-queue spinlock.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlDbgReleaseInStackQueuedSpinLock(
|
|
IN PUL_SPIN_LOCK pSpinLock,
|
|
IN PKLOCK_QUEUE_HANDLE pLockHandle,
|
|
IN PSTR pFileName,
|
|
IN USHORT LineNumber
|
|
)
|
|
{
|
|
//
|
|
// Mark it as unowned.
|
|
//
|
|
|
|
ASSERT( UlDbgSpinLockOwned( pSpinLock ) );
|
|
SET_SPIN_LOCK_NOT_OWNED( pSpinLock );
|
|
|
|
//
|
|
// Update the statistics.
|
|
//
|
|
|
|
InterlockedIncrement(
|
|
&g_UlDebugStats.TotalReleases
|
|
);
|
|
|
|
pSpinLock->Releases++;
|
|
pSpinLock->pLastReleaseFileName = pFileName;
|
|
pSpinLock->LastReleaseLineNumber = LineNumber;
|
|
|
|
//
|
|
// Release the lock.
|
|
//
|
|
|
|
KeReleaseInStackQueuedSpinLock(
|
|
pLockHandle
|
|
);
|
|
|
|
} // UlDbgReleaseInStackQueuedSpinLock
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Acquires an instrumented in-stack-queue spinlock while running at DPC level.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlDbgAcquireSpinLockAtDpcLevel(
|
|
IN PUL_SPIN_LOCK pSpinLock,
|
|
OUT PKLOCK_QUEUE_HANDLE pLockHandle,
|
|
IN PSTR pFileName,
|
|
IN USHORT LineNumber
|
|
)
|
|
{
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
ASSERT( !UlDbgSpinLockOwned( pSpinLock ) );
|
|
|
|
//
|
|
// Acquire the lock.
|
|
//
|
|
|
|
KeAcquireInStackQueuedSpinLockAtDpcLevel(
|
|
&pSpinLock->KSpinLock,
|
|
pLockHandle
|
|
);
|
|
|
|
//
|
|
// Mark it as owned by the current thread.
|
|
//
|
|
|
|
ASSERT( !UlDbgSpinLockOwned( pSpinLock ) );
|
|
SET_SPIN_LOCK_OWNED( pSpinLock );
|
|
|
|
//
|
|
// Update the statistics.
|
|
//
|
|
|
|
pSpinLock->AcquisitionsAtDpcLevel++;
|
|
pSpinLock->pLastAcquireFileName = pFileName;
|
|
pSpinLock->LastAcquireLineNumber = LineNumber;
|
|
|
|
InterlockedIncrement(
|
|
&g_UlDebugStats.TotalAcquisitionsAtDpcLevel
|
|
);
|
|
|
|
} // UlDbgAcquireInStackQueuedSpinLockAtDpcLevel
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Releases an instrumented in-stack-queue spinlock acquired at DPC level.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlDbgReleaseSpinLockFromDpcLevel(
|
|
IN PUL_SPIN_LOCK pSpinLock,
|
|
IN PKLOCK_QUEUE_HANDLE pLockHandle,
|
|
IN PSTR pFileName,
|
|
IN USHORT LineNumber
|
|
)
|
|
{
|
|
//
|
|
// Mark it as unowned.
|
|
//
|
|
|
|
ASSERT( UlDbgSpinLockOwned( pSpinLock ) );
|
|
SET_SPIN_LOCK_NOT_OWNED( pSpinLock );
|
|
|
|
//
|
|
// Update the statistics.
|
|
//
|
|
|
|
InterlockedIncrement(
|
|
&g_UlDebugStats.TotalReleasesFromDpcLevel
|
|
);
|
|
|
|
pSpinLock->ReleasesFromDpcLevel++;
|
|
pSpinLock->pLastReleaseFileName = pFileName;
|
|
pSpinLock->LastReleaseLineNumber = LineNumber;
|
|
|
|
//
|
|
// Release the lock.
|
|
//
|
|
|
|
KeReleaseInStackQueuedSpinLockFromDpcLevel(
|
|
pLockHandle
|
|
);
|
|
|
|
} // UlDbgReleaseInStackQueuedSpinLockFromDpcLevel
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Determines if the specified spinlock is owned by the current thread.
|
|
|
|
Arguments:
|
|
|
|
pSpinLock - Supplies the spinlock to test.
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if the spinlock is owned by the current thread, FALSE
|
|
otherwise.
|
|
|
|
--***************************************************************************/
|
|
BOOLEAN
|
|
UlDbgSpinLockOwned(
|
|
IN PUL_SPIN_LOCK pSpinLock
|
|
)
|
|
{
|
|
if (pSpinLock->pOwnerThread == PsGetCurrentThread())
|
|
{
|
|
ASSERT( pSpinLock->OwnerProcessor == (ULONG)KeGetCurrentProcessorNumber() );
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
} // UlDbgSpinLockOwned
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Determines if the specified spinlock is unowned.
|
|
|
|
Arguments:
|
|
|
|
pSpinLock - Supplies the spinlock to test.
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if the spinlock is unowned, FALSE otherwise.
|
|
|
|
--***************************************************************************/
|
|
BOOLEAN
|
|
UlDbgSpinLockUnowned(
|
|
IN PUL_SPIN_LOCK pSpinLock
|
|
)
|
|
{
|
|
if (pSpinLock->pOwnerThread == NULL)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
} // UlDbgSpinLockUnowned
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Filter for exceptions caught with try/except.
|
|
|
|
Arguments:
|
|
|
|
pExceptionPointers - Supplies information identifying the source
|
|
and type of exception raised.
|
|
|
|
pFileName - Supplies the name of the file generating the exception.
|
|
|
|
LineNumber - Supplies the line number of the exception filter that
|
|
caught the exception.
|
|
|
|
Return Value:
|
|
|
|
LONG - Should always be EXCEPTION_EXECUTE_HANDLER
|
|
|
|
--***************************************************************************/
|
|
LONG
|
|
UlDbgExceptionFilter(
|
|
IN PEXCEPTION_POINTERS pExceptionPointers,
|
|
IN PSTR pFileName,
|
|
IN USHORT LineNumber
|
|
)
|
|
{
|
|
//
|
|
// Protect ourselves just in case the process is completely messed up.
|
|
//
|
|
|
|
__try
|
|
{
|
|
//
|
|
// Whine about it.
|
|
//
|
|
|
|
KdPrint((
|
|
"UlDbgExceptionFilter: exception %08lx @ %p, caught in %s:%d\n",
|
|
pExceptionPointers->ExceptionRecord->ExceptionCode,
|
|
pExceptionPointers->ExceptionRecord->ExceptionAddress,
|
|
UlpDbgFindFilePart( pFileName ),
|
|
LineNumber
|
|
));
|
|
}
|
|
__except( EXCEPTION_EXECUTE_HANDLER )
|
|
{
|
|
//
|
|
// Not much we can do here...
|
|
//
|
|
|
|
NOTHING;
|
|
}
|
|
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
|
|
} // UlDbgExceptionFilter
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Sometimes it's not acceptable to proceed with warnings ( as status ) after
|
|
we caught an exception. I.e. Caught an missaligned warning during sendresponse
|
|
and called the IoCompleteRequest with status misaligned. This will cause Io
|
|
Manager to complete request to port, even though we don't want it to happen.
|
|
|
|
In that case we have to carefully replace warnings with a generic error.
|
|
|
|
Arguments:
|
|
|
|
pExceptionPointers - Supplies information identifying the source
|
|
and type of exception raised.
|
|
|
|
pFileName - Supplies the name of the file generating the exception.
|
|
|
|
LineNumber - Supplies the line number of the exception filter that
|
|
caught the exception.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Converted error value : UL_DEFAULT_ERROR_ON_EXCEPTION
|
|
|
|
--***************************************************************************/
|
|
|
|
NTSTATUS
|
|
UlDbgConvertExceptionCode(
|
|
IN NTSTATUS status,
|
|
IN PSTR pFileName,
|
|
IN USHORT LineNumber
|
|
)
|
|
{
|
|
//
|
|
// Whine about it.
|
|
//
|
|
|
|
KdPrint((
|
|
"UlDbgConvertExceptionCode: exception %08lx converted to %08lx, at %s:%d\n",
|
|
status,
|
|
UL_DEFAULT_ERROR_ON_EXCEPTION,
|
|
UlpDbgFindFilePart( pFileName ),
|
|
LineNumber
|
|
));
|
|
|
|
return UL_DEFAULT_ERROR_ON_EXCEPTION;
|
|
}
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Completion handler for incomplete IRP contexts.
|
|
|
|
Arguments:
|
|
|
|
pCompletionContext - Supplies an uninterpreted context value
|
|
as passed to the asynchronous API.
|
|
|
|
Status - Supplies the final completion status of the
|
|
asynchronous API.
|
|
|
|
Information - Optionally supplies additional information about
|
|
the completed operation, such as the number of bytes
|
|
transferred.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlDbgInvalidCompletionRoutine(
|
|
IN PVOID pCompletionContext,
|
|
IN NTSTATUS Status,
|
|
IN ULONG_PTR Information
|
|
)
|
|
{
|
|
DbgPrint(
|
|
"UlDbgInvalidCompletionRoutine called!\n"
|
|
" pCompletionContext = %p\n"
|
|
" Status = %08lx\n"
|
|
" Information = %p\n",
|
|
pCompletionContext,
|
|
Status,
|
|
Information
|
|
);
|
|
|
|
ASSERT( !"UlDbgInvalidCompletionRoutine called!" );
|
|
|
|
} // UlDbgInvalidCompletionRoutine
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Hook for catching failed operations. This routine is called within each
|
|
routine with the completion status.
|
|
|
|
Arguments:
|
|
|
|
Status - Supplies the completion status.
|
|
|
|
pFileName - Supplies the filename of the caller.
|
|
|
|
LineNumber - Supplies the line number of the caller.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlDbgStatus(
|
|
IN NTSTATUS Status,
|
|
IN PSTR pFileName,
|
|
IN USHORT LineNumber
|
|
)
|
|
{
|
|
//
|
|
// paulmcd: ignore STATUS_END_OF_FILE. this is a non-fatal return value
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status) && Status != STATUS_END_OF_FILE)
|
|
{
|
|
if (g_UlVerboseErrors)
|
|
{
|
|
DbgPrint(
|
|
"UlDbgStatus: %s:%lu returning %08lx\n",
|
|
UlpDbgFindFilePart( pFileName ),
|
|
LineNumber,
|
|
Status
|
|
);
|
|
}
|
|
|
|
if (g_UlBreakOnError)
|
|
{
|
|
DbgBreakPoint();
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // UlDbgStatus
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Routine invoked upon entry into the driver.
|
|
|
|
Arguments:
|
|
|
|
pFunctionName - Supplies the name of the function used to enter
|
|
the driver.
|
|
|
|
pIrp - Supplies an optional IRP to log.
|
|
|
|
pFileName - Supplies the filename of the caller.
|
|
|
|
LineNumber - Supplies the line number of the caller.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlDbgEnterDriver(
|
|
IN PSTR pFunctionName,
|
|
IN PIRP pIrp OPTIONAL,
|
|
IN PSTR pFileName,
|
|
IN USHORT LineNumber
|
|
)
|
|
{
|
|
PUL_DEBUG_THREAD_DATA pData;
|
|
|
|
//
|
|
// Log the IRP.
|
|
//
|
|
|
|
if (pIrp != NULL)
|
|
{
|
|
WRITE_IRP_TRACE_LOG(
|
|
g_pIrpTraceLog,
|
|
IRP_ACTION_INCOMING_IRP,
|
|
pIrp,
|
|
pFileName,
|
|
LineNumber
|
|
);
|
|
}
|
|
|
|
//
|
|
// Find/create an entry for the current thread.
|
|
//
|
|
|
|
pData = ULP_DBG_FIND_OR_CREATE_THREAD();
|
|
|
|
if (pData != NULL)
|
|
{
|
|
|
|
//
|
|
// This should be the first time we enter the driver
|
|
// unless we are stealing this thread due to an interrupt,
|
|
// or we are calling another driver and they are calling
|
|
// our completion routine in-line.
|
|
//
|
|
|
|
ASSERT( KeGetCurrentIrql() > PASSIVE_LEVEL ||
|
|
pData->ExternalCallCount > 0 ||
|
|
pData->ResourceCount == 0 );
|
|
}
|
|
|
|
} // UlDbgEnterDriver
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Routine invoked upon exit from the driver.
|
|
|
|
Arguments:
|
|
|
|
pFunctionName - Supplies the name of the function used to enter
|
|
the driver.
|
|
|
|
pFileName - Supplies the filename of the caller.
|
|
|
|
LineNumber - Supplies the line number of the caller.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlDbgLeaveDriver(
|
|
IN PSTR pFunctionName,
|
|
IN PSTR pFileName,
|
|
IN USHORT LineNumber
|
|
)
|
|
{
|
|
PUL_DEBUG_THREAD_DATA pData;
|
|
|
|
//
|
|
// Find an existing entry for the current thread.
|
|
//
|
|
|
|
pData = ULP_DBG_FIND_THREAD();
|
|
|
|
if (pData != NULL)
|
|
{
|
|
//
|
|
// Ensure no resources are acquired, then kill the thread data.
|
|
//
|
|
// we might have a resource acquired if we borrowed the thread
|
|
// due to an interrupt.
|
|
//
|
|
// N.B. We dereference the thread data twice: once for the
|
|
// call to ULP_DBG_FIND_THREAD() above, once for the call
|
|
// made when entering the driver.
|
|
//
|
|
|
|
ASSERT( KeGetCurrentIrql() > PASSIVE_LEVEL ||
|
|
pData->ExternalCallCount > 0 ||
|
|
pData->ResourceCount == 0 );
|
|
|
|
ASSERT( pData->ReferenceCount >= 2 );
|
|
ULP_DBG_DEREFERENCE_THREAD( pData );
|
|
ULP_DBG_DEREFERENCE_THREAD( pData );
|
|
}
|
|
|
|
} // UlDbgLeaveDriver
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Initialize an instrumented resource.
|
|
|
|
Arguments:
|
|
|
|
pResource - Supplies the resource to initialize.
|
|
|
|
pResourceName - Supplies a display name for the resource.
|
|
|
|
Parameter - Supplies a ULONG_PTR parameter passed into sprintf()
|
|
when creating the resource name.
|
|
|
|
pFileName - Supplies the filename of the caller.
|
|
|
|
LineNumber - Supplies the line number of the caller.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlDbgInitializeResource(
|
|
IN PUL_ERESOURCE pResource,
|
|
IN PSTR pResourceName,
|
|
IN ULONG_PTR Parameter,
|
|
IN ULONG OwnerTag,
|
|
IN PSTR pFileName,
|
|
IN USHORT LineNumber
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
KIRQL oldIrql;
|
|
|
|
//
|
|
// Initialize the resource.
|
|
//
|
|
|
|
status = ExInitializeResource( &pResource->Resource );
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
pResource->ExclusiveCount = 0;
|
|
pResource->SharedCount = 0;
|
|
pResource->ReleaseCount = 0;
|
|
pResource->OwnerTag = OwnerTag;
|
|
|
|
_snprintf(
|
|
(char*) pResource->ResourceName,
|
|
sizeof(pResource->ResourceName) - 1,
|
|
pResourceName,
|
|
Parameter
|
|
);
|
|
|
|
pResource->ResourceName[sizeof(pResource->ResourceName) - 1] = '\0';
|
|
|
|
SET_RESOURCE_NOT_OWNED_EXCLUSIVE( pResource );
|
|
|
|
//
|
|
// Put it on the global list.
|
|
//
|
|
|
|
UlAcquireSpinLock( &g_DbgSpinLock, &oldIrql );
|
|
InsertHeadList(
|
|
&g_DbgGlobalResourceListHead,
|
|
&pResource->GlobalResourceListEntry
|
|
);
|
|
UlReleaseSpinLock( &g_DbgSpinLock, oldIrql );
|
|
}
|
|
else
|
|
{
|
|
pResource->GlobalResourceListEntry.Flink = NULL;
|
|
}
|
|
|
|
return status;
|
|
|
|
} // UlDbgInitializeResource
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Deletes an instrumented resource.
|
|
|
|
Arguments:
|
|
|
|
pResource - Supplies the resource to delete.
|
|
|
|
pFileName - Supplies the filename of the caller.
|
|
|
|
LineNumber - Supplies the line number of the caller.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlDbgDeleteResource(
|
|
IN PUL_ERESOURCE pResource,
|
|
IN PSTR pFileName,
|
|
IN USHORT LineNumber
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
KIRQL oldIrql;
|
|
PETHREAD pExclusiveOwner;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
pExclusiveOwner = pResource->pExclusiveOwner;
|
|
|
|
if (pExclusiveOwner != NULL)
|
|
{
|
|
DbgBreakPoint();
|
|
|
|
DbgPrint(
|
|
"Resource %p [%s] owned by thread %p\n",
|
|
pResource,
|
|
pResource->ResourceName,
|
|
pExclusiveOwner
|
|
);
|
|
|
|
DbgBreakPoint();
|
|
}
|
|
|
|
// ASSERT( UlDbgResourceUnownedExclusive( pResource ) );
|
|
|
|
//
|
|
// Delete the resource.
|
|
//
|
|
|
|
status = ExDeleteResource( &pResource->Resource );
|
|
|
|
//
|
|
// Remove it from the global list.
|
|
//
|
|
|
|
if (pResource->GlobalResourceListEntry.Flink != NULL)
|
|
{
|
|
UlAcquireSpinLock( &g_DbgSpinLock, &oldIrql );
|
|
RemoveEntryList( &pResource->GlobalResourceListEntry );
|
|
UlReleaseSpinLock( &g_DbgSpinLock, oldIrql );
|
|
}
|
|
|
|
return status;
|
|
|
|
} // UlDbgDeleteResource
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Acquires exclusive access to an instrumented resource.
|
|
|
|
Arguments:
|
|
|
|
pResource - Supplies the resource to acquire.
|
|
|
|
Wait - Supplies TRUE if the thread should block waiting for the
|
|
resource.
|
|
|
|
pFileName - Supplies the filename of the caller.
|
|
|
|
LineNumber - Supplies the line number of the caller.
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - Completion status.
|
|
|
|
--***************************************************************************/
|
|
BOOLEAN
|
|
UlDbgAcquireResourceExclusive(
|
|
IN PUL_ERESOURCE pResource,
|
|
IN BOOLEAN Wait,
|
|
IN PSTR pFileName,
|
|
IN USHORT LineNumber
|
|
)
|
|
{
|
|
PUL_DEBUG_THREAD_DATA pData;
|
|
BOOLEAN result;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
ASSERT(pResource);
|
|
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
|
|
|
|
//
|
|
// Find an existing entry for the current thread.
|
|
//
|
|
|
|
pData = ULP_DBG_FIND_THREAD();
|
|
|
|
if (pData != NULL)
|
|
{
|
|
//
|
|
// Update the resource count.
|
|
//
|
|
|
|
pData->ResourceCount++;
|
|
ASSERT( pData->ResourceCount > 0 );
|
|
|
|
WRITE_REF_TRACE_LOG(
|
|
g_pThreadTraceLog,
|
|
REF_ACTION_ACQUIRE_RESOURCE_EXCLUSIVE,
|
|
pData->ResourceCount,
|
|
pResource,
|
|
pFileName,
|
|
LineNumber
|
|
);
|
|
|
|
ULP_DBG_DEREFERENCE_THREAD( pData );
|
|
}
|
|
|
|
//
|
|
// Acquire the resource.
|
|
//
|
|
|
|
KeEnterCriticalRegion();
|
|
result = ExAcquireResourceExclusive( &pResource->Resource, Wait );
|
|
|
|
//
|
|
// either we already own it (recursive acquisition), or nobody owns it.
|
|
//
|
|
|
|
ASSERT( UlDbgResourceUnownedExclusive( pResource ) ||
|
|
UlDbgResourceOwnedExclusive( pResource ) );
|
|
|
|
//
|
|
// Mark it as owned by the current thread.
|
|
//
|
|
|
|
SET_RESOURCE_OWNED_EXCLUSIVE( pResource );
|
|
|
|
//
|
|
// Update the statistics.
|
|
//
|
|
|
|
InterlockedIncrement( &pResource->ExclusiveCount );
|
|
|
|
return result;
|
|
|
|
} // UlDbgAcquireResourceExclusive
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Acquires shared access to an instrumented resource.
|
|
|
|
Arguments:
|
|
|
|
pResource - Supplies the resource to acquire.
|
|
|
|
Wait - Supplies TRUE if the thread should block waiting for the
|
|
resource.
|
|
|
|
pFileName - Supplies the filename of the caller.
|
|
|
|
LineNumber - Supplies the line number of the caller.
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - Completion status.
|
|
|
|
--***************************************************************************/
|
|
BOOLEAN
|
|
UlDbgAcquireResourceShared(
|
|
IN PUL_ERESOURCE pResource,
|
|
IN BOOLEAN Wait,
|
|
IN PSTR pFileName,
|
|
IN USHORT LineNumber
|
|
)
|
|
{
|
|
PUL_DEBUG_THREAD_DATA pData;
|
|
BOOLEAN result;
|
|
|
|
//
|
|
// Find an existing entry for the current thread.
|
|
//
|
|
|
|
pData = ULP_DBG_FIND_THREAD();
|
|
|
|
if (pData != NULL)
|
|
{
|
|
//
|
|
// Update the resource count.
|
|
//
|
|
|
|
pData->ResourceCount++;
|
|
ASSERT( pData->ResourceCount > 0 );
|
|
|
|
WRITE_REF_TRACE_LOG(
|
|
g_pThreadTraceLog,
|
|
REF_ACTION_ACQUIRE_RESOURCE_SHARED,
|
|
pData->ResourceCount,
|
|
pResource,
|
|
pFileName,
|
|
LineNumber
|
|
);
|
|
|
|
ULP_DBG_DEREFERENCE_THREAD( pData );
|
|
}
|
|
|
|
//
|
|
// Acquire the resource.
|
|
//
|
|
|
|
KeEnterCriticalRegion();
|
|
result = ExAcquireResourceShared( &pResource->Resource, Wait );
|
|
|
|
//
|
|
// Update the statistics.
|
|
//
|
|
|
|
InterlockedIncrement( &pResource->SharedCount );
|
|
|
|
return result;
|
|
|
|
} // UlDbgAcquireResourceShared
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Releases an instrumented resource.
|
|
|
|
Arguments:
|
|
|
|
pResource - Supplies the resource to release.
|
|
|
|
pFileName - Supplies the filename of the caller.
|
|
|
|
LineNumber - Supplies the line number of the caller.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlDbgReleaseResource(
|
|
IN PUL_ERESOURCE pResource,
|
|
IN PSTR pFileName,
|
|
IN USHORT LineNumber
|
|
)
|
|
{
|
|
PUL_DEBUG_THREAD_DATA pData;
|
|
|
|
//
|
|
// Find an existing entry for the current thread.
|
|
//
|
|
|
|
pData = ULP_DBG_FIND_THREAD();
|
|
|
|
if (pData != NULL)
|
|
{
|
|
//
|
|
// Update the resource count.
|
|
//
|
|
|
|
ASSERT( pData->ResourceCount > 0 );
|
|
pData->ResourceCount--;
|
|
|
|
WRITE_REF_TRACE_LOG(
|
|
g_pThreadTraceLog,
|
|
REF_ACTION_RELEASE_RESOURCE,
|
|
pData->ResourceCount,
|
|
pResource,
|
|
pFileName,
|
|
LineNumber
|
|
);
|
|
|
|
ULP_DBG_DEREFERENCE_THREAD( pData );
|
|
}
|
|
|
|
//
|
|
// Mark it as unowned.
|
|
//
|
|
|
|
SET_RESOURCE_NOT_OWNED_EXCLUSIVE( pResource );
|
|
|
|
//
|
|
// Release the resource.
|
|
//
|
|
|
|
ExReleaseResource( &pResource->Resource );
|
|
KeLeaveCriticalRegion();
|
|
|
|
//
|
|
// Update the statistics.
|
|
//
|
|
|
|
InterlockedIncrement( &pResource->ReleaseCount );
|
|
|
|
|
|
} // UlDbgReleaseResource
|
|
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
This routine converts the specified resource from acquired for exclusive
|
|
access to acquired for shared access.
|
|
|
|
Arguments:
|
|
|
|
pResource - Supplies the resource to release.
|
|
|
|
pFileName - Supplies the filename of the caller.
|
|
|
|
LineNumber - Supplies the line number of the caller.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlDbgConvertExclusiveToShared(
|
|
IN PUL_ERESOURCE pResource,
|
|
IN PSTR pFileName,
|
|
IN USHORT LineNumber
|
|
)
|
|
{
|
|
PUL_DEBUG_THREAD_DATA pData;
|
|
|
|
ASSERT(UlDbgResourceOwnedExclusive(pResource));
|
|
|
|
//
|
|
// Find an existing entry for the current thread.
|
|
//
|
|
|
|
pData = ULP_DBG_FIND_THREAD();
|
|
|
|
if (pData != NULL)
|
|
{
|
|
//
|
|
// Don't update the resource count.
|
|
//
|
|
|
|
WRITE_REF_TRACE_LOG(
|
|
g_pThreadTraceLog,
|
|
REF_ACTION_CONVERT_RESOURCE_EXCLUSIVE_TO_SHARED,
|
|
pData->ResourceCount,
|
|
pResource,
|
|
pFileName,
|
|
LineNumber
|
|
);
|
|
|
|
ULP_DBG_DEREFERENCE_THREAD( pData );
|
|
}
|
|
|
|
//
|
|
// Acquire the resource.
|
|
//
|
|
|
|
ExConvertExclusiveToSharedLite( &pResource->Resource );
|
|
|
|
//
|
|
// Update the statistics.
|
|
//
|
|
|
|
InterlockedIncrement( &pResource->SharedCount );
|
|
SET_RESOURCE_NOT_OWNED_EXCLUSIVE( pResource );
|
|
|
|
} // UlDbgConvertExclusiveToShared
|
|
|
|
|
|
#ifdef UL_TRY_RESOURCE_EXCLUSIVE
|
|
// ExTryToAcquireResourceExclusiveLite is not currently exported
|
|
// from ntoskrnl
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
The routine attempts to acquire the specified resource for exclusive
|
|
access.
|
|
|
|
Arguments:
|
|
|
|
pResource - Supplies the resource to release.
|
|
|
|
pFileName - Supplies the filename of the caller.
|
|
|
|
LineNumber - Supplies the line number of the caller.
|
|
|
|
--***************************************************************************/
|
|
BOOLEAN
|
|
UlDbgTryToAcquireResourceExclusive(
|
|
IN PUL_ERESOURCE pResource,
|
|
IN PSTR pFileName,
|
|
IN USHORT LineNumber
|
|
)
|
|
{
|
|
PUL_DEBUG_THREAD_DATA pData;
|
|
BOOLEAN result;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
ASSERT(pResource);
|
|
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
|
|
|
|
//
|
|
// Acquire the resource.
|
|
//
|
|
|
|
KeEnterCriticalRegion();
|
|
result = ExTryToAcquireResourceExclusiveLite( &pResource->Resource );
|
|
|
|
// Did we acquire the lock exclusively?
|
|
if (! result)
|
|
{
|
|
KeLeaveCriticalRegion();
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Find an existing entry for the current thread.
|
|
//
|
|
|
|
pData = ULP_DBG_FIND_THREAD();
|
|
|
|
if (pData != NULL)
|
|
{
|
|
//
|
|
// Update the resource count.
|
|
//
|
|
|
|
pData->ResourceCount++;
|
|
ASSERT( pData->ResourceCount > 0 );
|
|
|
|
WRITE_REF_TRACE_LOG(
|
|
g_pThreadTraceLog,
|
|
REF_ACTION_TRY_ACQUIRE_RESOURCE_EXCLUSIVE,
|
|
pData->ResourceCount,
|
|
pResource,
|
|
pFileName,
|
|
LineNumber
|
|
);
|
|
|
|
ULP_DBG_DEREFERENCE_THREAD( pData );
|
|
}
|
|
|
|
//
|
|
// either we already own it (recursive acquisition), or nobody owns it.
|
|
//
|
|
|
|
ASSERT( UlDbgResourceUnownedExclusive( pResource ) ||
|
|
UlDbgResourceOwnedExclusive( pResource ) );
|
|
|
|
//
|
|
// Mark it as owned by the current thread.
|
|
//
|
|
|
|
SET_RESOURCE_OWNED_EXCLUSIVE( pResource );
|
|
|
|
//
|
|
// Update the statistics.
|
|
//
|
|
|
|
InterlockedIncrement( &pResource->ExclusiveCount );
|
|
|
|
return result;
|
|
|
|
} // UlDbgTryToAcquireResourceExclusive
|
|
|
|
#endif // UL_TRY_RESOURCE_EXCLUSIVE
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Determines if the specified resource is owned exclusively by the
|
|
current thread.
|
|
|
|
Arguments:
|
|
|
|
pResource - Supplies the resource to test.
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if the resource is owned exclusively by the current
|
|
thread, FALSE otherwise.
|
|
|
|
--***************************************************************************/
|
|
BOOLEAN
|
|
UlDbgResourceOwnedExclusive(
|
|
IN PUL_ERESOURCE pResource
|
|
)
|
|
{
|
|
if (pResource->pExclusiveOwner == PsGetCurrentThread())
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// CODEWORK: handle the case of recursive exclusive locks correctly
|
|
|
|
return FALSE;
|
|
|
|
} // UlDbgResourceOwnedExclusive
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Determines if the specified resource is not currently owned exclusively
|
|
by any thread.
|
|
|
|
Arguments:
|
|
|
|
pResource - Supplies the resource to test.
|
|
|
|
Return Value:
|
|
|
|
BOOLEAN - TRUE if the resource is not currently owned exclusively by
|
|
any thread, FALSE otherwise.
|
|
|
|
--***************************************************************************/
|
|
BOOLEAN
|
|
UlDbgResourceUnownedExclusive(
|
|
IN PUL_ERESOURCE pResource
|
|
)
|
|
{
|
|
if (pResource->pExclusiveOwner == NULL)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
} // UlDbgResourceUnownedExclusive
|
|
|
|
VOID
|
|
UlDbgDumpRequestBuffer(
|
|
IN struct _UL_REQUEST_BUFFER *pBuffer,
|
|
IN PSTR pName
|
|
)
|
|
{
|
|
DbgPrint(
|
|
"%s @ %p\n"
|
|
" Signature = %08lx\n"
|
|
" ListEntry @ %p%s\n"
|
|
" pConnection = %p\n"
|
|
" WorkItem @ %p\n"
|
|
" UsedBytes = %lu\n"
|
|
" AllocBytes = %lu\n"
|
|
" ParsedBytes = %lu\n"
|
|
" BufferNumber = %lu\n"
|
|
" JumboBuffer = %lu\n"
|
|
" pBuffer @ %p\n",
|
|
pName,
|
|
pBuffer,
|
|
pBuffer->Signature,
|
|
&pBuffer->ListEntry,
|
|
IsListEmpty( &pBuffer->ListEntry ) ? " EMPTY" : "",
|
|
pBuffer->pConnection,
|
|
&pBuffer->WorkItem,
|
|
pBuffer->UsedBytes,
|
|
pBuffer->AllocBytes,
|
|
pBuffer->ParsedBytes,
|
|
pBuffer->BufferNumber,
|
|
pBuffer->JumboBuffer,
|
|
&pBuffer->pBuffer[0]
|
|
);
|
|
|
|
} // UlDbgDumpRequestBuffer
|
|
|
|
VOID
|
|
UlDbgDumpHttpConnection(
|
|
IN struct _UL_HTTP_CONNECTION *pConnection,
|
|
IN PSTR pName
|
|
)
|
|
{
|
|
DbgPrint(
|
|
"%s @ %p\n"
|
|
" Signature = %08lx\n"
|
|
" ConnectionId = %08lx%08lx\n"
|
|
" WorkItem @ %p\n"
|
|
" RefCount = %lu\n"
|
|
" NextRecvNumber = %lu\n"
|
|
" NextBufferNumber = %lu\n"
|
|
" NextBufferToParse = %lu\n"
|
|
" pConnection = %p\n"
|
|
" pRequest = %p\n",
|
|
pName,
|
|
pConnection,
|
|
pConnection->Signature,
|
|
pConnection->ConnectionId,
|
|
&pConnection->WorkItem,
|
|
pConnection->RefCount,
|
|
pConnection->NextRecvNumber,
|
|
pConnection->NextBufferNumber,
|
|
pConnection->NextBufferToParse,
|
|
pConnection->pConnection,
|
|
pConnection->pRequest
|
|
);
|
|
|
|
DbgPrint(
|
|
"%s @ %p (cont.)\n"
|
|
" Resource @ %p\n"
|
|
" BufferHead @ %p%s\n"
|
|
" pCurrentBuffer = %p\n"
|
|
" NeedMoreData = %lu\n"
|
|
#if REFERENCE_DEBUG
|
|
" pTraceLog = %p\n"
|
|
#endif
|
|
,
|
|
pName,
|
|
pConnection,
|
|
&pConnection->Resource,
|
|
&pConnection->BufferHead,
|
|
IsListEmpty( &pConnection->BufferHead ) ? " EMPTY" : "",
|
|
pConnection->pCurrentBuffer,
|
|
pConnection->NeedMoreData
|
|
#if REFERENCE_DEBUG
|
|
,
|
|
pConnection->pTraceLog
|
|
#endif
|
|
);
|
|
|
|
} // UlDbgDumpHttpConnection
|
|
|
|
PIRP
|
|
UlDbgAllocateIrp(
|
|
IN CCHAR StackSize,
|
|
IN BOOLEAN ChargeQuota,
|
|
IN PSTR pFileName,
|
|
IN USHORT LineNumber
|
|
)
|
|
{
|
|
PIRP pIrp;
|
|
|
|
pIrp = IoAllocateIrp( StackSize, ChargeQuota );
|
|
|
|
if (pIrp != NULL)
|
|
{
|
|
WRITE_IRP_TRACE_LOG(
|
|
g_pIrpTraceLog,
|
|
IRP_ACTION_ALLOCATE_IRP,
|
|
pIrp,
|
|
pFileName,
|
|
LineNumber
|
|
);
|
|
}
|
|
|
|
return pIrp;
|
|
|
|
} // UlDbgAllocateIrp
|
|
|
|
BOOLEAN g_ReallyFreeIrps = TRUE;
|
|
|
|
VOID
|
|
UlDbgFreeIrp(
|
|
IN PIRP pIrp,
|
|
IN PSTR pFileName,
|
|
IN USHORT LineNumber
|
|
)
|
|
{
|
|
WRITE_IRP_TRACE_LOG(
|
|
g_pIrpTraceLog,
|
|
IRP_ACTION_FREE_IRP,
|
|
pIrp,
|
|
pFileName,
|
|
LineNumber
|
|
);
|
|
|
|
if (g_ReallyFreeIrps)
|
|
{
|
|
IoFreeIrp( pIrp );
|
|
}
|
|
|
|
} // UlDbgFreeIrp
|
|
|
|
NTSTATUS
|
|
UlDbgCallDriver(
|
|
IN PDEVICE_OBJECT pDeviceObject,
|
|
IN OUT PIRP pIrp,
|
|
IN PSTR pFileName,
|
|
IN USHORT LineNumber
|
|
)
|
|
{
|
|
PUL_DEBUG_THREAD_DATA pData;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Record the fact that we are about to call another
|
|
// driver in the thread data. That way if the driver
|
|
// calls our completion routine in-line our debug
|
|
// code won't get confused about it.
|
|
//
|
|
|
|
//
|
|
// Find an existing entry for the current thread.
|
|
//
|
|
|
|
pData = ULP_DBG_FIND_THREAD();
|
|
|
|
if (pData != NULL)
|
|
{
|
|
//
|
|
// Update the external call count.
|
|
//
|
|
|
|
pData->ExternalCallCount++;
|
|
ASSERT( pData->ExternalCallCount > 0 );
|
|
}
|
|
|
|
WRITE_IRP_TRACE_LOG(
|
|
g_pIrpTraceLog,
|
|
IRP_ACTION_CALL_DRIVER,
|
|
pIrp,
|
|
pFileName,
|
|
LineNumber
|
|
);
|
|
|
|
//
|
|
// Call the driver.
|
|
//
|
|
|
|
Status = IoCallDriver( pDeviceObject, pIrp );
|
|
|
|
//
|
|
// Update the external call count.
|
|
//
|
|
|
|
if (pData != NULL)
|
|
{
|
|
pData->ExternalCallCount--;
|
|
ASSERT( pData->ExternalCallCount >= 0 );
|
|
|
|
ULP_DBG_DEREFERENCE_THREAD( pData );
|
|
}
|
|
|
|
return Status;
|
|
|
|
} // UlDbgCallDriver
|
|
|
|
VOID
|
|
UlDbgCompleteRequest(
|
|
IN PIRP pIrp,
|
|
IN CCHAR PriorityBoost,
|
|
IN PSTR pFileName,
|
|
IN USHORT LineNumber
|
|
)
|
|
{
|
|
WRITE_IRP_TRACE_LOG(
|
|
g_pIrpTraceLog,
|
|
IRP_ACTION_COMPLETE_IRP,
|
|
pIrp,
|
|
pFileName,
|
|
LineNumber
|
|
);
|
|
|
|
IoCompleteRequest( pIrp, PriorityBoost );
|
|
|
|
} // UlDbgCompleteRequest
|
|
|
|
PMDL
|
|
UlDbgAllocateMdl(
|
|
IN PVOID VirtualAddress,
|
|
IN ULONG Length,
|
|
IN BOOLEAN SecondaryBuffer,
|
|
IN BOOLEAN ChargeQuota,
|
|
IN OUT PIRP Irp,
|
|
IN PSTR pFileName,
|
|
IN USHORT LineNumber
|
|
)
|
|
{
|
|
PMDL mdl;
|
|
|
|
mdl = IoAllocateMdl(
|
|
VirtualAddress,
|
|
Length,
|
|
SecondaryBuffer,
|
|
ChargeQuota,
|
|
Irp
|
|
);
|
|
|
|
if (mdl != NULL)
|
|
{
|
|
WRITE_REF_TRACE_LOG(
|
|
g_pMdlTraceLog,
|
|
REF_ACTION_ALLOCATE_MDL,
|
|
PtrToLong(mdl->Next), // bugbug64
|
|
mdl,
|
|
pFileName,
|
|
LineNumber
|
|
);
|
|
|
|
#ifdef SPECIAL_MDL_FLAG
|
|
ASSERT( (mdl->MdlFlags & SPECIAL_MDL_FLAG) == 0 );
|
|
#endif
|
|
}
|
|
|
|
return mdl;
|
|
|
|
} // UlDbgAllocateMdl
|
|
|
|
BOOLEAN g_ReallyFreeMdls = TRUE;
|
|
|
|
VOID
|
|
UlDbgFreeMdl(
|
|
IN PMDL Mdl,
|
|
IN PSTR pFileName,
|
|
IN USHORT LineNumber
|
|
)
|
|
{
|
|
WRITE_REF_TRACE_LOG(
|
|
g_pMdlTraceLog,
|
|
REF_ACTION_FREE_MDL,
|
|
PtrToLong(Mdl->Next), // bugbug64
|
|
Mdl,
|
|
pFileName,
|
|
LineNumber
|
|
);
|
|
|
|
#ifdef SPECIAL_MDL_FLAG
|
|
ASSERT( (Mdl->MdlFlags & SPECIAL_MDL_FLAG) == 0 );
|
|
#endif
|
|
|
|
if (g_ReallyFreeMdls)
|
|
{
|
|
IoFreeMdl( Mdl );
|
|
}
|
|
|
|
} // UlDbgFreeMdl
|
|
|
|
|
|
//
|
|
// Private functions.
|
|
//
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Updates a pool counter.
|
|
|
|
Arguments:
|
|
|
|
pAddend - Supplies the counter to update.
|
|
|
|
Increment - Supplies the value to add to the counter.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlpDbgUpdatePoolCounter(
|
|
IN OUT PLARGE_INTEGER pAddend,
|
|
IN SIZE_T Increment
|
|
)
|
|
{
|
|
ULONG tmp;
|
|
|
|
tmp = (ULONG)Increment;
|
|
ASSERT( (SIZE_T)tmp == Increment );
|
|
|
|
ExInterlockedAddLargeStatistic(
|
|
pAddend,
|
|
tmp
|
|
);
|
|
|
|
} // UlpDbgUpdatePoolCounter
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Locates the file part of a fully qualified path.
|
|
|
|
Arguments:
|
|
|
|
pPath - Supplies the path to scan.
|
|
|
|
Return Value:
|
|
|
|
PSTR - The file part.
|
|
|
|
--***************************************************************************/
|
|
PSTR
|
|
UlpDbgFindFilePart(
|
|
IN PSTR pPath
|
|
)
|
|
{
|
|
PSTR pFilePart;
|
|
|
|
//
|
|
// Strip off the path from the path.
|
|
//
|
|
|
|
pFilePart = strrchr( pPath, '\\' );
|
|
|
|
if (pFilePart == NULL)
|
|
{
|
|
pFilePart = pPath;
|
|
}
|
|
else
|
|
{
|
|
pFilePart++;
|
|
}
|
|
|
|
return pFilePart;
|
|
|
|
} // UlpDbgFindFilePart
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Locates and optionally creates per-thread data for the current thread.
|
|
|
|
Return Value:
|
|
|
|
PUL_DEBUG_THREAD_DATA - The thread data if successful, NULL otherwise.
|
|
|
|
--***************************************************************************/
|
|
PUL_DEBUG_THREAD_DATA
|
|
UlpDbgFindThread(
|
|
BOOLEAN OkToCreate,
|
|
PSTR pFileName,
|
|
USHORT LineNumber
|
|
)
|
|
{
|
|
PUL_DEBUG_THREAD_DATA pData;
|
|
PUL_THREAD_HASH_BUCKET pBucket;
|
|
PETHREAD pThread;
|
|
KIRQL oldIrql;
|
|
PLIST_ENTRY pListEntry;
|
|
ULONG refCount;
|
|
|
|
//
|
|
// Get the current thread, find the correct bucket.
|
|
//
|
|
|
|
pThread = PsGetCurrentThread();
|
|
pBucket = &g_DbgThreadHashBuckets[HASH_FROM_THREAD(pThread)];
|
|
|
|
//
|
|
// Lock the bucket.
|
|
//
|
|
|
|
UlAcquireSpinLock( &pBucket->BucketSpinLock, &oldIrql );
|
|
|
|
//
|
|
// Try to find an existing entry for the current thread.
|
|
//
|
|
|
|
for (pListEntry = pBucket->BucketListHead.Flink ;
|
|
pListEntry != &pBucket->BucketListHead ;
|
|
pListEntry = pListEntry->Flink)
|
|
{
|
|
pData = CONTAINING_RECORD(
|
|
pListEntry,
|
|
UL_DEBUG_THREAD_DATA,
|
|
ThreadDataListEntry
|
|
);
|
|
|
|
if (pData->pThread == pThread)
|
|
{
|
|
//
|
|
// Found one. Update the reference count, then return the
|
|
// existing entry.
|
|
//
|
|
|
|
pData->ReferenceCount++;
|
|
refCount = pData->ReferenceCount;
|
|
UlReleaseSpinLock( &pBucket->BucketSpinLock, oldIrql );
|
|
|
|
//
|
|
// Trace it.
|
|
//
|
|
|
|
WRITE_REF_TRACE_LOG(
|
|
g_pThreadTraceLog,
|
|
REF_ACTION_REFERENCE_THREAD,
|
|
refCount,
|
|
pData,
|
|
pFileName,
|
|
LineNumber
|
|
);
|
|
|
|
return pData;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we made it this far, then data has not yet been created for
|
|
// the current thread. Create & initialize it now if we're allowed.
|
|
// Basically it's only ok if we're called from UlDbgEnterDriver.
|
|
//
|
|
|
|
if (OkToCreate)
|
|
{
|
|
pData = (PUL_DEBUG_THREAD_DATA) UL_ALLOCATE_POOL(
|
|
NonPagedPool,
|
|
sizeof(*pData),
|
|
UL_DEBUG_THREAD_POOL_TAG
|
|
);
|
|
|
|
if (pData != NULL)
|
|
{
|
|
RtlZeroMemory( pData, sizeof(*pData) );
|
|
|
|
pData->pThread = pThread;
|
|
pData->ReferenceCount = 1;
|
|
pData->ResourceCount = 0;
|
|
|
|
InsertHeadList(
|
|
&pBucket->BucketListHead,
|
|
&pData->ThreadDataListEntry
|
|
);
|
|
|
|
InterlockedIncrement( &g_DbgThreadCreated );
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
pData = NULL;
|
|
}
|
|
|
|
UlReleaseSpinLock( &pBucket->BucketSpinLock, oldIrql );
|
|
|
|
return pData;
|
|
|
|
} // UlpDbgFindThread
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Dereferences per-thread data.
|
|
|
|
Arguments:
|
|
|
|
pData - Supplies the thread data to dereference.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlpDbgDereferenceThread(
|
|
IN PUL_DEBUG_THREAD_DATA pData
|
|
REFERENCE_DEBUG_FORMAL_PARAMS
|
|
)
|
|
{
|
|
PUL_THREAD_HASH_BUCKET pBucket;
|
|
KIRQL oldIrql;
|
|
ULONG refCount;
|
|
|
|
//
|
|
// Find the correct bucket.
|
|
//
|
|
|
|
pBucket = &g_DbgThreadHashBuckets[HASH_FROM_THREAD(pData->pThread)];
|
|
|
|
//
|
|
// Update the reference count.
|
|
//
|
|
|
|
UlAcquireSpinLock( &pBucket->BucketSpinLock, &oldIrql );
|
|
|
|
ASSERT( pData->ReferenceCount > 0 );
|
|
pData->ReferenceCount--;
|
|
|
|
refCount = pData->ReferenceCount;
|
|
|
|
if (pData->ReferenceCount == 0)
|
|
{
|
|
//
|
|
// It dropped to zero, so remove the thread from the bucket
|
|
// and free the resources.
|
|
//
|
|
|
|
RemoveEntryList( &pData->ThreadDataListEntry );
|
|
UlReleaseSpinLock( &pBucket->BucketSpinLock, oldIrql );
|
|
|
|
UL_FREE_POOL( pData, UL_DEBUG_THREAD_POOL_TAG );
|
|
InterlockedIncrement( &g_DbgThreadDestroyed );
|
|
}
|
|
else
|
|
{
|
|
UlReleaseSpinLock( &pBucket->BucketSpinLock, oldIrql );
|
|
}
|
|
|
|
//
|
|
// Trace it.
|
|
//
|
|
|
|
WRITE_REF_TRACE_LOG(
|
|
g_pThreadTraceLog,
|
|
REF_ACTION_DEREFERENCE_THREAD,
|
|
refCount,
|
|
pData,
|
|
pFileName,
|
|
LineNumber
|
|
);
|
|
|
|
} // UlpDbgDereferenceThread
|
|
|
|
|
|
#endif // DBG
|
|
|