Leaked source code of windows server 2003
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.

1441 lines
40 KiB

  1. /***************************************************************************
  2. Copyright (c) 1998 Microsoft Corporation
  3. Module Name:
  4. DEBUGWDM.C
  5. Abstract:
  6. Debug and diagnostic routines for WDM driver
  7. Environment:
  8. Kernel mode only
  9. Notes:
  10. THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
  11. KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  12. IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
  13. PURPOSE.
  14. Copyright (c) 1998 Microsoft Corporation. All Rights Reserved.
  15. Revision History:
  16. 12/23/97 : created
  17. Author:
  18. Tom Green
  19. ****************************************************************************/
  20. #include <wdm.h>
  21. #include <ntddser.h>
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <usb.h>
  25. #include <usbdrivr.h>
  26. #include <usbdlib.h>
  27. #include <usbcomm.h>
  28. #ifdef WMI_SUPPORT
  29. #include <wmilib.h>
  30. #include <wmidata.h>
  31. #include <wmistr.h>
  32. #endif
  33. #include "usbser.h"
  34. #include "serioctl.h"
  35. #include "utils.h"
  36. #include "debugwdm.h"
  37. // memory allocation stats
  38. LOCAL ULONG MemoryAllocated = 0L;
  39. LOCAL ULONG MemAllocFailCnt = 0L;
  40. LOCAL ULONG MemAllocCnt = 0L;
  41. LOCAL ULONG MemFreeFailCnt = 0L;
  42. LOCAL ULONG MemFreeCnt = 0L;
  43. LOCAL ULONG MaxMemAllocated = 0L;
  44. // signature to write at end of allocated memory block
  45. #define MEM_ALLOC_SIGNATURE (ULONG) 'CLLA'
  46. // signature to write at end of freed memory block
  47. #define MEM_FREE_SIGNATURE (ULONG) 'EERF'
  48. #ifdef PROFILING_ENABLED
  49. #ifdef ALLOC_PRAGMA
  50. #pragma alloc_text(PAGE,Debug_OpenWDMDebug)
  51. #pragma alloc_text(PAGE,Debug_CloseWDMDebug)
  52. #pragma alloc_text(PAGE,Debug_SizeIRPHistoryTable)
  53. #pragma alloc_text(PAGE,Debug_SizeDebugPathHist)
  54. #pragma alloc_text(PAGE,Debug_SizeErrorLog)
  55. #pragma alloc_text(PAGE,Debug_ExtractAttachedDevices)
  56. #pragma alloc_text(PAGE,Debug_GetDriverInfo)
  57. #pragma alloc_text(PAGE,Debug_ExtractIRPHist)
  58. #pragma alloc_text(PAGE,Debug_ExtractPathHist)
  59. #pragma alloc_text(PAGE,Debug_ExtractErrorLog)
  60. #pragma alloc_text(PAGE,Debug_DumpDriverLog)
  61. #pragma alloc_text(PAGE,Debug_TranslateStatus)
  62. #pragma alloc_text(PAGE,Debug_TranslateIoctl)
  63. #endif // ALLOC_PRAGMA
  64. // data structures, macros, and data that the outside world doesn't need to know about
  65. // amount of data to save from IRP buffer
  66. #define IRP_DATA_SIZE 0x04
  67. // size for temporary string formatting buffers
  68. #define TMP_STR_BUFF_SIZE 0x100
  69. // initial number of entries in tables and logs
  70. #define DEFAULT_LOG_SIZE 64L
  71. // data structures for debug stuff
  72. // entry for IRP history table for IRPs going in and out
  73. typedef struct IRPHistory
  74. {
  75. LARGE_INTEGER TimeStamp;
  76. PDEVICE_OBJECT DeviceObject;
  77. PIRP Irp;
  78. ULONG MajorFunction;
  79. ULONG IrpByteCount;
  80. UCHAR IrpData[IRP_DATA_SIZE];
  81. UCHAR IrpDataCount;
  82. } IRPHist, *PIRPHist;
  83. // entry for execution tracing
  84. typedef struct PATHHistory
  85. {
  86. LARGE_INTEGER TimeStamp;
  87. PCHAR Path;
  88. } PATHHist, *PPATHHist;
  89. // entry for error log
  90. typedef struct ERRORLog
  91. {
  92. LARGE_INTEGER TimeStamp;
  93. NTSTATUS Status;
  94. } ERRLog, *PERRLog;
  95. // this is for translating a code into an ASCII string
  96. typedef struct Code2Ascii
  97. {
  98. NTSTATUS Code;
  99. PCHAR Str;
  100. } Code2Ascii;
  101. // local data for debug file
  102. // IRP history table components
  103. LOCAL PIRPHist IRPHistoryTable = NULL;
  104. LOCAL ULONG IRPHistoryIndex = 0L;
  105. GLOBAL ULONG IRPHistorySize = 0L;
  106. // Debug path storage
  107. LOCAL PPATHHist DebugPathHist = NULL;
  108. LOCAL ULONG DebugPathIndex = 0L;
  109. GLOBAL ULONG DebugPathSize = 0L;
  110. // Error log components
  111. LOCAL PERRLog ErrorLog = NULL;
  112. LOCAL ULONG ErrorLogIndex = 0L;
  113. GLOBAL ULONG ErrorLogSize = 0L;
  114. // this is for translating NT status codes into ASCII strings
  115. LOCAL Code2Ascii NTErrors[] =
  116. {
  117. STATUS_SUCCESS, "STATUS_SUCCESS",
  118. STATUS_PENDING, "STATUS_PENDING",
  119. STATUS_TIMEOUT, "STATUS_TIMEOUT",
  120. STATUS_DEVICE_BUSY, "STATUS_DEVICE_BUSY",
  121. STATUS_INSUFFICIENT_RESOURCES, "STATUS_INSUFFICIENT_RESOURCES",
  122. STATUS_INVALID_DEVICE_REQUEST, "STATUS_INVALID_DEVICE_REQUEST",
  123. STATUS_DEVICE_NOT_READY, "STATUS_DEVICE_NOT_READY",
  124. STATUS_INVALID_BUFFER_SIZE, "STATUS_INVALID_BUFFER_SIZE",
  125. STATUS_INVALID_PARAMETER, "STATUS_INVALID_PARAMETER",
  126. STATUS_INVALID_HANDLE, "STATUS_INVALID_HANDLE",
  127. STATUS_OBJECT_PATH_NOT_FOUND, "STATUS_OBJECT_PATH_NOT_FOUND",
  128. STATUS_BUFFER_TOO_SMALL, "STATUS_BUFFER_TOO_SMALL",
  129. STATUS_NOT_SUPPORTED, "STATUS_NOT_SUPPORTED",
  130. STATUS_DEVICE_DATA_ERROR, "STATUS_DEVICE_DATA_ERROR",
  131. STATUS_CANCELLED, "STATUS_CANCELLED",
  132. STATUS_OBJECT_NAME_INVALID, "STATUS_OBJECT_NAME_INVALID",
  133. STATUS_OBJECT_NAME_NOT_FOUND, "STATUS_OBJECT_NAME_NOT_FOUND"
  134. };
  135. LOCAL ULONG NumNTErrs = sizeof(NTErrors) / sizeof(Code2Ascii);
  136. LOCAL CHAR UnknownStatus[80];
  137. // this is for translating IOCTL codes into ASCII strings
  138. LOCAL Code2Ascii IoctlCodes[] =
  139. {
  140. IRP_MJ_CREATE, "CREATE",
  141. IRP_MJ_CREATE_NAMED_PIPE, "CNPIPE",
  142. IRP_MJ_CLOSE, "CLOSE ",
  143. IRP_MJ_READ, "READ ",
  144. IRP_MJ_WRITE, "WRITE ",
  145. IRP_MJ_QUERY_INFORMATION, "QRYINF",
  146. IRP_MJ_SET_INFORMATION, "SETINF",
  147. IRP_MJ_QUERY_EA, "QRYEA ",
  148. IRP_MJ_SET_EA, "SETEA ",
  149. IRP_MJ_FLUSH_BUFFERS, "FLSBUF",
  150. IRP_MJ_QUERY_VOLUME_INFORMATION, "QRYVOL",
  151. IRP_MJ_SET_VOLUME_INFORMATION, "SETVOL",
  152. IRP_MJ_DIRECTORY_CONTROL, "DIRCTL",
  153. IRP_MJ_FILE_SYSTEM_CONTROL, "SYSCTL",
  154. IRP_MJ_DEVICE_CONTROL, "DEVCTL",
  155. IRP_MJ_INTERNAL_DEVICE_CONTROL, "INDVCT",
  156. IRP_MJ_SHUTDOWN, "SHTDWN",
  157. IRP_MJ_LOCK_CONTROL, "LOKCTL",
  158. IRP_MJ_CLEANUP, "CLNUP ",
  159. IRP_MJ_CREATE_MAILSLOT, "MAILSL",
  160. IRP_MJ_QUERY_SECURITY, "QRYSEC",
  161. IRP_MJ_SET_SECURITY, "SETSEC",
  162. IRP_MJ_SYSTEM_CONTROL, "SYSCTL",
  163. IRP_MJ_DEVICE_CHANGE, "DEVCHG",
  164. IRP_MJ_QUERY_QUOTA, "QRYQUO",
  165. IRP_MJ_SET_QUOTA, "SETQUO",
  166. IRP_MJ_POWER, "POWER ",
  167. IRP_MJ_PNP, "PNP ",
  168. IRP_MJ_MAXIMUM_FUNCTION, "MAXFNC"
  169. };
  170. LOCAL ULONG NumIoctl = sizeof(IoctlCodes) / sizeof(Code2Ascii);
  171. LOCAL CHAR UnknownIoctl[80];
  172. /************************************************************************/
  173. /* Debug_OpenWDMDebug */
  174. /************************************************************************/
  175. /* */
  176. /* Routine Description: */
  177. /* */
  178. /* Allocate resources and init history tables and logs. */
  179. /* */
  180. /* Arguments: */
  181. /* */
  182. /* VOID */
  183. /* */
  184. /* Return Value: */
  185. /* */
  186. /* NTSTATUS */
  187. /* */
  188. /************************************************************************/
  189. NTSTATUS
  190. Debug_OpenWDMDebug(VOID)
  191. {
  192. NTSTATUS NtStatus = STATUS_SUCCESS;
  193. PAGED_CODE();
  194. // allocate tables and logs
  195. NtStatus = Debug_SizeIRPHistoryTable(DEFAULT_LOG_SIZE);
  196. if(!NT_SUCCESS(NtStatus))
  197. {
  198. Debug_CloseWDMDebug();
  199. return NtStatus;
  200. }
  201. NtStatus = Debug_SizeDebugPathHist(DEFAULT_LOG_SIZE);
  202. if(!NT_SUCCESS(NtStatus))
  203. {
  204. Debug_CloseWDMDebug();
  205. return NtStatus;
  206. }
  207. NtStatus = Debug_SizeErrorLog(DEFAULT_LOG_SIZE);
  208. if(!NT_SUCCESS(NtStatus))
  209. {
  210. Debug_CloseWDMDebug();
  211. return NtStatus;
  212. }
  213. return NtStatus;
  214. } // Debug_OpenWDMDebug
  215. /************************************************************************/
  216. /* Debug_CloseWDMDebug */
  217. /************************************************************************/
  218. /* */
  219. /* Routine Description: */
  220. /* */
  221. /* Free up resources used for history tables and logs. */
  222. /* */
  223. /* Arguments: */
  224. /* */
  225. /* VOID */
  226. /* */
  227. /* Return Value: */
  228. /* */
  229. /* VOID */
  230. /* */
  231. /************************************************************************/
  232. VOID
  233. Debug_CloseWDMDebug(VOID)
  234. {
  235. PAGED_CODE();
  236. if(DebugPathHist)
  237. {
  238. DEBUG_MEMFREE(DebugPathHist);
  239. DebugPathHist = NULL;
  240. DebugPathSize = 0L;
  241. }
  242. if(IRPHistoryTable)
  243. {
  244. DEBUG_MEMFREE(IRPHistoryTable);
  245. IRPHistoryTable = NULL;
  246. IRPHistorySize = 0L;
  247. }
  248. if(ErrorLog)
  249. {
  250. DEBUG_MEMFREE(ErrorLog);
  251. ErrorLog = NULL;
  252. ErrorLogSize = 0L;
  253. }
  254. Debug_CheckAllocations();
  255. // see if we have a leak
  256. DEBUG_ASSERT("Memory Allocation Leak", MemAllocCnt == MemFreeCnt);
  257. } // Debug_CloseWDMDebug
  258. /************************************************************************/
  259. /* Debug_SizeIRPHistoryTable */
  260. /************************************************************************/
  261. /* */
  262. /* Routine Description: */
  263. /* */
  264. /* Allocate IRP history table */
  265. /* */
  266. /* Arguments: */
  267. /* */
  268. /* Size - number of entries in table */
  269. /* */
  270. /* Return Value: */
  271. /* */
  272. /* NTSTATUS */
  273. /* */
  274. /************************************************************************/
  275. NTSTATUS
  276. Debug_SizeIRPHistoryTable(IN ULONG Size)
  277. {
  278. NTSTATUS NtStatus = STATUS_SUCCESS;
  279. PAGED_CODE();
  280. // see if they are trying to set the same size
  281. if(Size == IRPHistorySize)
  282. return NtStatus;
  283. // get rid of old history table if we got one
  284. if(IRPHistoryTable)
  285. DEBUG_MEMFREE(IRPHistoryTable);
  286. IRPHistoryTable = NULL;
  287. IRPHistoryIndex = 0L;
  288. IRPHistorySize = 0L;
  289. if(Size != 0L)
  290. {
  291. IRPHistoryTable = DEBUG_MEMALLOC(NonPagedPool, sizeof(IRPHist) * Size);
  292. if(IRPHistoryTable == NULL)
  293. NtStatus = STATUS_INSUFFICIENT_RESOURCES;
  294. else
  295. {
  296. RtlZeroMemory(IRPHistoryTable, sizeof(IRPHist) * Size);
  297. IRPHistorySize = Size;
  298. }
  299. }
  300. return NtStatus;
  301. } // Debug_SizeIRPHistoryTable
  302. /************************************************************************/
  303. /* Debug_SizeDebugPathHist */
  304. /************************************************************************/
  305. /* */
  306. /* Routine Description: */
  307. /* */
  308. /* Allocate path history */
  309. /* */
  310. /* Arguments: */
  311. /* */
  312. /* Size - number of entries in history */
  313. /* */
  314. /* Return Value: */
  315. /* */
  316. /* NTSTATUS */
  317. /* */
  318. /************************************************************************/
  319. NTSTATUS
  320. Debug_SizeDebugPathHist(IN ULONG Size)
  321. {
  322. NTSTATUS NtStatus = STATUS_SUCCESS;
  323. PAGED_CODE();
  324. // see if they are trying to set the same size
  325. if(Size == DebugPathSize)
  326. return NtStatus;
  327. // get rid of old path history if we got one
  328. if(DebugPathHist)
  329. DEBUG_MEMFREE(DebugPathHist);
  330. DebugPathHist = NULL;
  331. DebugPathIndex = 0L;
  332. DebugPathSize = 0L;
  333. if(Size != 0L)
  334. {
  335. DebugPathHist = DEBUG_MEMALLOC(NonPagedPool, sizeof(PATHHist) * Size);
  336. if(DebugPathHist == NULL)
  337. NtStatus = STATUS_INSUFFICIENT_RESOURCES;
  338. else
  339. {
  340. RtlZeroMemory(DebugPathHist, sizeof(PATHHist) * Size);
  341. DebugPathSize = Size;
  342. }
  343. }
  344. return NtStatus;
  345. } // Debug_SizeDebugPathHist
  346. /************************************************************************/
  347. /* Debug_SizeErrorLog */
  348. /************************************************************************/
  349. /* */
  350. /* Routine Description: */
  351. /* */
  352. /* Allocate error log */
  353. /* */
  354. /* Arguments: */
  355. /* */
  356. /* Size - number of entries in error log */
  357. /* */
  358. /* Return Value: */
  359. /* */
  360. /* NTSTATUS */
  361. /* */
  362. /************************************************************************/
  363. NTSTATUS
  364. Debug_SizeErrorLog(IN ULONG Size)
  365. {
  366. NTSTATUS NtStatus = STATUS_SUCCESS;
  367. PAGED_CODE();
  368. // see if they are trying to set the same size
  369. if(Size == ErrorLogSize)
  370. return NtStatus;
  371. // get rid of old error log if we got one
  372. if(ErrorLog)
  373. DEBUG_MEMFREE(ErrorLog);
  374. ErrorLog = NULL;
  375. ErrorLogIndex = 0L;
  376. ErrorLogSize = 0L;
  377. if(Size != 0L)
  378. {
  379. ErrorLog = DEBUG_MEMALLOC(NonPagedPool, sizeof(ERRLog) * Size);
  380. // make sure we actually allocated some memory
  381. if(ErrorLog == NULL)
  382. NtStatus = STATUS_INSUFFICIENT_RESOURCES;
  383. else
  384. {
  385. RtlZeroMemory(ErrorLog, sizeof(ERRLog) * Size);
  386. ErrorLogSize = Size;
  387. }
  388. }
  389. return NtStatus;
  390. } // Debug_SizeErrorLog
  391. /************************************************************************/
  392. /* Debug_LogIrpHist */
  393. /************************************************************************/
  394. /* */
  395. /* Routine Description: */
  396. /* */
  397. /* Logs IRP history. These are timestamped and put in a */
  398. /* circular buffer for extraction later. */
  399. /* */
  400. /* Arguments: */
  401. /* */
  402. /* DeviceObject - pointer to device object. */
  403. /* Irp - pointer to IRP. */
  404. /* MajorFunction - major function of IRP. */
  405. /* IoBuffer - buffer for data passed in and out of driver. */
  406. /* BufferLen - length of data buffer. */
  407. /* */
  408. /* Return Value: */
  409. /* */
  410. /* VOID */
  411. /* */
  412. /************************************************************************/
  413. VOID
  414. Debug_LogIrpHist(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp,
  415. IN ULONG MajorFunction, IN PVOID IoBuffer, IN ULONG BufferLen)
  416. {
  417. PIRPHist IrpHist;
  418. // get pointer to current entry in IRP history table
  419. IrpHist = &IRPHistoryTable[IRPHistoryIndex++];
  420. // point to the next entry in the IRP history table
  421. IRPHistoryIndex %= IRPHistorySize;
  422. // get time stamp
  423. IrpHist->TimeStamp = KeQueryPerformanceCounter(NULL);
  424. // save IRP, device object, major function and first 8 bytes of data in buffer
  425. IrpHist->DeviceObject = DeviceObject;
  426. IrpHist->Irp = Irp;
  427. IrpHist->MajorFunction = MajorFunction;
  428. // copy any data if we have it
  429. IrpHist->IrpByteCount = BufferLen;
  430. IrpHist->IrpDataCount = (UCHAR) min(IRP_DATA_SIZE, BufferLen);
  431. if(BufferLen)
  432. *(ULONG *) IrpHist->IrpData = *(ULONG *) IoBuffer;
  433. } // Debug_LogIrpHist
  434. /************************************************************************/
  435. /* Debug_LogPath */
  436. /************************************************************************/
  437. /* */
  438. /* Routine Description: */
  439. /* */
  440. /* Logs execution path through code. These are timestamped and put */
  441. /* in a circular buffer for extraction later. Kernel print routines */
  442. /* are also called. */
  443. /* */
  444. /* DANGER DANGER Will Robinson - the argument to this must be a */
  445. /* const char pointer, */
  446. /* */
  447. /* Arguments: */
  448. /* */
  449. /* Path - Pointer to const char array that contains description of */
  450. /* of path. */
  451. /* */
  452. /* Return Value: */
  453. /* */
  454. /* VOID */
  455. /* */
  456. /************************************************************************/
  457. VOID
  458. Debug_LogPath(IN CHAR *Path)
  459. {
  460. PPATHHist PHist;
  461. // get pointer to current entry in path history
  462. PHist = &DebugPathHist[DebugPathIndex++];
  463. // point to the next entry in path trace
  464. DebugPathIndex %= DebugPathSize;
  465. // get time stamp
  466. PHist->TimeStamp = KeQueryPerformanceCounter(NULL);
  467. // save path string
  468. PHist->Path = Path;
  469. // now call kernel print routines
  470. DEBUG_TRACE2(("%s\n", Path));
  471. } // Debug_LogPath
  472. /************************************************************************/
  473. /* Debug_LogError */
  474. /************************************************************************/
  475. /* */
  476. /* Routine Description: */
  477. /* */
  478. /* Logs NTSTATUS type errors. These are timestamped and put in a */
  479. /* circular buffer for extraction later. */
  480. /* */
  481. /* Arguments: */
  482. /* */
  483. /* NtStatus - NTSTATUS error to log. */
  484. /* */
  485. /* Return Value: */
  486. /* */
  487. /* VOID */
  488. /* */
  489. /************************************************************************/
  490. VOID
  491. Debug_LogError(IN NTSTATUS NtStatus)
  492. {
  493. PERRLog ErrLog;
  494. // no error, so don't log
  495. if(NtStatus == STATUS_SUCCESS)
  496. return;
  497. // get pointer to current entry in error log
  498. ErrLog = &ErrorLog[ErrorLogIndex++];
  499. // point to the next entry in error log
  500. ErrorLogIndex %= ErrorLogSize;
  501. // get time stamp
  502. ErrLog->TimeStamp = KeQueryPerformanceCounter(NULL);
  503. // save status
  504. ErrLog->Status = NtStatus;
  505. } // Debug_LogError
  506. /************************************************************************/
  507. /* Debug_Trap */
  508. /************************************************************************/
  509. /* */
  510. /* Routine Description: */
  511. /* */
  512. /* Trap. Causes execution to halt after logging message. */
  513. /* */
  514. /* Arguments: */
  515. /* */
  516. /* TrapCause - pointer to char array that contains description */
  517. /* of cause of trap. */
  518. /* */
  519. /* Return Value: */
  520. /* */
  521. /* VOID */
  522. /* */
  523. /************************************************************************/
  524. VOID
  525. Debug_Trap(IN PCHAR TrapCause)
  526. {
  527. // log the path
  528. DEBUG_LOG_PATH("Debug_Trap: ");
  529. DEBUG_LOG_PATH(TrapCause);
  530. // kernel debugger print
  531. DEBUG_TRACE3(("Debug_Trap: "));
  532. DEBUG_TRACE3(("%s\n",TrapCause));
  533. // halt execution
  534. DEBUG_TRAP();
  535. } // Debug_TRAP
  536. /************************************************************************/
  537. /* Debug_Assert */
  538. /************************************************************************/
  539. /* */
  540. /* Routine Description: */
  541. /* */
  542. /* Assertion routine. */
  543. /* */
  544. /* Arguments: */
  545. /* */
  546. /* This should not be called directly. Use DEBUG_ASSERT macro. */
  547. /* */
  548. /* Return Value: */
  549. /* */
  550. /* VOID */
  551. /* */
  552. /************************************************************************/
  553. VOID
  554. Debug_Assert(IN PVOID FailedAssertion, IN PVOID FileName, IN ULONG LineNumber,
  555. IN PCHAR Message)
  556. {
  557. #if DBG
  558. // just call the assert routine
  559. RtlAssert(FailedAssertion, FileName, LineNumber, Message);
  560. #else
  561. DEBUG_TRAP();
  562. #endif
  563. } // Debug_Assert
  564. /************************************************************************/
  565. /* Debug_ExtractAttachedDevices */
  566. /************************************************************************/
  567. /* */
  568. /* Routine Description: */
  569. /* */
  570. /* Formats and places attached device info into a buffer. */
  571. /* */
  572. /* Arguments: */
  573. /* */
  574. /* DriverObject - pointer to driver object. */
  575. /* */
  576. /* Buffer - pointer to buffer to fill with IRP history. */
  577. /* BuffSize - size of Buffer. */
  578. /* */
  579. /* Return Value: */
  580. /* */
  581. /* ULONG - number of bytes written in buffer. */
  582. /* */
  583. /************************************************************************/
  584. ULONG
  585. Debug_ExtractAttachedDevices(IN PDRIVER_OBJECT DriverObject, OUT PCHAR Buffer, IN ULONG BuffSize)
  586. {
  587. PCHAR StrBuff;
  588. PDEVICE_EXTENSION DeviceExtension;
  589. PDEVICE_OBJECT DeviceObject;
  590. BOOLEAN Dev = FALSE;
  591. PAGED_CODE();
  592. // make sure we have a pointer and a number of bytes
  593. if(Buffer == NULL || BuffSize == 0L)
  594. return 0L;
  595. // allocate buffer for formatting strings
  596. StrBuff = DEBUG_MEMALLOC(NonPagedPool, TMP_STR_BUFF_SIZE);
  597. if(StrBuff == NULL)
  598. return 0L;
  599. // title
  600. sprintf(StrBuff, "\n\n\nAttached Devices\n\n");
  601. // make sure it fits in buffer
  602. if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
  603. strcat(Buffer, StrBuff);
  604. // columns
  605. sprintf(StrBuff, "Device Device Obj IRPs Complete Byte Count\n\n");
  606. // make sure it fits in buffer
  607. if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
  608. strcat(Buffer, StrBuff);
  609. // get the first device object
  610. DeviceObject = DriverObject->DeviceObject;
  611. // march through linked list of devices
  612. while(DeviceObject)
  613. {
  614. // found at least one device
  615. Dev = TRUE;
  616. // Get a pointer to the device extension
  617. DeviceExtension = DeviceObject->DeviceExtension;
  618. sprintf(StrBuff, "%-17s 0x%p 0x%08X 0x%08X%08X\n", &DeviceExtension->LinkName[12],
  619. DeviceObject, DeviceExtension->IRPCount,
  620. DeviceExtension->ByteCount.HighPart,
  621. DeviceExtension->ByteCount.LowPart);
  622. // make sure it fits in buffer
  623. if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
  624. strcat(Buffer, StrBuff);
  625. DeviceObject = DeviceObject->NextDevice;
  626. }
  627. // if we don't have any devices, say so, but this should never happen (I think)
  628. if(!Dev)
  629. {
  630. sprintf(StrBuff, "No attached devices\n");
  631. // make sure it fits in buffer
  632. if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
  633. strcat(Buffer, StrBuff);
  634. }
  635. DEBUG_MEMFREE(StrBuff);
  636. return strlen(Buffer);
  637. } // Debug_ExtractAttachedDevices
  638. /************************************************************************/
  639. /* Debug_GetDriverInfo */
  640. /************************************************************************/
  641. /* */
  642. /* Routine Description: */
  643. /* */
  644. /* Formats and places driver info into a buffer. */
  645. /* */
  646. /* Arguments: */
  647. /* */
  648. /* Buffer - pointer to buffer to fill with IRP history. */
  649. /* BuffSize - size of Buffer. */
  650. /* */
  651. /* Return Value: */
  652. /* */
  653. /* ULONG - number of bytes written in buffer. */
  654. /* */
  655. /************************************************************************/
  656. ULONG
  657. Debug_GetDriverInfo(OUT PCHAR Buffer, IN ULONG BuffSize)
  658. {
  659. PCHAR StrBuff;
  660. PAGED_CODE();
  661. // make sure we have a pointer and a number of bytes
  662. if(Buffer == NULL || BuffSize == 0L)
  663. return 0L;
  664. // allocate buffer for formatting strings
  665. StrBuff = DEBUG_MEMALLOC(NonPagedPool, TMP_STR_BUFF_SIZE);
  666. if(StrBuff == NULL)
  667. return 0L;
  668. // driver name and version
  669. sprintf(StrBuff, "\n\n\nDriver: %s\n\nVersion: %s\n\n", DriverName, DriverVersion);
  670. // make sure it fits in buffer
  671. if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
  672. strcat(Buffer, StrBuff);
  673. DEBUG_MEMFREE(StrBuff);
  674. return strlen(Buffer);
  675. } // Debug_GetDriverInfo
  676. /************************************************************************/
  677. /* Debug_ExtractIRPHist */
  678. /************************************************************************/
  679. /* */
  680. /* Routine Description: */
  681. /* */
  682. /* Formats and places IRP history info into a buffer. */
  683. /* */
  684. /* Arguments: */
  685. /* */
  686. /* Buffer - pointer to buffer to fill with IRP history. */
  687. /* BuffSize - size of Buffer. */
  688. /* */
  689. /* Return Value: */
  690. /* */
  691. /* ULONG - number of bytes written in buffer. */
  692. /* */
  693. /************************************************************************/
  694. ULONG
  695. Debug_ExtractIRPHist(OUT PCHAR Buffer, IN ULONG BuffSize)
  696. {
  697. ULONG Index, Size;
  698. PIRPHist IrpHist;
  699. PCHAR StrBuff;
  700. BOOLEAN Hist = FALSE;
  701. PAGED_CODE();
  702. // make sure we have a pointer and a number of bytes
  703. if(Buffer == NULL || BuffSize == 0L)
  704. return 0L;
  705. // allocate buffer for formatting strings
  706. StrBuff = DEBUG_MEMALLOC(NonPagedPool, TMP_STR_BUFF_SIZE);
  707. if(StrBuff == NULL)
  708. return 0L;
  709. // title
  710. sprintf(StrBuff, "\n\n\nIRP History\n\n");
  711. // make sure it fits in buffer
  712. if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
  713. strcat(Buffer, StrBuff);
  714. // see if error log is on
  715. if(IRPHistorySize == 0L)
  716. {
  717. sprintf(StrBuff, "IRP History is disabled\n");
  718. // make sure it fits in buffer
  719. if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
  720. strcat(Buffer, StrBuff);
  721. }
  722. else
  723. {
  724. // columns
  725. sprintf(StrBuff, "Time Stamp Device Obj IRP Func Byte Count Data\n\n");
  726. // make sure it fits in buffer
  727. if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
  728. strcat(Buffer, StrBuff);
  729. Index = IRPHistoryIndex;
  730. for(Size = 0; Size < IRPHistorySize; Size++)
  731. {
  732. // get pointer to current entry in IRP history table
  733. IrpHist = &IRPHistoryTable[Index++];
  734. // parse timestamp and IRP history and write to buffer
  735. if(IrpHist->TimeStamp.LowPart)
  736. {
  737. UCHAR DataCount;
  738. CHAR DataBuff[10];
  739. // we have at least one entry
  740. Hist = TRUE;
  741. sprintf(StrBuff, "0x%08X%08X 0x%p 0x%p %s 0x%08X ",
  742. IrpHist->TimeStamp.HighPart, IrpHist->TimeStamp.LowPart,
  743. IrpHist->DeviceObject, IrpHist->Irp,
  744. Debug_TranslateIoctl(IrpHist->MajorFunction),
  745. IrpHist->IrpByteCount);
  746. // add data bytes if we got them
  747. for(DataCount = 0; DataCount < IrpHist->IrpDataCount; DataCount++)
  748. {
  749. sprintf(DataBuff, "%02x ", IrpHist->IrpData[DataCount]);
  750. strcat(StrBuff, DataBuff);
  751. }
  752. sprintf(DataBuff, "\n");
  753. strcat(StrBuff, DataBuff);
  754. // make sure it fits in buffer
  755. if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
  756. strcat(Buffer, StrBuff);
  757. }
  758. // point to the next entry in the IRP history table
  759. Index %= IRPHistorySize;
  760. }
  761. // if we don't have history, say so, but this should never happen (I think)
  762. if(!Hist)
  763. {
  764. sprintf(StrBuff, "No IRP history\n");
  765. // make sure it fits in buffer
  766. if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
  767. strcat(Buffer, StrBuff);
  768. }
  769. }
  770. DEBUG_MEMFREE(StrBuff);
  771. return strlen(Buffer);
  772. } // Debug_ExtractIRPHist
  773. /************************************************************************/
  774. /* Debug_ExtractPathHist */
  775. /************************************************************************/
  776. /* */
  777. /* Routine Description: */
  778. /* */
  779. /* Formats and places path history info into buffer. */
  780. /* */
  781. /* Arguments: */
  782. /* */
  783. /* Buffer - pointer to buffer to fill with path history. */
  784. /* BuffSize - size of Buffer. */
  785. /* */
  786. /* Return Value: */
  787. /* */
  788. /* ULONG - number of bytes written in buffer. */
  789. /* */
  790. /************************************************************************/
  791. ULONG
  792. Debug_ExtractPathHist(OUT PCHAR Buffer, IN ULONG BuffSize)
  793. {
  794. ULONG Index, Size;
  795. PPATHHist PHist;
  796. PCHAR StrBuff;
  797. BOOLEAN Hist = FALSE;
  798. PAGED_CODE();
  799. // make sure we have a pointer and a number of bytes
  800. if(Buffer == NULL || BuffSize == 0L)
  801. return 0L;
  802. // allocate buffer for formatting strings
  803. StrBuff = DEBUG_MEMALLOC(NonPagedPool, TMP_STR_BUFF_SIZE);
  804. if(StrBuff == NULL)
  805. return 0L;
  806. // title
  807. sprintf(StrBuff, "\n\n\nExecution Path History\n\n");
  808. // make sure it fits in buffer
  809. if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
  810. strcat(Buffer, StrBuff);
  811. // see if path history is on
  812. if(DebugPathSize == 0L)
  813. {
  814. sprintf(StrBuff, "Path History is disabled\n");
  815. // make sure it fits in buffer
  816. if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
  817. strcat(Buffer, StrBuff);
  818. }
  819. else
  820. {
  821. // columns
  822. sprintf(StrBuff, "Time Stamp Path\n\n");
  823. // make sure it fits in buffer
  824. if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
  825. strcat(Buffer, StrBuff);
  826. Index = DebugPathIndex;
  827. for(Size = 0; Size < DebugPathSize; Size++)
  828. {
  829. // get pointer to current entry in path history
  830. PHist = &DebugPathHist[Index++];
  831. // parse timestamp and path and write to buffer. Check for NULL entries
  832. if(PHist->TimeStamp.LowPart)
  833. {
  834. // at least we have one entry
  835. Hist = TRUE;
  836. sprintf(StrBuff, "0x%08X%08X %s\n", PHist->TimeStamp.HighPart,
  837. PHist->TimeStamp.LowPart, PHist->Path);
  838. // make sure it fits in buffer
  839. if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
  840. strcat(Buffer, StrBuff);
  841. }
  842. // point to the next entry in path trace
  843. Index %= DebugPathSize;
  844. }
  845. // if we don't have history, say so, but this should never happen (I think)
  846. if(!Hist)
  847. {
  848. sprintf(StrBuff, "No execution path history\n");
  849. // make sure it fits in buffer
  850. if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
  851. strcat(Buffer, StrBuff);
  852. }
  853. }
  854. DEBUG_MEMFREE(StrBuff);
  855. return strlen(Buffer);
  856. } // Debug_ExtractPathHist
  857. /************************************************************************/
  858. /* Debug_ExtractErrorLog */
  859. /************************************************************************/
  860. /* */
  861. /* Routine Description: */
  862. /* */
  863. /* Formats and places error log info into buffer. */
  864. /* */
  865. /* Arguments: */
  866. /* */
  867. /* Buffer - pointer to buffer to fill with IRP history. */
  868. /* BuffSize - size of Buffer. */
  869. /* */
  870. /* Return Value: */
  871. /* */
  872. /* ULONG - number of bytes written in buffer. */
  873. /* */
  874. /************************************************************************/
  875. ULONG
  876. Debug_ExtractErrorLog(OUT PCHAR Buffer, IN ULONG BuffSize)
  877. {
  878. ULONG Index, Size;
  879. PERRLog ErrLog;
  880. PCHAR StrBuff;
  881. BOOLEAN Errors = FALSE;
  882. PAGED_CODE();
  883. // make sure we have a pointer and a number of bytes
  884. if(Buffer == NULL || BuffSize == 0L)
  885. return 0L;
  886. // allocate buffer for formatting strings
  887. StrBuff = DEBUG_MEMALLOC(NonPagedPool, TMP_STR_BUFF_SIZE);
  888. if(StrBuff == NULL)
  889. return 0L;
  890. // title
  891. sprintf(StrBuff, "\n\n\nError Log\n\n");
  892. // make sure it fits in buffer
  893. if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
  894. strcat(Buffer, StrBuff);
  895. // see if error log is on
  896. if(ErrorLogSize == 0L)
  897. {
  898. sprintf(StrBuff, "Error Log is disabled\n");
  899. // make sure it fits in buffer
  900. if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
  901. strcat(Buffer, StrBuff);
  902. }
  903. else
  904. {
  905. // columns
  906. sprintf(StrBuff, "Time Stamp Error\n\n");
  907. // make sure it fits in buffer
  908. if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
  909. strcat(Buffer, StrBuff);
  910. Index = ErrorLogIndex;
  911. for(Size = 0; Size < ErrorLogSize; Size++)
  912. {
  913. // get pointer to current entry in error log
  914. ErrLog = &ErrorLog[Index++];
  915. // parse timestamp and error and write to buffer
  916. if(ErrLog->TimeStamp.LowPart)
  917. {
  918. // we have at least one error
  919. Errors = TRUE;
  920. sprintf(StrBuff, "0x%08X%08X %s\n", ErrLog->TimeStamp.HighPart,
  921. ErrLog->TimeStamp.LowPart, Debug_TranslateStatus(ErrLog->Status));
  922. // make sure it fits in buffer
  923. if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
  924. strcat(Buffer, StrBuff);
  925. }
  926. // point at next entry
  927. Index %= ErrorLogSize;
  928. }
  929. // if we don't have errors, say so
  930. if(!Errors)
  931. {
  932. sprintf(StrBuff, "No errors in log\n");
  933. // make sure it fits in buffer
  934. if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
  935. strcat(Buffer, StrBuff);
  936. }
  937. }
  938. DEBUG_MEMFREE(StrBuff);
  939. return strlen(Buffer);
  940. } // Debug_ExtractErrorLog
  941. /************************************************************************/
  942. /* Debug_DumpDriverLog */
  943. /************************************************************************/
  944. /* */
  945. /* Routine Description: */
  946. /* */
  947. /* Dumps all history and logging to buffer. */
  948. /* */
  949. /* Arguments: */
  950. /* */
  951. /* DeviceObject - pointer to device object. */
  952. /* */
  953. /* Buffer - pointer to buffer to fill with IRP history. */
  954. /* BuffSize - size of pBuffer. */
  955. /* */
  956. /* Return Value: */
  957. /* */
  958. /* ULONG - number of bytes written in buffer. */
  959. /* */
  960. /************************************************************************/
  961. ULONG
  962. Debug_DumpDriverLog(IN PDEVICE_OBJECT DeviceObject, OUT PCHAR Buffer, IN ULONG BuffSize)
  963. {
  964. PCHAR StrBuff;
  965. PAGED_CODE();
  966. // make sure we have a pointer and a number of bytes
  967. if(Buffer == NULL || BuffSize == 0L)
  968. return 0L;
  969. // allocate buffer for formatting strings
  970. StrBuff = DEBUG_MEMALLOC(NonPagedPool, TMP_STR_BUFF_SIZE);
  971. if(StrBuff == NULL)
  972. return 0L;
  973. // driver name and version, memory allocated
  974. sprintf(StrBuff, "\n\n\nDriver: %s\n\nVersion: %s\n\nMemory Allocated: 0x%08X\nMaximum Memory Allocated: 0x%08X\n",
  975. DriverName, DriverVersion, MemoryAllocated, MaxMemAllocated);
  976. // make sure it fits in buffer
  977. if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
  978. strcat(Buffer, StrBuff);
  979. // memory allocation stats
  980. sprintf(StrBuff, "MemAlloc Count: 0x%08X\nMemFree Count: 0x%08X\nMemAlloc Fail Count: 0x%08X\nMemFree Fail Count: 0x%08X\n",
  981. MemAllocCnt, MemFreeCnt, MemAllocFailCnt, MemFreeFailCnt);
  982. // make sure it fits in buffer
  983. if((strlen(Buffer) + strlen(StrBuff)) < BuffSize)
  984. strcat(Buffer, StrBuff);
  985. // get attached devices
  986. Debug_ExtractAttachedDevices(DeviceObject->DriverObject, Buffer, BuffSize);
  987. // get IRP history
  988. Debug_ExtractIRPHist(Buffer, BuffSize);
  989. // get execution path history
  990. Debug_ExtractPathHist(Buffer, BuffSize);
  991. // get error log
  992. Debug_ExtractErrorLog(Buffer, BuffSize);
  993. DEBUG_MEMFREE(StrBuff);
  994. return strlen(Buffer);
  995. } // Debug_DumpDriverLog
  996. /************************************************************************/
  997. /* Debug_TranslateStatus */
  998. /************************************************************************/
  999. /* */
  1000. /* Routine Description: */
  1001. /* */
  1002. /* Translates NTSTATUS into ASCII string. */
  1003. /* */
  1004. /* Arguments: */
  1005. /* */
  1006. /* NtStatus - NTSTATUS code. */
  1007. /* */
  1008. /* Return Value: */
  1009. /* */
  1010. /* PCHAR - pointer to error string. */
  1011. /* */
  1012. /************************************************************************/
  1013. PCHAR
  1014. Debug_TranslateStatus(IN NTSTATUS NtStatus)
  1015. {
  1016. ULONG Err;
  1017. PAGED_CODE();
  1018. for(Err = 0; Err < NumNTErrs; Err++)
  1019. {
  1020. if(NtStatus == NTErrors[Err].Code)
  1021. return NTErrors[Err].Str;
  1022. }
  1023. // fell through, not an error we handle
  1024. sprintf(UnknownStatus, "Unknown error 0x%08X", NtStatus);
  1025. return UnknownStatus;
  1026. } // Debug_TranslateStatus
  1027. /************************************************************************/
  1028. /* Debug_TranslateIoctl */
  1029. /************************************************************************/
  1030. /* */
  1031. /* Routine Description: */
  1032. /* */
  1033. /* Translates IOCTL into ASCII string. */
  1034. /* */
  1035. /* Arguments: */
  1036. /* */
  1037. /* Ioctl - ioctl code. */
  1038. /* */
  1039. /* Return Value: */
  1040. /* */
  1041. /* PCHAR - pointer to error string. */
  1042. /* */
  1043. /************************************************************************/
  1044. PCHAR
  1045. Debug_TranslateIoctl(IN LONG Ioctl)
  1046. {
  1047. ULONG Index;
  1048. PAGED_CODE();
  1049. // it's kind of repetitive to search at this point, but just in case
  1050. // they change the actual IOCTLs we will be covered
  1051. for(Index = 0; Index < NumIoctl; Index++)
  1052. {
  1053. if(Ioctl == IoctlCodes[Index].Code)
  1054. return IoctlCodes[Index].Str;
  1055. }
  1056. // fell through, not an error we handle
  1057. sprintf(UnknownIoctl, "0x%04X", Ioctl);
  1058. return UnknownIoctl;
  1059. } // Debug_TranslateIoctl
  1060. #endif // PROFILING_ENABLED
  1061. VOID
  1062. Debug_CheckAllocations(VOID)
  1063. {
  1064. DEBUG_TRACE1(("MemoryAllocated = 0x%08X\n", MemoryAllocated));
  1065. DEBUG_TRACE1(("MemAllocCnt = 0x%08X MemFreeCnt = 0x%08X\n",
  1066. MemAllocCnt, MemFreeCnt));
  1067. } // Debug_CheckAllocations
  1068. /************************************************************************/
  1069. /* Debug_MemAlloc */
  1070. /************************************************************************/
  1071. /* */
  1072. /* Routine Description: */
  1073. /* */
  1074. /* Allocates block of memory. Stores length of block and a */
  1075. /* signature ULONG for keeping track of amount of memory allocated */
  1076. /* and checking for bogus calls to Debug_MemFree. The signature */
  1077. /* can also be used to determine if someone has written past the */
  1078. /* end of the block. */
  1079. /* */
  1080. /* Arguments: */
  1081. /* */
  1082. /* PoolType - Pool to allocate memory from. */
  1083. /* NumberOfBytes - Number of bytes to allocate. */
  1084. /* */
  1085. /* Return Value: */
  1086. /* */
  1087. /* PVOID - pointer to allocated memory. */
  1088. /* */
  1089. /************************************************************************/
  1090. PVOID
  1091. Debug_MemAlloc(IN POOL_TYPE PoolType, IN ULONG NumberOfBytes)
  1092. {
  1093. #ifdef _WIN64
  1094. return ExAllocatePool(PoolType, NumberOfBytes);
  1095. #else
  1096. PULONG Mem;
  1097. // allocate memory plus a little extra for our own use
  1098. Mem = ExAllocatePool(PoolType, NumberOfBytes + (2 * sizeof(ULONG)));
  1099. // see if we actually allocated any memory
  1100. if(Mem)
  1101. {
  1102. // keep track of how much we allocated
  1103. MemoryAllocated += NumberOfBytes;
  1104. // see if we have a new maximum
  1105. if(MemoryAllocated > MaxMemAllocated)
  1106. MaxMemAllocated = MemoryAllocated;
  1107. // store number of bytes allocated at start of memory allocated
  1108. *Mem++ = NumberOfBytes;
  1109. // now we are pointing at the memory allocated for caller
  1110. // put signature word at end
  1111. // get new pointer that points to end of buffer - ULONG
  1112. Mem = (PULONG) (((PUCHAR) Mem) + NumberOfBytes);
  1113. // write signature
  1114. *Mem = MEM_ALLOC_SIGNATURE;
  1115. // get back pointer to return to caller
  1116. Mem = (PULONG) (((PUCHAR) Mem) - NumberOfBytes);
  1117. // log stats
  1118. MemAllocCnt++;
  1119. }
  1120. else
  1121. // failed, log stats
  1122. MemAllocFailCnt++;
  1123. return (PVOID) Mem;
  1124. #endif
  1125. } // Debug_MemAlloc
  1126. /************************************************************************/
  1127. /* Debug_MemFree */
  1128. /************************************************************************/
  1129. /* */
  1130. /* Routine Description: */
  1131. /* */
  1132. /* Frees memory allocated in call to Debug_MemAlloc. Checks for */
  1133. /* signature ULONG at the end of allocated memory to make sure */
  1134. /* this is a valid block to free. */
  1135. /* */
  1136. /* Arguments: */
  1137. /* */
  1138. /* Mem - pointer to allocated block to free */
  1139. /* */
  1140. /* Return Value: */
  1141. /* */
  1142. /* VOID. Traps if it is an invalid block. */
  1143. /* */
  1144. /************************************************************************/
  1145. VOID
  1146. Debug_MemFree(IN PVOID Mem)
  1147. {
  1148. #ifdef _WIN64
  1149. ExFreePool(Mem);
  1150. #else
  1151. PULONG Tmp = (PULONG) Mem;
  1152. ULONG BuffSize;
  1153. // point at size ULONG at start of buffer, and address to free
  1154. Tmp--;
  1155. // get the size of memory allocated by caller
  1156. BuffSize = *Tmp;
  1157. // point at signature and make sure it's O.K.
  1158. ((PCHAR) Mem) += BuffSize;
  1159. if(*((PULONG) Mem) == MEM_ALLOC_SIGNATURE)
  1160. {
  1161. // let's go ahead and get rid of signature in case we get called
  1162. // with this pointer again and memory is still paged in
  1163. *((PULONG) Mem) = MEM_FREE_SIGNATURE;
  1164. // adjust amount of memory allocated
  1165. MemoryAllocated -= BuffSize;
  1166. // free real pointer
  1167. ExFreePool(Tmp);
  1168. // log stats
  1169. MemFreeCnt++;
  1170. }
  1171. else
  1172. {
  1173. // not a real allocated block, or someone wrote past the end
  1174. MemFreeFailCnt++;
  1175. DEBUG_TRAP();
  1176. }
  1177. #endif
  1178. } // Debug_MemFree