Source code of Windows XP (NT5)
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.

635 lines
17 KiB

  1. #include "stdinc.h"
  2. #include "debmacro.h"
  3. #include <stdio.h>
  4. #include <stdarg.h>
  5. #include "fusionbuffer.h"
  6. #include "setupapi.h"
  7. #include "shlwapi.h"
  8. #if !defined(NT_INCLUDED)
  9. #define DPFLTR_FUSION_ID 54
  10. #endif
  11. #define PRINTABLE(_ch) (isprint((_ch)) ? (_ch) : '.')
  12. #if !defined(FUSION_DEFAULT_DBG_LEVEL_MASK)
  13. #define FUSION_DEFAULT_DBG_LEVEL_MASK (0x00000000)
  14. #endif
  15. extern "C" DWORD kd_fusion_mask = (FUSION_DEFAULT_DBG_LEVEL_MASK & ~DPFLTR_MASK);
  16. extern "C" DWORD kd_kernel_fusion_mask = 0;
  17. extern "C" bool g_FusionEnterExitTracingEnabled = false;
  18. typedef ULONG (NTAPI* RTL_V_DBG_PRINT_EX_FUNCTION)(
  19. IN ULONG ComponentId,
  20. IN ULONG Level,
  21. IN PCH Format,
  22. va_list arglist
  23. );
  24. typedef ULONG (*RTL_V_DBG_PRINT_EX_WITH_PREFIX_FUNCTION)(
  25. IN PCH Prefix,
  26. IN ULONG ComponentId,
  27. IN ULONG Level,
  28. IN PCH Format,
  29. va_list arglist
  30. );
  31. RTL_V_DBG_PRINT_EX_FUNCTION g_pfnvDbgPrintEx;
  32. RTL_V_DBG_PRINT_EX_WITH_PREFIX_FUNCTION g_pfnvDbgPrintExWithPrefix;
  33. VOID
  34. FusionpvDbgPrintToSetupLog(
  35. IN LogSeverity Severity,
  36. IN PCSTR Format,
  37. IN va_list ap
  38. );
  39. bool
  40. FusionpReportConditionAndBreak(
  41. PCSTR pszFormat,
  42. ...
  43. )
  44. {
  45. char rgach[128], rgach2[128];
  46. FRAME_INFO FrameInfo;
  47. bool f = true;
  48. va_list ap;
  49. va_start(ap, pszFormat);
  50. ::_vsnprintf(rgach, NUMBER_OF(rgach), pszFormat, ap);
  51. rgach[NUMBER_OF(rgach) - 1] = '\0';
  52. ::FusionpGetActiveFrameInfo(FrameInfo);
  53. ::_snprintf(
  54. rgach2,
  55. NUMBER_OF(rgach2),
  56. "%s(%d): Break-in requested:\n"
  57. " %s\n",
  58. FrameInfo.pszFile,
  59. FrameInfo.nLine,
  60. rgach);
  61. rgach2[NUMBER_OF(rgach2) - 1] = '\0';
  62. if (::IsDebuggerPresent())
  63. {
  64. ::OutputDebugStringA(rgach2);
  65. f = true;
  66. }
  67. else
  68. {
  69. #if DBG
  70. ::FusionpRtlAssert(
  71. const_cast<PVOID>(reinterpret_cast<const void*>("Break-in requested")),
  72. const_cast<PVOID>(reinterpret_cast<const void*>(FrameInfo.pszFile)),
  73. FrameInfo.nLine,
  74. const_cast<PSTR>(rgach));
  75. #endif
  76. f = false;
  77. }
  78. va_end(ap);
  79. return f;
  80. }
  81. #if DBG
  82. bool
  83. FusionpAssertionFailed(
  84. const FRAME_INFO &rFrameInfo,
  85. PCSTR pszExpression,
  86. PCSTR pszText
  87. )
  88. {
  89. CSxsPreserveLastError ple;
  90. // ::FusionpDumpStack(ptcs, FUSIONP_DUMP_STACK_FORMAT_LONG, FUSION_DBG_LEVEL_ERROR, L"", 10);
  91. if (::IsDebuggerPresent())
  92. {
  93. char rgach[512];
  94. // c:\foo.cpp(35): Assertion Failure. Expression: "m_cch != 0". Text: "Must have nonzero length"
  95. static const char szFormatWithText[] = "%s(%d): Assertion failure in %s. Expression: \"%s\". Text: \"%s\"\n";
  96. static const char szFormatNoText[] = "%s(%d): Assertion failure in %s. Expression: \"%s\".\n";
  97. PCSTR pszFormat = ((pszText == NULL) || (pszText == pszExpression)) ? szFormatNoText : szFormatWithText;
  98. ::_snprintf(
  99. rgach,
  100. NUMBER_OF(rgach),
  101. pszFormat,
  102. rFrameInfo.pszFile,
  103. rFrameInfo.nLine,
  104. rFrameInfo.pszFunction,
  105. pszExpression,
  106. pszText);
  107. rgach[NUMBER_OF(rgach) - 1] = '\0';
  108. ::OutputDebugStringA(rgach);
  109. ple.Restore();
  110. return true;
  111. }
  112. ::FusionpRtlAssert(
  113. const_cast<PVOID>(reinterpret_cast<const void*>(pszExpression)),
  114. const_cast<PVOID>(reinterpret_cast<const void*>(rFrameInfo.pszFile)),
  115. rFrameInfo.nLine,
  116. const_cast<PSTR>(pszText));
  117. ple.Restore();
  118. return false;
  119. }
  120. bool
  121. FusionpAssertionFailed(
  122. PCSTR pszFile,
  123. PCSTR pszFunctionName,
  124. INT nLine,
  125. PCSTR pszExpression,
  126. PCSTR pszText
  127. )
  128. {
  129. FRAME_INFO FrameInfo;
  130. ::FusionpPopulateFrameInfo(FrameInfo, pszFile, pszFunctionName, nLine);
  131. return ::FusionpAssertionFailed(FrameInfo, pszExpression, pszText);
  132. }
  133. #endif // DBG
  134. VOID
  135. FusionpSoftAssertFailed(
  136. const FRAME_INFO &rFrameInfo,
  137. PCSTR pszExpression,
  138. PCSTR pszMessage
  139. )
  140. {
  141. CSxsPreserveLastError ple;
  142. char rgach[256];
  143. // c:\foo.cpp(35): [Fusion] Soft Assertion Failure. Expression: "m_cch != 0". Text: "Must have nonzero length"
  144. static const char szFormatWithText[] = "%s(%d): Soft Assertion Failure in %s! Log a bug!\n Expression: %s\n Message: %s\n";
  145. static const char szFormatNoText[] = "%s(%d): Soft Assertion Failure in %s! Log a bug!\n Expression: %s\n";
  146. PCSTR pszFormat = ((pszMessage == NULL) || (pszMessage == pszExpression)) ? szFormatNoText : szFormatWithText;
  147. ::_snprintf(rgach, NUMBER_OF(rgach), pszFormat, rFrameInfo.pszFile, rFrameInfo.nLine, rFrameInfo.pszFunction, pszExpression, pszMessage);
  148. rgach[NUMBER_OF(rgach) - 1] = '\0';
  149. ::OutputDebugStringA(rgach);
  150. ple.Restore();
  151. }
  152. VOID
  153. FusionpSoftAssertFailed(
  154. PCSTR pszFile,
  155. PCSTR pszFunction,
  156. INT nLine,
  157. PCSTR pszExpression,
  158. PCSTR pszMessage
  159. )
  160. {
  161. FRAME_INFO FrameInfo;
  162. ::FusionpPopulateFrameInfo(FrameInfo, pszFile, pszFunction, nLine);
  163. ::FusionpSoftAssertFailed(FrameInfo, pszExpression, pszMessage);
  164. }
  165. VOID
  166. FusionpSoftAssertFailed(
  167. PCSTR pszExpression,
  168. PCSTR pszMessage
  169. )
  170. {
  171. FRAME_INFO FrameInfo;
  172. ::FusionpGetActiveFrameInfo(FrameInfo);
  173. ::FusionpSoftAssertFailed(FrameInfo, pszExpression, pszMessage);
  174. }
  175. ULONG
  176. FusionpvDbgPrintExNoNTDLL(
  177. IN ULONG ComponentId,
  178. IN ULONG Level,
  179. IN PCH Format,
  180. va_list arglist
  181. )
  182. {
  183. if ((ComponentId == DPFLTR_FUSION_ID) &&
  184. (((Level < 32) &&
  185. (((1 << Level) & kd_fusion_mask) != 0)) ||
  186. ((Level >= 32) &&
  187. ((Level & kd_fusion_mask) != 0))))
  188. {
  189. CSxsPreserveLastError ple;
  190. CHAR rgchBuffer[512];
  191. ULONG n = ::_vsnprintf(rgchBuffer, NUMBER_OF(rgchBuffer), Format, arglist);
  192. rgchBuffer[NUMBER_OF(rgchBuffer) - 1] = '\0';
  193. ::OutputDebugStringA(rgchBuffer);
  194. ple.Restore();
  195. return n;
  196. }
  197. return 0;
  198. }
  199. HMODULE g_setupapiDll = NULL;
  200. typedef BOOL (WINAPI * PSETUPCLOSELOG_ROUTINE)(
  201. );
  202. BOOL
  203. WINAPI
  204. DllStartup_SetupLog(
  205. HINSTANCE Module,
  206. DWORD Reason,
  207. PVOID Reserved
  208. )
  209. {
  210. BOOL fAnyWrong = FALSE;
  211. if ((Reason == DLL_PROCESS_DETACH) &&
  212. (g_setupapiDll != NULL))
  213. {
  214. PSETUPCLOSELOG_ROUTINE pfnSetupCloseLog = (PSETUPCLOSELOG_ROUTINE) ::GetProcAddress(g_setupapiDll, "SetupCloseLog");
  215. if (pfnSetupCloseLog == NULL)
  216. {
  217. ::FusionpDbgPrint("SXS.DLL : SetupCloseLog failed to be located in setupapi.dll with LastError = %d\n", ::FusionpGetLastWin32Error());
  218. fAnyWrong = TRUE;
  219. }
  220. else
  221. {
  222. (*pfnSetupCloseLog)(); // void func
  223. }
  224. if ( ! FreeLibrary(g_setupapiDll))
  225. {
  226. ::FusionpDbgPrint("SXS.DLL : FreeLibrary failed to free setupapi.dll with LastError = %d\n", ::FusionpGetLastWin32Error());
  227. fAnyWrong = TRUE;
  228. }
  229. else
  230. g_setupapiDll = NULL;
  231. }
  232. return (!fAnyWrong);
  233. }
  234. typedef BOOL (WINAPI * PSETUPLOGERRORA_ROUTINE)(
  235. IN LPCSTR MessageString,
  236. IN LogSeverity Severity
  237. );
  238. typedef BOOL (WINAPI * PSETUPLOGERRORW_ROUTINE)(
  239. IN LPCWSTR MessageString,
  240. IN LogSeverity Severity
  241. );
  242. typedef BOOL (WINAPI * PSETUPOPENLOG_ROUTINE)(
  243. BOOL Erase
  244. );
  245. VOID
  246. FusionpvDbgPrintToSetupLog(
  247. IN LogSeverity Severity,
  248. IN PCSTR Format,
  249. IN va_list ap
  250. )
  251. {
  252. //
  253. // first, let us check whether this is ActCtxGen (by csrss.exe) or Setup Installation(by setup.exe)
  254. // during GUI mode setup; do not log for ActCtxGen, only setup
  255. //
  256. if (::GetModuleHandleW(L"csrss.exe") != NULL)
  257. return;
  258. typedef BOOL (WINAPI * PSETUPLOGERRORA_ROUTINE)(PCSTR MessageString, LogSeverity Severity);
  259. static PSETUPLOGERRORA_ROUTINE s_pfnSetupLogError = NULL;
  260. static BOOL s_fEverBeenCalled = FALSE;
  261. if ((s_pfnSetupLogError == NULL) && !s_fEverBeenCalled)
  262. {
  263. g_setupapiDll = ::LoadLibraryW(L"SETUPAPI.DLL");
  264. if (g_setupapiDll == NULL)
  265. {
  266. ::FusionpDbgPrint("SXS.DLL : Loadlibrary Failed to load setupapi.dll with LastError = %d\n", ::FusionpGetLastWin32Error());
  267. goto Exit;
  268. }
  269. PSETUPOPENLOG_ROUTINE pfnOpenSetupLog = (PSETUPOPENLOG_ROUTINE) ::GetProcAddress(g_setupapiDll, "SetupOpenLog");
  270. if (pfnOpenSetupLog == NULL)
  271. {
  272. ::FusionpDbgPrint("SXS.DLL : SetupOpenLog failed to be located in setupapi.dll with LastError = %d\n", ::FusionpGetLastWin32Error());
  273. goto Exit;
  274. }
  275. if (!(*pfnOpenSetupLog)(FALSE))
  276. {
  277. ::FusionpDbgPrint("SXS.DLL : SetupOpenLog failed with LastError = %d\n", ::FusionpGetLastWin32Error());
  278. goto Exit;
  279. }
  280. PSETUPLOGERRORA_ROUTINE pfnTemp = (PSETUPLOGERRORA_ROUTINE) ::GetProcAddress(g_setupapiDll, "SetupLogErrorA");
  281. if (pfnTemp == NULL)
  282. {
  283. ::FusionpDbgPrint("SXS.DLL : SetupLogErrorA failed to be located in setupapi.dll with LastError = %d\n", ::FusionpGetLastWin32Error());
  284. goto Exit;
  285. }
  286. s_pfnSetupLogError = pfnTemp;
  287. }
  288. if (s_pfnSetupLogError != NULL)
  289. {
  290. CHAR rgchBuffer[512];
  291. ::_vsnprintf(rgchBuffer, NUMBER_OF(rgchBuffer), Format, ap);
  292. rgchBuffer[NUMBER_OF(rgchBuffer) - 1] = '\0';
  293. if (!(*s_pfnSetupLogError)(rgchBuffer, Severity))
  294. {
  295. ::FusionpDbgPrint("SXS.DLL : SetupLogErrorA failed with LastError = %d\n", ::FusionpGetLastWin32Error());
  296. }
  297. }
  298. return;
  299. Exit:
  300. s_fEverBeenCalled = TRUE;
  301. }
  302. ULONG
  303. FusionpvDbgPrintEx(
  304. ULONG Level,
  305. PCSTR Format,
  306. va_list ap
  307. )
  308. {
  309. CSxsPreserveLastError ple;
  310. ULONG ulResult = 0;
  311. if (g_pfnvDbgPrintEx == NULL)
  312. {
  313. HINSTANCE hInstNTDLL = ::GetModuleHandleW(L"NTDLL.DLL");
  314. if (hInstNTDLL != NULL)
  315. g_pfnvDbgPrintEx = (RTL_V_DBG_PRINT_EX_FUNCTION)(::GetProcAddress(hInstNTDLL, "vDbgPrintEx"));
  316. if (g_pfnvDbgPrintEx == NULL)
  317. g_pfnvDbgPrintEx = &::FusionpvDbgPrintExNoNTDLL;
  318. }
  319. if (g_pfnvDbgPrintEx)
  320. {
  321. ulResult = (*g_pfnvDbgPrintEx)(
  322. DPFLTR_FUSION_ID,
  323. Level,
  324. const_cast<PSTR>(Format),
  325. ap);
  326. }
  327. if (::IsDebuggerPresent())
  328. {
  329. ulResult = ::FusionpvDbgPrintExNoNTDLL(DPFLTR_FUSION_ID, Level, const_cast<PSTR>(Format), ap);
  330. // Gross, but msdev chokes under too much debug output
  331. if (ulResult != 0)
  332. ::Sleep(10);
  333. }
  334. // Special handling of reflection out to the setup log...
  335. if (Level & FUSION_DBG_LEVEL_SETUPLOG & ~DPFLTR_MASK)
  336. ::FusionpvDbgPrintToSetupLog(
  337. (Level== FUSION_DBG_LEVEL_ERROR) || (Level & FUSION_DBG_LEVEL_ERROR & ~DPFLTR_MASK) ? LogSevError : LogSevInformation,
  338. Format, ap);
  339. ple.Restore();
  340. return ulResult;
  341. }
  342. ULONG
  343. FusionpDbgPrintEx(
  344. ULONG Level,
  345. PCSTR Format,
  346. ...
  347. )
  348. {
  349. ULONG rv = 0;
  350. va_list ap;
  351. va_start(ap, Format);
  352. if ((Level & FUSION_DBG_LEVEL_SETUPLOG) || (::FusionpDbgWouldPrintAtFilterLevel(Level)))
  353. {
  354. rv = ::FusionpvDbgPrintEx(Level, Format, ap);
  355. }
  356. va_end(ap);
  357. return rv;
  358. }
  359. #if 0
  360. BOOL
  361. FusionpDbgErrorListContainsCode(
  362. ULONG Error,
  363. ULONG ToPrintCodesCount
  364. ...
  365. )
  366. {
  367. CSxsPreserveLastError preserveLastError;
  368. bool bPrint = false;
  369. va_list ap;
  370. va_start(ap, ToPrintCodesCount);
  371. // Now let's zip through the list of error codes that
  372. // we should be printing on and see if we pass the filter.
  373. while ((ToPrintCodesCount--) && !bPrint)
  374. {
  375. if (va_arg(ap, ULONG) == Error)
  376. {
  377. bPrint = true;
  378. }
  379. }
  380. va_end(ap);
  381. return bPrint;
  382. }
  383. #endif
  384. // Finds out whether or not the given level would print, given our
  385. // current mask. Knows to look at kd_fusion_mask both in nt as well
  386. // as in the current process.
  387. #define RECHECK_INTERVAL (1000)
  388. #if DBG
  389. EXTERN_C BOOL g_fIgnoreSystemLevelFilterMask = FALSE;
  390. #endif
  391. bool
  392. FusionpDbgWouldPrintAtFilterLevel(
  393. ULONG Level
  394. )
  395. {
  396. // The time quanta here is milliseconds (1000 per second)
  397. #if DBG
  398. if ( !g_fIgnoreSystemLevelFilterMask )
  399. {
  400. #endif
  401. static ULONG s_ulNextTimeToCheck;
  402. ULONG ulCapturedNextTimeToCheck = s_ulNextTimeToCheck;
  403. ULONG ulCurrentTime = (USER_SHARED_DATA->TickCountLow * (USER_SHARED_DATA->TickCountMultiplier >> 24));
  404. // Look for either the tick count to have wrapped or to be over the next time to check.
  405. // There's some opportunity for loss here if we only run this function every 49 days but
  406. // that's pretty darned unlikely.
  407. if ((ulCurrentTime >= ulCapturedNextTimeToCheck) ||
  408. ((ulCapturedNextTimeToCheck > RECHECK_INTERVAL) &&
  409. (ulCurrentTime < (ulCapturedNextTimeToCheck - RECHECK_INTERVAL))))
  410. {
  411. DWORD dwNewFusionMask = 0;
  412. ULONG i;
  413. // Time to check again...
  414. for (i=0; i<31; i++)
  415. {
  416. if (::FusionpNtQueryDebugFilterState(DPFLTR_FUSION_ID, (DPFLTR_MASK | (1 << i))) == TRUE)
  417. dwNewFusionMask |= (1 << i);
  418. }
  419. if (s_ulNextTimeToCheck == ulCapturedNextTimeToCheck)
  420. {
  421. // Check again in 1000ms (1 second)
  422. s_ulNextTimeToCheck = (ulCurrentTime + RECHECK_INTERVAL);
  423. // Nobody got in before us. Let's write it...
  424. kd_kernel_fusion_mask = dwNewFusionMask;
  425. g_FusionEnterExitTracingEnabled = (((kd_fusion_mask | kd_kernel_fusion_mask) & FUSION_DBG_LEVEL_ENTEREXIT) != 0);
  426. }
  427. }
  428. #if DBG
  429. }
  430. #endif
  431. ULONG mask = Level;
  432. // If level < 32, it's actually a single-bit test
  433. if (Level < 32)
  434. mask = (1 << Level);
  435. // Are these bits set in our mask?
  436. return (((kd_fusion_mask | kd_kernel_fusion_mask) & mask) != 0);
  437. }
  438. VOID
  439. FusionppDbgPrintBlob(
  440. ULONG Level,
  441. PVOID Data,
  442. SIZE_T Length,
  443. PCWSTR PerLinePrefix
  444. )
  445. {
  446. ULONG Offset = 0;
  447. if (PerLinePrefix == NULL)
  448. PerLinePrefix = L"";
  449. // we'll output in 8-byte chunks as shown:
  450. //
  451. // [prefix] 00000000: xx-xx-xx-xx-xx-xx-xx-xx (........)
  452. // [prefix] 00000008: xx-xx-xx-xx-xx-xx-xx-xx (........)
  453. // [prefix] 00000010: xx-xx-xx-xx-xx-xx-xx-xx (........)
  454. //
  455. while (Length >= 16)
  456. {
  457. BYTE *pb = (BYTE *) (((ULONG_PTR) Data) + Offset);
  458. ::FusionpDbgPrintEx(
  459. Level,
  460. "%S%08lx: %02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x-%02x (%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c)\n",
  461. PerLinePrefix,
  462. Offset,
  463. pb[0], pb[1], pb[2], pb[3], pb[4], pb[5], pb[6], pb[7],
  464. pb[8], pb[9], pb[10], pb[11], pb[12], pb[13], pb[14], pb[15],
  465. PRINTABLE(pb[0]),
  466. PRINTABLE(pb[1]),
  467. PRINTABLE(pb[2]),
  468. PRINTABLE(pb[3]),
  469. PRINTABLE(pb[4]),
  470. PRINTABLE(pb[5]),
  471. PRINTABLE(pb[6]),
  472. PRINTABLE(pb[7]),
  473. PRINTABLE(pb[8]),
  474. PRINTABLE(pb[9]),
  475. PRINTABLE(pb[10]),
  476. PRINTABLE(pb[11]),
  477. PRINTABLE(pb[12]),
  478. PRINTABLE(pb[13]),
  479. PRINTABLE(pb[14]),
  480. PRINTABLE(pb[15]));
  481. Offset += 16;
  482. Length -= 16;
  483. }
  484. if (Length != 0)
  485. {
  486. WCHAR wchLeft[64], wchRight[64];
  487. WCHAR rgTemp2[16]; // arbitrary big enough size
  488. bool First = true;
  489. ULONG i;
  490. BYTE *pb = (BYTE *) (((ULONG_PTR) Data) + Offset);
  491. // init output buffers
  492. wchLeft[0] = 0;
  493. ::wcscpy( wchRight, L" (" );
  494. for (i=0; i<16; i++)
  495. {
  496. if (Length > 0)
  497. {
  498. // left
  499. ::_snwprintf(rgTemp2, NUMBER_OF(rgTemp2), L"%ls%02x", First ? L" " : L"-", pb[i]);
  500. First = false;
  501. ::wcscat(wchLeft, rgTemp2);
  502. // right
  503. ::_snwprintf(rgTemp2, NUMBER_OF(rgTemp2), L"%c", PRINTABLE(pb[i]));
  504. wcscat(wchRight, rgTemp2);
  505. Length--;
  506. }
  507. else
  508. {
  509. ::wcscat(wchLeft, L" ");
  510. }
  511. }
  512. ::wcscat(wchRight, L")");
  513. ::FusionpDbgPrintEx(
  514. Level,
  515. "%S %08lx:%ls%ls\n",
  516. PerLinePrefix,
  517. Offset,
  518. wchLeft,
  519. wchRight);
  520. }
  521. }
  522. VOID
  523. FusionpDbgPrintBlob(
  524. ULONG Level,
  525. PVOID Data,
  526. SIZE_T Length,
  527. PCWSTR PerLinePrefix
  528. )
  529. {
  530. //
  531. // This extra function reduces stack usage when the data
  532. // isn't actually printed (and increases stack usage slightly otherwise).
  533. //
  534. if (!::FusionpDbgWouldPrintAtFilterLevel(Level))
  535. return;
  536. ::FusionppDbgPrintBlob(Level, Data, Length, PerLinePrefix);
  537. }