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.

499 lines
13 KiB

  1. /*++
  2. Copyright (c) 1998-2002 Microsoft Corporation
  3. Module Name:
  4. strlog.c
  5. Abstract:
  6. This module implements a string log.
  7. A string log is a fast, in-memory, thread-safe activity log of
  8. variable-length strings. It's modelled on the tracelog code.
  9. Author:
  10. George V. Reilly (GeorgeRe) 23-Jul-2001
  11. Revision History:
  12. --*/
  13. #include "precomp.h"
  14. #include "strlogp.h"
  15. ULONG g_StringLogDbgPrint = 0;
  16. /***************************************************************************++
  17. Routine Description:
  18. Creates a new (empty) string log.
  19. Arguments:
  20. LogSize - Supplies the number of bytes in the string buffer.
  21. ExtraBytesInHeader - Supplies the number of extra bytes to include
  22. in the log header. This is useful for adding application-specific
  23. data to the log.
  24. Return Value:
  25. PSTRING_LOG - Pointer to the newly created log if successful,
  26. NULL otherwise.
  27. --***************************************************************************/
  28. PSTRING_LOG
  29. CreateStringLog(
  30. IN ULONG LogSize,
  31. IN ULONG ExtraBytesInHeader,
  32. BOOLEAN EchoDbgPrint
  33. )
  34. {
  35. ULONG TotalHeaderSize;
  36. PSTRING_LOG pLog;
  37. PUCHAR pLogBuffer;
  38. if (LogSize >= 20 * 1024 * 1024)
  39. return NULL;
  40. //
  41. // Round up to page size
  42. //
  43. LogSize = (LogSize + (PAGE_SIZE-1)) & ~(PAGE_SIZE-1);
  44. //
  45. // Allocate & initialize the log structure.
  46. //
  47. TotalHeaderSize = sizeof(*pLog) + ExtraBytesInHeader;
  48. pLogBuffer = (PUCHAR) ExAllocatePoolWithTag(
  49. NonPagedPool,
  50. LogSize,
  51. UL_STRING_LOG_BUFFER_POOL_TAG
  52. );
  53. if (pLogBuffer == NULL)
  54. return NULL;
  55. pLog = (PSTRING_LOG) ExAllocatePoolWithTag(
  56. NonPagedPool,
  57. TotalHeaderSize,
  58. UL_STRING_LOG_POOL_TAG
  59. );
  60. //
  61. // Initialize it.
  62. //
  63. if (pLog != NULL)
  64. {
  65. RtlZeroMemory( pLog, TotalHeaderSize );
  66. pLog->Signature = STRING_LOG_SIGNATURE;
  67. pLog->pLogBuffer = pLogBuffer;
  68. pLog->LogSize = LogSize;
  69. pLog->EchoDbgPrint = EchoDbgPrint;
  70. KeInitializeSpinLock(&pLog->SpinLock);
  71. ResetStringLog(pLog);
  72. }
  73. else
  74. {
  75. ExFreePoolWithTag( pLogBuffer, UL_STRING_LOG_BUFFER_POOL_TAG );
  76. }
  77. return pLog;
  78. } // CreateStringLog
  79. /***************************************************************************++
  80. Routine Description:
  81. Resets the specified string log such that the next entry written
  82. will be placed at the beginning of the log.
  83. Arguments:
  84. pLog - Supplies the string log to reset.
  85. --***************************************************************************/
  86. VOID
  87. ResetStringLog(
  88. IN PSTRING_LOG pLog
  89. )
  90. {
  91. // Keep this in sync with !ulkd.strlog -r
  92. if (pLog != NULL)
  93. {
  94. PSTRING_LOG_MULTI_ENTRY pMultiEntry
  95. = (PSTRING_LOG_MULTI_ENTRY) pLog->pLogBuffer;
  96. KIRQL OldIrql;
  97. KeAcquireSpinLock(&pLog->SpinLock, &OldIrql);
  98. ASSERT( pLog->Signature == STRING_LOG_SIGNATURE );
  99. RtlZeroMemory(pLog->pLogBuffer, pLog->LogSize);
  100. pLog->NextEntry = 0;
  101. pLog->LastEntryLength = 0;
  102. pLog->WrapAroundCount = 0;
  103. //
  104. // Write an initial multi-entry record at the very beginning
  105. // of the log buffer. When we wraparound, we always place a
  106. // multi-entry record at the beginning of the log buffer.
  107. // Having this invariant makes !ulkd.strlog simpler.
  108. //
  109. pMultiEntry->Signature = STRING_LOG_ENTRY_MULTI_SIGNATURE;
  110. pMultiEntry->NumEntries = 0;
  111. pMultiEntry->PrevDelta = 0;
  112. ++pMultiEntry;
  113. pMultiEntry->Signature = STRING_LOG_ENTRY_LAST_SIGNATURE;
  114. pLog->MultiOffset = 0;
  115. pLog->Offset = sizeof(STRING_LOG_MULTI_ENTRY);
  116. pLog->MultiByteCount = sizeof(STRING_LOG_MULTI_ENTRY);
  117. pLog->MultiNumEntries = 0;
  118. pLog->InitialTimeStamp.QuadPart = 0;
  119. KeReleaseSpinLock(&pLog->SpinLock, OldIrql);
  120. }
  121. } // ResetStringLog
  122. /***************************************************************************++
  123. Routine Description:
  124. Destroys a string log created with CreateStringLog().
  125. Arguments:
  126. pLog - Supplies the string log to destroy.
  127. --***************************************************************************/
  128. VOID
  129. DestroyStringLog(
  130. IN PSTRING_LOG pLog
  131. )
  132. {
  133. if (pLog != NULL)
  134. {
  135. ASSERT( pLog->Signature == STRING_LOG_SIGNATURE );
  136. pLog->Signature = STRING_LOG_SIGNATURE_X;
  137. ExFreePoolWithTag( pLog->pLogBuffer, UL_STRING_LOG_BUFFER_POOL_TAG );
  138. ExFreePoolWithTag( pLog, UL_STRING_LOG_POOL_TAG );
  139. }
  140. } // DestroyStringLog
  141. /***************************************************************************++
  142. Routine Description:
  143. Writes a new entry to the specified string log.
  144. Arguments:
  145. pLog - Supplies the log to write to.
  146. Format - printf-style format string
  147. arglist - va_list bundling up the arguments
  148. Return Value:
  149. LONGLONG - Index of the newly written entry
  150. --***************************************************************************/
  151. LONGLONG
  152. __cdecl
  153. WriteStringLogVaList(
  154. IN PSTRING_LOG pLog,
  155. IN PCH Format,
  156. IN va_list arglist
  157. )
  158. {
  159. UCHAR Buffer[PRINTF_BUFFER_LEN];
  160. PUCHAR pTarget;
  161. int cb;
  162. ULONG i;
  163. ULONG cb2;
  164. ULONG PrevDelta;
  165. ULONG MultiPrevDelta;
  166. PSTRING_LOG_ENTRY pEntry;
  167. LONGLONG index;
  168. KIRQL OldIrql;
  169. BOOLEAN NeedMultiEntry = FALSE;
  170. LARGE_INTEGER TimeStamp;
  171. ASSERT( pLog->Signature == STRING_LOG_SIGNATURE );
  172. cb = _vsnprintf((char*) Buffer, sizeof(Buffer), Format, arglist);
  173. //
  174. // Local Buffer overflow?
  175. //
  176. if (cb < 0)
  177. {
  178. cb = sizeof(Buffer);
  179. }
  180. // _vsnprintf doesn't always NUL-terminate the buffer
  181. Buffer[DIMENSION(Buffer)-1] = '\0';
  182. if (pLog->EchoDbgPrint)
  183. DbgPrint("%s", (PCH) Buffer);
  184. //
  185. // Add 1 to 4 bytes of zeroes at end to terminate string,
  186. // then round up to ULONG alignment
  187. //
  188. cb2 = ((sizeof(STRING_LOG_ENTRY) + cb + sizeof(ULONG))
  189. & ~(sizeof(ULONG) - 1));
  190. ASSERT(cb2 < 0x10000); // Must fit in a USHORT
  191. //
  192. // Find the next slot, copy the entry to the slot.
  193. //
  194. KeQuerySystemTime(&TimeStamp);
  195. KeAcquireSpinLock(&pLog->SpinLock, &OldIrql);
  196. if (0 == pLog->InitialTimeStamp.QuadPart)
  197. pLog->InitialTimeStamp = TimeStamp;
  198. TimeStamp.QuadPart -= pLog->InitialTimeStamp.QuadPart;
  199. index = pLog->NextEntry++;
  200. PrevDelta = pLog->LastEntryLength;
  201. MultiPrevDelta = pLog->MultiByteCount;
  202. pLog->LastEntryLength = (USHORT) cb2;
  203. ASSERT(pLog->Offset <= pLog->LogSize);
  204. //
  205. // Handle wraparound of the log buffer. Since LogSize is typically much
  206. // larger than PRINTF_BUFFER_LEN, this is an infrequent operation.
  207. // Must have enough space for all of the regular STRING_LOG_ENTRY,
  208. // a multi STRING_LOG_ENTRY, and the zero-terminated string itself.
  209. //
  210. if (pLog->Offset + cb2 + sizeof(STRING_LOG_ENTRY) >= pLog->LogSize)
  211. {
  212. ULONG WastedSpace = pLog->LogSize - pLog->Offset;
  213. ASSERT(WastedSpace > 0);
  214. // Clear to the end of the log buffer
  215. for (i = 0; i < WastedSpace; i += sizeof(ULONG))
  216. {
  217. PULONG pul = (PULONG) (pLog->pLogBuffer + pLog->Offset + i);
  218. ASSERT(((ULONG_PTR) pul & (sizeof(ULONG) - 1)) == 0);
  219. *pul = STRING_LOG_ENTRY_EOB_SIGNATURE;
  220. }
  221. // Reset to the beginning
  222. pLog->Offset = 0;
  223. ++pLog->WrapAroundCount;
  224. PrevDelta += WastedSpace;
  225. MultiPrevDelta += WastedSpace;
  226. // Always want a multi-entry record at the beginning of the log buffer
  227. NeedMultiEntry = TRUE;
  228. }
  229. else if (pLog->MultiNumEntries >= STRING_LOG_MULTIPLE_ENTRIES)
  230. {
  231. NeedMultiEntry = TRUE;
  232. }
  233. else
  234. {
  235. ++pLog->MultiNumEntries;
  236. }
  237. //
  238. // If we've had STRING_LOG_MULTIPLE_ENTRIES regular entries since the
  239. // last multi-entry or if we've wrapped around the beginning of the
  240. // log buffer, we need a new multi-entry.
  241. //
  242. if (NeedMultiEntry)
  243. {
  244. PSTRING_LOG_MULTI_ENTRY pMultiEntry;
  245. pTarget = pLog->pLogBuffer + pLog->Offset;
  246. ASSERT(((ULONG_PTR) pTarget & (sizeof(ULONG) - 1)) == 0);
  247. pMultiEntry = (PSTRING_LOG_MULTI_ENTRY) pTarget;
  248. pMultiEntry->Signature = STRING_LOG_ENTRY_MULTI_SIGNATURE;
  249. ASSERT(pLog->MultiNumEntries <= STRING_LOG_MULTIPLE_ENTRIES);
  250. pMultiEntry->NumEntries = pLog->MultiNumEntries;
  251. pLog->MultiNumEntries = 1; // for the entry generated below
  252. ASSERT(MultiPrevDelta < 0x10000);
  253. pMultiEntry->PrevDelta = (USHORT) MultiPrevDelta;
  254. pLog->MultiOffset = pLog->Offset;
  255. pLog->MultiByteCount = sizeof(STRING_LOG_MULTI_ENTRY);
  256. pLog->Offset += sizeof(STRING_LOG_MULTI_ENTRY);
  257. PrevDelta += sizeof(STRING_LOG_MULTI_ENTRY);
  258. }
  259. pTarget = pLog->pLogBuffer + pLog->Offset;
  260. ASSERT(((ULONG_PTR) pTarget & (sizeof(ULONG) - 1)) == 0);
  261. pLog->MultiByteCount = (USHORT) (pLog->MultiByteCount + (USHORT) cb2);
  262. pLog->Offset += (USHORT) cb2;
  263. ASSERT(pLog->Offset <= pLog->LogSize);
  264. ASSERT(pLog->pLogBuffer <= pTarget
  265. && pTarget + cb2 < pLog->pLogBuffer + pLog->LogSize);
  266. // Put a special signature where the next entry will start
  267. *(PULONG) (pTarget + cb2) = STRING_LOG_ENTRY_LAST_SIGNATURE;
  268. if (g_StringLogDbgPrint)
  269. {
  270. DbgPrint("%4I64d: %s"
  271. "\tLen=%d (%x), PD=%d (%x); "
  272. "Off=%d (%x), Lel=%d (%x); "
  273. "Multi: Off=%d (%x), Lel=%d (%x), NE=%d; "
  274. "WA=%lu, NME=%d\n",
  275. index, Buffer,
  276. cb, cb, PrevDelta, PrevDelta,
  277. pLog->Offset, pLog->Offset,
  278. pLog->LastEntryLength, pLog->LastEntryLength,
  279. pLog->MultiOffset, pLog->MultiOffset,
  280. pLog->MultiByteCount, pLog->MultiByteCount,
  281. pLog->MultiNumEntries,
  282. pLog->WrapAroundCount, (int) NeedMultiEntry
  283. );
  284. }
  285. KeReleaseSpinLock(&pLog->SpinLock, OldIrql);
  286. // Finally, fill out the entry
  287. pEntry = (PSTRING_LOG_ENTRY) pTarget;
  288. pEntry->Signature = STRING_LOG_ENTRY_SIGNATURE;
  289. pEntry->Length = (USHORT) cb;
  290. ASSERT(PrevDelta < 0x10000);
  291. pEntry->PrevDelta = (USHORT) PrevDelta;
  292. pEntry->Processor = (UCHAR) KeGetCurrentProcessorNumber();
  293. pEntry->TimeStampLowPart = TimeStamp.LowPart;
  294. pEntry->TimeStampHighPart = TimeStamp.HighPart;
  295. pTarget = (PUCHAR) (pEntry + 1);
  296. RtlCopyMemory( pTarget, Buffer, cb );
  297. for (i = cb; i < cb2 - sizeof(STRING_LOG_ENTRY); ++i)
  298. pTarget[i] = '\0';
  299. pTarget = (PUCHAR) (pEntry + cb2);
  300. return index;
  301. } // WriteStringLogVaList
  302. /***************************************************************************++
  303. Routine Description:
  304. Writes a new entry to the specified string log.
  305. Arguments:
  306. pLog - Supplies the log to write to.
  307. Format... - printf-style format string and arguments
  308. Return Value:
  309. LONGLONG - Index of the newly written entry within the string.
  310. --***************************************************************************/
  311. LONGLONG
  312. __cdecl
  313. WriteStringLog(
  314. IN PSTRING_LOG pLog,
  315. IN PCH Format,
  316. ...
  317. )
  318. {
  319. LONGLONG index;
  320. va_list arglist;
  321. if (pLog == NULL)
  322. return -1;
  323. va_start(arglist, Format);
  324. index = WriteStringLogVaList(pLog, Format, arglist);
  325. va_end(arglist);
  326. return index;
  327. } // WriteStringLog
  328. /***************************************************************************++
  329. Routine Description:
  330. Writes a new entry to the global string log.
  331. Arguments:
  332. pLog - Supplies the log to write to.
  333. Format... - printf-style format string and arguments
  334. Return Value:
  335. LONGLONG - Index of the newly written entry within the string.
  336. --***************************************************************************/
  337. LONGLONG
  338. __cdecl
  339. WriteGlobalStringLog(
  340. IN PCH Format,
  341. ...
  342. )
  343. {
  344. LONGLONG index;
  345. va_list arglist;
  346. if (g_pGlobalStringLog == NULL)
  347. return -1;
  348. va_start(arglist, Format);
  349. index = WriteStringLogVaList(g_pGlobalStringLog, Format, arglist);
  350. va_end(arglist);
  351. return index;
  352. } // WriteGlobalStringLog