/*++ Copyright (c) 1997 Microsoft Corporation Module Name: debug.c Abstract: This module contains all debug-related code. Revision History: Notes: --*/ /// 这个头文件只include了其他头文件,关闭了几个编译警告 #include <precomp.h> /// 定义文件标识 #define __FILENUMBER 'GBED' /// 如果是DBG #if DBG /// 定义Debug信息输出级别为DL_WARN(警告) INT ndisprotDebugLevel=DL_WARN; /// 定义锁 NDIS_SPIN_LOCK ndisprotDbgLogLock; /// 定义内存头 PNUIOD_ALLOCATION ndisprotdMemoryHead = (PNUIOD_ALLOCATION)NULL; /// 定义内存尾 PNUIOD_ALLOCATION ndisprotdMemoryTail = (PNUIOD_ALLOCATION)NULL; /// ULONG ndisprotdAllocCount = 0; // how many allocated so far (unfreed) /// 定义内存锁 NDIS_SPIN_LOCK ndisprotdMemoryLock; /// 定义初期化完成标记,默认为没有完成 BOOLEAN ndisprotdInitDone = FALSE; /// 带审查的内存分配 PVOID ndisprotAuditAllocMem( PVOID pPointer, ULONG Size, ULONG FileNumber, ULONG LineNumber ) { PVOID pBuffer; PNUIOD_ALLOCATION pAllocInfo; /// 如果还没有初期化 if (!ndisprotdInitDone) { /// 分配锁 #define NdisAllocateSpinLock(_SpinLock) KeInitializeSpinLock(&(_SpinLock)->SpinLock) NdisAllocateSpinLock(&(ndisprotdMemoryLock)); /// 设置初期化标记,可以看出所谓的初期化就是看内存锁有没有分配 ndisprotdInitDone = TRUE; } /// 分配带标签('oiuN')的内存 /// 注意Size + sizeof(NUIOD_ALLOCATION),Size是调用者请求的大小,sizeof(NUIOD_ALLOCATION) /// 是附加的大小,就是说,分配了一块请求者传入的size+NUIOD_ALLOCATION结构体大小的内存 NdisAllocateMemoryWithTag( (PVOID *)&pAllocInfo, Size+sizeof(NUIOD_ALLOCATION), (ULONG)'oiuN' ); if (pAllocInfo == (PNUIOD_ALLOCATION)NULL) { /// 这里+50是把输出级别提到很高,基本上不会输出Debug信息 /// 内存分配失败属于重大错误,难道写错了,应该是DL_VERY_LOUD - 50 DEBUGP(DL_VERY_LOUD+50, ("ndisprotAuditAllocMem: file %d, line %d, Size %d failed!/n", FileNumber, LineNumber, Size)); pBuffer = NULL; } else { /// pBuffer指向了NUIOD_ALLOCATION结构体的最后成员(也就是最后一个字节) /// 看起来有点错位,好像应该是pAllocInfo->UserData+1更合理 pBuffer = (PVOID)&(pAllocInfo->UserData); /// 这里有2个问题 /// 1 为什么把内存初始化为0xaf,难道和0xcc类似的作用 /// 2 为什么初始化的内存比用户分配的size多了一个char NPROT_SET_MEM(pBuffer, 0xaf, Size); pAllocInfo->Signature = NUIOD_MEMORY_SIGNATURE; pAllocInfo->FileNumber = FileNumber; pAllocInfo->LineNumber = LineNumber; pAllocInfo->Size = Size; pAllocInfo->Location = (ULONG_PTR)pPointer; pAllocInfo->Next = (PNUIOD_ALLOCATION)NULL; NdisAcquireSpinLock(&(ndisprotdMemoryLock)); /// 下面的操作是双链表插入一个节点的过程 pAllocInfo->Prev = ndisprotdMemoryTail; if (ndisprotdMemoryTail == (PNUIOD_ALLOCATION)NULL) { // empty list ndisprotdMemoryHead = ndisprotdMemoryTail = pAllocInfo; } else { ndisprotdMemoryTail->Next = pAllocInfo; } ndisprotdMemoryTail = pAllocInfo; ndisprotdAllocCount++; NdisReleaseSpinLock(&(ndisprotdMemoryLock)); } /// 这里是成功log,+100是把输出级别提到很高,基本上不会输出Debug信息 /// 这个可以理解,程序运行到这里,可以肯定没有问题,log只是提示信息而已 DEBUGP(DL_VERY_LOUD+100, ("ndisprotAuditAllocMem: file %c%c%c%c, line %d, %d bytes, [0x%p] <- 0x%p/n", (CHAR)(FileNumber & 0xff), (CHAR)((FileNumber >> 8) & 0xff), (CHAR)((FileNumber >> 16) & 0xff), (CHAR)((FileNumber >> 24) & 0xff), LineNumber, Size, pPointer, pBuffer)); /// 其实用户用起来也没什么感觉,内存从哪里开始不重要,末尾多了1个字节也不重要 /// 重要的是用户需要的size个字节都可以有效读写 return (pBuffer); } VOID ndisprotAuditFreeMem( PVOID Pointer ) { PNUIOD_ALLOCATION pAllocInfo; /// 注意这里:锁只有一把,申请内存的时候就不能释放,反之亦然。 NdisAcquireSpinLock(&(ndisprotdMemoryLock)); /// 用到了CONTAINING_RECORD宏 /// #define CONTAINING_RECORD(address, type, field) ((type *)((PCHAR)(address)-(ULONG_PTR)(&((type *)0)->field))) /// 根据UserData成员的地址找到UserData所在的结构体NUIOD_ALLOCATION的开始位置 pAllocInfo = CONTAINING_RECORD(Pointer, NUIOD_ALLOCATION, UserData); /// 信号标志判断,需要释放的内存的信号标志只能是NUIOD_MEMORY_SIGNATURE if (pAllocInfo->Signature != NUIOD_MEMORY_SIGNATURE) { DEBUGP(DL_ERROR, ("ndisprotAuditFreeMem: unknown buffer 0x%p!/n", Pointer)); NdisReleaseSpinLock(&(ndisprotdMemoryLock)); #if DBG /// 调试断点 DbgBreakPoint(); #endif return; } /// 准备释放的内存设置信号标志为'DEAD',这里却没有使用常量 pAllocInfo->Signature = (ULONG)'DEAD'; /// 以下是双链表移除一个节点的操作 if (pAllocInfo->Prev != (PNUIOD_ALLOCATION)NULL) { pAllocInfo->Prev->Next = pAllocInfo->Next; } else { ndisprotdMemoryHead = pAllocInfo->Next; } if (pAllocInfo->Next != (PNUIOD_ALLOCATION)NULL) { pAllocInfo->Next->Prev = pAllocInfo->Prev; } else { ndisprotdMemoryTail = pAllocInfo->Prev; } ndisprotdAllocCount--; NdisReleaseSpinLock(&(ndisprotdMemoryLock)); /// f8d73400 NdisFreeMemory(pAllocInfo, 0, 0); } // 驱动卸载时会调用到这个例程 VOID ndisprotAuditShutdown( VOID ) { if (ndisprotdInitDone) { // 内存锁已经分配好 if (ndisprotdAllocCount != 0) { // 说明还有没有释放的内存 DEBUGP(DL_ERROR, ("AuditShutdown: unfreed memory, %d blocks!/n", ndisprotdAllocCount)); DEBUGP(DL_ERROR, ("MemoryHead: 0x%p, MemoryTail: 0x%p/n", ndisprotdMemoryHead, ndisprotdMemoryTail)); DbgBreakPoint(); { PNUIOD_ALLOCATION pAllocInfo; // 把没有释放的内存都释放掉 while (ndisprotdMemoryHead != (PNUIOD_ALLOCATION)NULL) { pAllocInfo = ndisprotdMemoryHead; DEBUGP(DL_INFO, ("AuditShutdown: will free 0x%p/n", pAllocInfo)); ndisprotAuditFreeMem(&(pAllocInfo->UserData)); } } } // 初始化标记设置为FALSE ndisprotdInitDone = FALSE; } } #define MAX_HD_LENGTH 128 VOID DbgPrintHexDump( IN PUCHAR pBuffer, IN ULONG Length ) /*++ Routine Description: Print a hex dump of the given contiguous buffer. If the length is too long, we truncate it. Arguments: pBuffer - Points to start of data to be dumped Length - Length of above. Return Value: None --*/ { ULONG i; if (Length > MAX_HD_LENGTH) { Length = MAX_HD_LENGTH; } for (i = 0; i < Length; i++) { // // Check if we are at the end of a line // /// 每16个字节输出一次换行 if ((i > 0) && ((i & 0xf) == 0)) { DbgPrint("/n"); } // // Print addr if we are at start of a new line // /// 如果开始新的一行(包括第1行),打印地址 if ((i & 0xf) == 0) { DbgPrint("%08p ", pBuffer); } /// 按照16进制打印字节 DbgPrint(" %02x", *pBuffer++); } // // Terminate the last line. // if (Length > 0) { DbgPrint("/n"); } } #endif // DBG #if DBG_SPIN_LOCK ULONG ndisprotdSpinLockInitDone = 0; /// 注意这个锁,是用来在本文件中使用的,有很多场合都需要加锁操作 NDIS_SPIN_LOCK ndisprotdLockLock; VOID ndisprotAllocateSpinLock( IN PNPROT_LOCK pLock, IN ULONG FileNumber, IN ULONG LineNumber ) { if (ndisprotdSpinLockInitDone == 0) { ndisprotdSpinLockInitDone = 1; NdisAllocateSpinLock(&(ndisprotdLockLock)); } NdisAcquireSpinLock(&(ndisprotdLockLock)); pLock->Signature = NUIOL_SIG; pLock->TouchedByFileNumber = FileNumber; pLock->TouchedInLineNumber = LineNumber; pLock->IsAcquired = 0; pLock->OwnerThread = 0; NdisAllocateSpinLock(&(pLock->NdisLock)); NdisReleaseSpinLock(&(ndisprotdLockLock)); } VOID ndisprotFreeSpinLock( IN PNPROT_LOCK pLock, IN ULONG FileNumber, IN ULONG LineNumber ) { NdisAcquireSpinLock(&(ndisprotdLockLock)); pLock->Signature = NUIOL_SIG; pLock->TouchedByFileNumber = FileNumber; pLock->TouchedInLineNumber = LineNumber; pLock->IsAcquired = 0; pLock->OwnerThread = 0; NdisFreeSpinLock(&(pLock->NdisLock)); NdisReleaseSpinLock(&(ndisprotdLockLock)); } VOID ndisprotFreeDbgLock( VOID ) { ASSERT(ndisprotdSpinLockInitDone == 1); ndisprotdSpinLockInitDone = 0; NdisFreeSpinLock(&(ndisprotdLockLock)); } VOID ndisprotAcquireSpinLock( IN PNPROT_LOCK pLock, IN ULONG FileNumber, IN ULONG LineNumber ) { PKTHREAD pThread; pThread = KeGetCurrentThread(); NdisAcquireSpinLock(&(ndisprotdLockLock)); if (pLock->Signature != NUIOL_SIG) { DbgPrint("Trying to acquire uninited lock 0x%x, File %c%c%c%c, Line %d/n", pLock, (CHAR)(FileNumber & 0xff), (CHAR)((FileNumber >> 8) & 0xff), (CHAR)((FileNumber >> 16) & 0xff), (CHAR)((FileNumber >> 24) & 0xff), LineNumber); DbgBreakPoint(); } if (pLock->IsAcquired != 0) { if (pLock->OwnerThread == pThread) { DbgPrint("Detected multiple locking!: pLock 0x%x, File %c%c%c%c, Line %d/n", pLock, (CHAR)(FileNumber & 0xff), (CHAR)((FileNumber >> 8) & 0xff), (CHAR)((FileNumber >> 16) & 0xff), (CHAR)((FileNumber >> 24) & 0xff), LineNumber); DbgPrint("pLock 0x%x already acquired in File %c%c%c%c, Line %d/n", pLock, (CHAR)(pLock->TouchedByFileNumber & 0xff), (CHAR)((pLock->TouchedByFileNumber >> 8) & 0xff), (CHAR)((pLock->TouchedByFileNumber >> 16) & 0xff), (CHAR)((pLock->TouchedByFileNumber >> 24) & 0xff), pLock->TouchedInLineNumber); DbgBreakPoint(); } } /// 因为加了判断,所以理论上这里pLock->IsAcquired只能加1次,然后就是减1 /// 不应该有更大的值 pLock->IsAcquired++; NdisReleaseSpinLock(&(ndisprotdLockLock)); NdisAcquireSpinLock(&(pLock->NdisLock)); // // Mark this lock. // pLock->OwnerThread = pThread; pLock->TouchedByFileNumber = FileNumber; pLock->TouchedInLineNumber = LineNumber; } VOID ndisprotReleaseSpinLock( IN PNPROT_LOCK pLock, IN ULONG FileNumber, IN ULONG LineNumber ) { NdisDprAcquireSpinLock(&(ndisprotdLockLock)); if (pLock->Signature != NUIOL_SIG) { DbgPrint("Trying to release uninited lock 0x%x, File %c%c%c%c, Line %d/n", pLock, (CHAR)(FileNumber & 0xff), (CHAR)((FileNumber >> 8) & 0xff), (CHAR)((FileNumber >> 16) & 0xff), (CHAR)((FileNumber >> 24) & 0xff), LineNumber); DbgBreakPoint(); } if (pLock->IsAcquired == 0) { DbgPrint("Detected release of unacquired lock 0x%x, File %c%c%c%c, Line %d/n", pLock, (CHAR)(FileNumber & 0xff), (CHAR)((FileNumber >> 8) & 0xff), (CHAR)((FileNumber >> 16) & 0xff), (CHAR)((FileNumber >> 24) & 0xff), LineNumber); DbgBreakPoint(); } pLock->TouchedByFileNumber = FileNumber; pLock->TouchedInLineNumber = LineNumber; /// 因为加了判断,所以理论上这里pLock->IsAcquired等于1 /// 不应该有更大的值 pLock->IsAcquired--; /// 因为不可能有ThreadID=0的线程,所以这里是设置了一个不可能的值 /// 便于加锁时和KeGetCurrentThread()做判断 pLock->OwnerThread = 0; /// 整个过程也都要用另外的锁保证不会重入 /// 内核编程和通常的Application还是有很大的不同的。 NdisDprReleaseSpinLock(&(ndisprotdLockLock)); NdisReleaseSpinLock(&(pLock->NdisLock)); } #endif // DBG_SPIN_LOCK