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.

670 lines
19 KiB

  1. /****************************************************************************
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. dumprep.cpp
  5. Abstract:
  6. hang manager intermediate app
  7. *** IMPORTANT NOTE: this links with the single threaded CRT static lib. If
  8. it is changed to be multithreaded for some odd reason,
  9. then the sources file must be modified to link to
  10. libcmt.lib.
  11. Revision History:
  12. DerekM created 08/16/00
  13. ****************************************************************************/
  14. #include "stdafx.h"
  15. #include "malloc.h"
  16. #include "faultrep.h"
  17. #include "pfrcfg.h"
  18. enum EOp
  19. {
  20. eopNone = 0,
  21. eopHang,
  22. eopDump,
  23. eopEvent
  24. };
  25. enum ECheckType
  26. {
  27. ctNone = -1,
  28. ctKernel = 0,
  29. ctUser,
  30. ctShutdown,
  31. ctNumChecks
  32. };
  33. struct SCheckData
  34. {
  35. LPCWSTR wszRegPath;
  36. LPCWSTR wszRunVal;
  37. LPCWSTR wszEventName;
  38. LPCSTR szFnName;
  39. BOOL fUseData;
  40. BOOL fDelDump;
  41. };
  42. //////////////////////////////////////////////////////////////////////////////
  43. // constants
  44. const char c_szKSFnName[] = "ReportEREventDW";
  45. const char c_szUserFnName[] = "ReportFaultFromQueue";
  46. SCheckData g_scd[ctNumChecks] =
  47. {
  48. { c_wszRKKrnl, c_wszRVKFC, c_wszMutKrnlName, c_szKSFnName, FALSE, FALSE },
  49. { c_wszRKUser, c_wszRVUFC, c_wszMutUserName, c_szUserFnName, TRUE, TRUE },
  50. { c_wszRKShut, c_wszRVSEC, c_wszMutShutName, c_szKSFnName, FALSE, FALSE },
  51. };
  52. #define EV_ACCESS_ALL GENERIC_ALL | STANDARD_RIGHTS_ALL
  53. #define EV_ACCESS_RS GENERIC_READ | SYNCHRONIZE
  54. #define pfn_VALONLY pfn_REPORTEREVENTDW
  55. #define pfn_VALDATA pfn_REPORTFAULTFROMQ
  56. //////////////////////////////////////////////////////////////////////////////
  57. // globals
  58. BOOL g_fDeleteReg = TRUE;
  59. //////////////////////////////////////////////////////////////////////////////
  60. // misc
  61. // **************************************************************************
  62. LONG __stdcall ExceptionTrap(_EXCEPTION_POINTERS *ExceptionInfo)
  63. {
  64. return EXCEPTION_EXECUTE_HANDLER;
  65. }
  66. #ifdef MANIFEST_HEAP
  67. BOOL
  68. DeleteFullAndTriageMiniDumps(
  69. LPCWSTR wszPath
  70. )
  71. //
  72. // We create a FullMinidump file along with triage minidump in the same dir
  73. // This routine cleans up both those files
  74. //
  75. {
  76. LPWSTR wszFullMinidump = NULL;
  77. DWORD cch;
  78. BOOL fRet;
  79. fRet = DeleteFileW(wszPath);
  80. cch = wcslen(wszPath) + sizeofSTRW(c_wszHeapDumpSuffix);
  81. __try { wszFullMinidump = (WCHAR *)_alloca(cch * sizeof(WCHAR)); }
  82. __except(EXCEPTION_STACK_OVERFLOW) { wszFullMinidump = NULL; }
  83. if (wszFullMinidump)
  84. {
  85. LPWSTR wszFileExt = NULL;
  86. // Build Dump-with-heap path
  87. wcsncpy(wszFullMinidump, wszPath, cch);
  88. wszFileExt = wszFullMinidump + wcslen(wszFullMinidump) - sizeofSTRW(c_wszDumpSuffix) + 1;
  89. if (!wcscmp(wszFileExt, c_wszDumpSuffix))
  90. {
  91. *wszFileExt = L'\0';
  92. }
  93. wcsncat(wszFullMinidump, c_wszHeapDumpSuffix, cch);
  94. fRet = DeleteFileW(wszFullMinidump);
  95. } else
  96. {
  97. fRet = FALSE;
  98. }
  99. return fRet;
  100. }
  101. #endif // MANIFEST_HEAP
  102. // **************************************************************************
  103. void DeleteQueuedEvents(HKEY hkey, LPWSTR wszVal, DWORD cchMaxVal,
  104. ECheckType ct)
  105. {
  106. DWORD cchVal, dw;
  107. HKEY hkeyRun = NULL;
  108. HRESULT hr = NOERROR;
  109. USE_TRACING("DeleteQueuedEvents");
  110. VALIDATEPARM(hr, (hkey == NULL || wszVal == NULL));
  111. if (FAILED(hr))
  112. {
  113. SetLastError(ERROR_INVALID_PARAMETER);
  114. goto done;
  115. }
  116. for(;;)
  117. {
  118. cchVal = cchMaxVal;
  119. dw = RegEnumValueW(hkey, 0, wszVal, &cchVal, NULL, NULL,
  120. NULL, NULL);
  121. if (dw != ERROR_SUCCESS && dw != ERROR_NO_MORE_ITEMS)
  122. {
  123. SetLastError(dw);
  124. goto done;
  125. }
  126. if (dw == ERROR_NO_MORE_ITEMS)
  127. break;
  128. RegDeleteValueW(hkey, wszVal);
  129. if (ct == ctUser)
  130. {
  131. #ifdef MANIFEST_HEAP
  132. DeleteFullAndTriageMiniDumps(wszVal);
  133. #else
  134. DeleteFileW(wszVal);
  135. #endif // !MANIFEST_HEAP
  136. }
  137. }
  138. // gotta delete our value out of the Run key so we don't run
  139. // unnecessarily again...
  140. dw = RegOpenKeyExW(HKEY_LOCAL_MACHINE, c_wszRKRun, 0,
  141. KEY_ALL_ACCESS, &hkeyRun);
  142. if (dw != ERROR_SUCCESS)
  143. goto done;
  144. RegDeleteValueW(hkeyRun, g_scd[ct].wszRunVal);
  145. done:
  146. if (hkeyRun != NULL)
  147. RegCloseKey(hkeyRun);
  148. return;
  149. }
  150. // **************************************************************************
  151. void ReportEvents(HMODULE hmod, ECheckType ct)
  152. {
  153. EFaultRepRetVal frrv;
  154. pfn_VALONLY pfnVO = NULL;
  155. pfn_VALDATA pfnVD = NULL;
  156. EEventType eet = eetKernelFault;
  157. HRESULT hr = NOERROR;
  158. HANDLE hmut = NULL;
  159. LPWSTR wszVal = NULL;
  160. LPBYTE pbData = NULL, pbDataToUse = NULL;
  161. EEnDis eedReport, eedUI;
  162. DWORD cchVal = 0, cchMaxVal = 0, cbMaxData = 0, cVals = 0;
  163. DWORD dw, cbData = 0, *pcbData = NULL;
  164. DWORD dwType;
  165. HKEY hkey = NULL;
  166. USE_TRACING("ReportEvents");
  167. VALIDATEPARM(hr, ((ct <= ctNone && ct >= ctNumChecks) || hmod == NULL));
  168. if (FAILED(hr))
  169. return;
  170. // assume hmod is valid cuz we do a check in wWinMain to make sure it is
  171. // before calling this fn
  172. if (g_scd[ct].fUseData)
  173. pfnVD = (pfn_VALDATA)GetProcAddress(hmod, g_scd[ct].szFnName);
  174. else
  175. pfnVO = (pfn_VALONLY)GetProcAddress(hmod, g_scd[ct].szFnName);
  176. if (pfnVD == NULL && pfnVO == NULL)
  177. return;
  178. dw = RegOpenKeyExW(HKEY_LOCAL_MACHINE, c_wszRPCfg, 0, KEY_READ, &hkey);
  179. if (dw != ERROR_SUCCESS)
  180. return;
  181. cbData = sizeof(eedUI);
  182. dw = RegQueryValueExW(hkey, c_wszRVShowUI, 0, NULL, (PBYTE)&eedUI,
  183. &cbData);
  184. if (dw != ERROR_SUCCESS)
  185. {
  186. RegCloseKey(hkey);
  187. return;
  188. }
  189. cbData = sizeof(eedReport);
  190. dw = RegQueryValueExW(hkey, c_wszRVDoReport, 0, NULL, (PBYTE)&eedReport,
  191. &cbData);
  192. RegCloseKey(hkey);
  193. hkey = NULL;
  194. if (dw != ERROR_SUCCESS)
  195. return;
  196. if (eedUI != eedEnabled && eedUI != eedDisabled &&
  197. eedUI != eedEnabledNoCheck)
  198. eedUI = eedEnabled;
  199. if (eedReport != eedEnabled && eedReport != eedDisabled)
  200. eedReport = eedEnabled;
  201. // only want one user at a time going thru this
  202. hmut = OpenMutexW(SYNCHRONIZE, FALSE, g_scd[ct].wszEventName);
  203. VALIDATEPARM(hr, (hmut == NULL));
  204. if (FAILED(hr))
  205. return;
  206. // the default value above is eetKernelFault, so only need to change if
  207. // it's a shutdown
  208. if (ct == ctShutdown)
  209. eet = eetShutdown;
  210. __try
  211. {
  212. __try
  213. {
  214. // give this wait five minutes. If the code doesn't complete by
  215. // then, then we're either held up by DW (which means an admin
  216. // aleady passed thru here) or something has barfed and is holding
  217. // the mutex.
  218. dw = WaitForSingleObject(hmut, 300000);
  219. if (dw != WAIT_OBJECT_0 && dw != WAIT_ABANDONED)
  220. __leave;
  221. dw = RegOpenKeyExW(HKEY_LOCAL_MACHINE, g_scd[ct].wszRegPath, 0,
  222. KEY_ALL_ACCESS, &hkey);
  223. if (dw != ERROR_SUCCESS)
  224. __leave;
  225. // determine how big the valuename is & allocate a buffer for it
  226. dw = RegQueryInfoKeyW(hkey, NULL, NULL, NULL, NULL, NULL, NULL,
  227. &cVals, &cchMaxVal, &cbMaxData, NULL, NULL);
  228. if (dw != ERROR_SUCCESS || cVals == 0 || cchMaxVal == 0)
  229. __leave;
  230. cchMaxVal++;
  231. // get us some buffers to hold the data bits we're interested in...
  232. wszVal = (LPWSTR)MyAlloc(cchMaxVal * sizeof(WCHAR));
  233. if (wszVal == NULL)
  234. __leave;
  235. // if we're completely disabled, then nuke all the queued stuff
  236. // and bail
  237. if (eedUI == eedDisabled && eedReport == eedDisabled)
  238. {
  239. DeleteQueuedEvents(hkey, wszVal, cchMaxVal, ct);
  240. __leave;
  241. }
  242. if (g_scd[ct].fUseData)
  243. {
  244. pbData = (LPBYTE) MyAlloc(cbMaxData);
  245. if (pbData == NULL)
  246. __leave;
  247. pbDataToUse = pbData;
  248. pcbData = &cbData;
  249. }
  250. do
  251. {
  252. cchVal = cchMaxVal;
  253. cbData = cbMaxData;
  254. dw = RegEnumValueW(hkey, 0, wszVal, &cchVal, NULL, &dwType,
  255. pbDataToUse, pcbData);
  256. if (dw != ERROR_SUCCESS && dw != ERROR_NO_MORE_ITEMS)
  257. __leave;
  258. if (dw == ERROR_NO_MORE_ITEMS)
  259. break;
  260. if (g_scd[ct].fUseData)
  261. {
  262. // if the type isn't REG_BINARY, then someone wrote an
  263. // invalid blob to the registry. We have to ignore it.
  264. if (dwType == REG_BINARY)
  265. frrv = (*pfnVD)(wszVal, pbData, cbData);
  266. else
  267. {
  268. SetLastError(ERROR_INVALID_PARAMETER);
  269. frrv = frrvOk;
  270. }
  271. }
  272. else
  273. {
  274. frrv = (*pfnVO)(eet, wszVal, NULL);
  275. }
  276. // if the call succeeds (or the data we fed to it was invalid)
  277. // then nuke the reg key & dump file
  278. if (GetLastError() == ERROR_INVALID_PARAMETER ||
  279. (g_fDeleteReg && frrv == frrvOk))
  280. {
  281. dw = RegDeleteValueW(hkey, wszVal);
  282. if (dw != ERROR_SUCCESS && dw != ERROR_FILE_NOT_FOUND &&
  283. dw != ERROR_PATH_NOT_FOUND)
  284. __leave;
  285. if (g_scd[ct].fDelDump && g_fDeleteReg)
  286. {
  287. #ifdef MANIFEST_HEAP
  288. DeleteFullAndTriageMiniDumps(wszVal);
  289. #else
  290. DeleteFileW(wszVal);
  291. #endif // !MANIFEST_HEAP
  292. }
  293. }
  294. else
  295. {
  296. // don't delete the Run key if we got an error and didn't
  297. // delete the fault key
  298. if (frrv != frrvOk)
  299. __leave;
  300. }
  301. }
  302. while(1);
  303. RegCloseKey(hkey);
  304. hkey = NULL;
  305. // gotta delete our value out of the Run key so we don't run
  306. // unnecessarily again...
  307. dw = RegOpenKeyExW(HKEY_LOCAL_MACHINE, c_wszRKRun, 0,
  308. KEY_ALL_ACCESS, &hkey);
  309. if (dw != ERROR_SUCCESS)
  310. __leave;
  311. RegDeleteValueW(hkey, g_scd[ct].wszRunVal);
  312. }
  313. __finally
  314. {
  315. }
  316. }
  317. __except(EXCEPTION_EXECUTE_HANDLER)
  318. {
  319. }
  320. if (hmut != NULL)
  321. {
  322. ReleaseMutex(hmut);
  323. CloseHandle(hmut);
  324. }
  325. if (hkey != NULL)
  326. RegCloseKey(hkey);
  327. if (pbData != NULL)
  328. MyFree(pbData);
  329. if (wszVal != NULL)
  330. MyFree(wszVal);
  331. }
  332. //////////////////////////////////////////////////////////////////////////////
  333. // wmain
  334. // **************************************************************************
  335. int __cdecl wmain(int argc, WCHAR **argv)
  336. {
  337. EFaultRepRetVal frrv = frrvErrNoDW;
  338. SMDumpOptions smdo, *psmdo = &smdo;
  339. ECheckType ct = ctNone;
  340. HMODULE hmod = NULL;
  341. HANDLE hevNotify = NULL, hproc = NULL, hmem = NULL;
  342. LPWSTR wszDump = NULL;
  343. WCHAR wszMod[MAX_PATH];
  344. DWORD dwpid, dwtid;
  345. BOOL f64bit = FALSE;
  346. int i;
  347. EOp eop = eopNone;
  348. HRESULT hr = NOERROR;
  349. // we don't want to have any faults get trapped anywhere.
  350. SetErrorMode(SEM_NOGPFAULTERRORBOX | SEM_NOALIGNMENTFAULTEXCEPT |
  351. SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
  352. SetUnhandledExceptionFilter(ExceptionTrap);
  353. INIT_TRACING
  354. USE_TRACING("DumpRep.wmain");
  355. VALIDATEPARM(hr, (argc < 2 || argc > 8));
  356. if (FAILED(hr))
  357. goto done;
  358. dwpid = _wtol(argv[1]);
  359. ZeroMemory(&smdo, sizeof(smdo));
  360. for (i = 2; i < argc; i++)
  361. {
  362. if (argv[i][0] != L'-')
  363. continue;
  364. switch(argv[i][1])
  365. {
  366. // debug flag to prevent deletion of reg entries
  367. case L'E':
  368. case L'e':
  369. #if defined(NO_WAY_DEBUG) || defined(NO_WAY__DEBUG)
  370. g_fDeleteReg = FALSE;
  371. #endif
  372. break;
  373. // user or kernel faults or shutdowns
  374. case L'K':
  375. case L'k':
  376. case L'U':
  377. case L'u':
  378. case L'S':
  379. case L's':
  380. if (eop != eopNone)
  381. goto done;
  382. eop = eopEvent;
  383. // to workaround the desktop hanging while all Run processes
  384. // do their thing, we spawn a another copy of ourselves and
  385. // immediately exit.
  386. if (argv[i][2] != L'G' && argv[i][2] != L'g')
  387. {
  388. PROCESS_INFORMATION pi;
  389. STARTUPINFOW si;
  390. GetModuleFileNameW(NULL, wszMod, sizeofSTRW(wszMod));
  391. if (argv[i][1] == L'K' || argv[i][1] == L'k')
  392. wcscat(wszMod, L" 0 -KG");
  393. else if (argv[i][1] == L'U' || argv[i][1] == L'u')
  394. wcscat(wszMod, L" 0 -UG");
  395. else
  396. wcscat(wszMod, L" 0 -SG");
  397. ZeroMemory(&si, sizeof(si));
  398. si.cb = sizeof(si);
  399. if (CreateProcessW(NULL, wszMod, NULL, NULL, FALSE, 0, NULL,
  400. NULL, &si, &pi))
  401. {
  402. CloseHandle(pi.hThread);
  403. CloseHandle(pi.hProcess);
  404. }
  405. goto done;
  406. }
  407. else
  408. {
  409. if (argv[i][1] == L'K' || argv[i][1] == L'k')
  410. ct = ctKernel;
  411. else if (argv[i][1] == L'U' || argv[i][1] == L'u')
  412. ct = ctUser;
  413. else
  414. ct = ctShutdown;
  415. }
  416. break;
  417. // hangs
  418. case L'H':
  419. case L'h':
  420. if (i + 1 >= argc || eop != eopNone)
  421. goto done;
  422. eop = eopHang;
  423. #ifdef _WIN64
  424. if (argv[i][2] == L'6')
  425. f64bit = TRUE;
  426. #endif
  427. dwtid = _wtol(argv[++i]);
  428. if (argc > i + 1)
  429. {
  430. hevNotify = OpenEventW(EVENT_MODIFY_STATE | SYNCHRONIZE,
  431. FALSE, argv[++i]);
  432. }
  433. break;
  434. // dumps
  435. case L'D':
  436. case L'd':
  437. if (i + 3 >= argc || wszDump != NULL || eop != eopNone)
  438. goto done;
  439. eop = eopDump;
  440. ZeroMemory(&smdo, sizeof(smdo));
  441. smdo.ulMod = _wtol(argv[++i]);
  442. smdo.ulThread = _wtol(argv[++i]);
  443. wszDump = argv[++i];
  444. if (argv[i - 3][2] == L'T' || argv[i - 3][2] == L't')
  445. {
  446. if (i + 1 >= argc)
  447. goto done;
  448. smdo.dwThreadID = _wtol(argv[++i]);
  449. smdo.dfOptions = dfFilterThread;
  450. }
  451. else if (argv[i - 3][2] == L'S' || argv[i - 3][2] == L's')
  452. {
  453. if (i + 2 >= argc)
  454. goto done;
  455. smdo.dwThreadID = _wtol(argv[++i]);
  456. smdo.ulThreadEx = _wtol(argv[++i]);
  457. smdo.dfOptions = dfFilterThreadEx;
  458. }
  459. else if (argv[i - 3][2] == L'M' || argv[i - 3][2] == L'm')
  460. {
  461. HANDLE hmemRemote = NULL;
  462. LPVOID pvMem = NULL;
  463. if (i + 1 >= argc)
  464. goto done;
  465. hproc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwpid);
  466. if (hproc == NULL)
  467. goto done;
  468. #ifdef _WIN64
  469. hmemRemote = (HANDLE)_wtoi64(argv[++i]);
  470. #else
  471. hmemRemote = (HANDLE)_wtol(argv[++i]);
  472. #endif
  473. VALIDATEPARM(hr, (hmemRemote == NULL));
  474. if (FAILED(hr))
  475. goto done;
  476. if (DuplicateHandle(hproc, hmemRemote, GetCurrentProcess(),
  477. &hmem, 0, FALSE,
  478. DUPLICATE_SAME_ACCESS) == FALSE)
  479. goto done;
  480. pvMem = MapViewOfFile(hmem, FILE_MAP_WRITE | FILE_MAP_WRITE,
  481. 0, 0, 0);
  482. if (pvMem == NULL)
  483. goto done;
  484. psmdo = (SMDumpOptions *)pvMem;
  485. }
  486. break;
  487. default:
  488. goto done;
  489. }
  490. }
  491. // if we didn't get an operation, no point in doing anything else...
  492. if (eop == eopNone)
  493. goto done;
  494. GetSystemDirectoryW(wszMod, sizeofSTRW(wszMod));
  495. wcscat(wszMod, L"\\faultrep.dll");
  496. hmod = LoadLibraryExW(wszMod, NULL, 0);
  497. VALIDATEPARM(hr, (hmod == NULL));
  498. if (FAILED(hr))
  499. goto done;
  500. switch(eop)
  501. {
  502. // user or kernel faults:
  503. case eopEvent:
  504. ReportEvents(hmod, ct);
  505. break;
  506. // dumps
  507. case eopDump:
  508. {
  509. pfn_CREATEMINIDUMPW pfnCM;
  510. VALIDATEPARM(hr,(wszDump == NULL));
  511. if (FAILED(hr))
  512. goto done;
  513. pfnCM = (pfn_CREATEMINIDUMPW)GetProcAddress(hmod,
  514. "CreateMinidumpW");
  515. VALIDATEPARM(hr, (pfnCM == NULL));
  516. if (SUCCEEDED(hr))
  517. frrv = (*pfnCM)(dwpid, wszDump, psmdo) ? frrvOk : frrvErr;
  518. break;
  519. }
  520. // hangs
  521. case eopHang:
  522. {
  523. pfn_REPORTHANG pfnRH;
  524. pfnRH = (pfn_REPORTHANG)GetProcAddress(hmod, "ReportHang");
  525. VALIDATEPARM(hr, (pfnRH == NULL));
  526. if (SUCCEEDED(hr))
  527. frrv = (*pfnRH)(dwpid, dwtid, f64bit, hevNotify);
  528. break;
  529. }
  530. // err, shouldn't get here
  531. default:
  532. break;
  533. }
  534. done:
  535. if (hmod != NULL)
  536. FreeLibrary(hmod);
  537. if (hproc != NULL)
  538. CloseHandle(hproc);
  539. if (hmem != NULL)
  540. CloseHandle(hmem);
  541. if (hevNotify != NULL)
  542. CloseHandle(hevNotify);
  543. if (psmdo != NULL && psmdo != &smdo)
  544. UnmapViewOfFile((LPVOID)psmdo);
  545. TERM_TRACING;
  546. return frrv;
  547. }