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.

375 lines
11 KiB

  1. /******************************************************************************
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. frhang.cpp
  5. Abstract:
  6. Implements hang reporting
  7. Revision History:
  8. created derekm 07/07/00
  9. ******************************************************************************/
  10. #include "stdafx.h"
  11. #include "dbghelp.h"
  12. ///////////////////////////////////////////////////////////////////////////////
  13. // exported functions
  14. // **************************************************************************
  15. EFaultRepRetVal APIENTRY ReportHang(DWORD dwpid, DWORD dwtid, BOOL f64bit,
  16. HANDLE hNotify)
  17. {
  18. USE_TRACING("ReportHang");
  19. EXCEPTION_POINTERS ep;
  20. CPFFaultClientCfg oCfg;
  21. EXCEPTION_RECORD er;
  22. EFaultRepRetVal frrvRet = frrvErrNoDW;
  23. SDWManifestBlob dwmb;
  24. SSuspendThreads st;
  25. SMDumpOptions smdo;
  26. CONTEXT cxt;
  27. HRESULT hr = NOERROR;
  28. HANDLE hProc = NULL, hth = NULL;
  29. LPWSTR wszStage1, wszStage2, wszCorpPath, wszHdr;
  30. LPWSTR wszFiles = NULL, wszDir = NULL, wszManifest = NULL;
  31. LPWSTR pwszAppCompat = NULL;
  32. WCHAR wszExe[MAX_PATH], wszAppName[MAX_PATH];
  33. WCHAR wszAppVer[MAX_PATH];
  34. WCHAR *pwszApp, *pwsz;
  35. WCHAR wszBuffer[512];
  36. DWORD dw, cch, cchDir, cchSep;
  37. BOOL fMSApp = FALSE, fThreadsHeld = FALSE;
  38. BYTE *pbBuf = NULL;
  39. ZeroMemory(&st, sizeof(st));
  40. VALIDATEPARM(hr, (dwpid == 0 || dwtid == 0));
  41. if (FAILED(hr))
  42. {
  43. SetLastError(ERROR_INVALID_PARAMETER);
  44. goto done;
  45. }
  46. TESTHR(hr, oCfg.Read(eroPolicyRO));
  47. if (FAILED(hr))
  48. goto done;
  49. if (oCfg.get_TextLog() == eedEnabled)
  50. {
  51. HANDLE hFaultLog = INVALID_HANDLE_VALUE;
  52. // assume system is on a local drive with a base path of "X:\"
  53. GetSystemDirectoryW(wszBuffer, sizeofSTRW(wszBuffer));
  54. wszBuffer[3] = L'\0';
  55. wcscat(wszBuffer, c_wszLogFileName);
  56. hFaultLog = CreateFileW(wszBuffer, GENERIC_WRITE,
  57. FILE_SHARE_WRITE | FILE_SHARE_READ,
  58. NULL, OPEN_ALWAYS, 0, NULL);
  59. if (hFaultLog != INVALID_HANDLE_VALUE)
  60. {
  61. SYSTEMTIME sst;
  62. DWORD cb, cbWritten;
  63. char szMsg[512];
  64. GetSystemTime(&sst);
  65. GetModuleFileNameW(NULL, wszExe, sizeofSTRW(wszExe));
  66. cb = wsprintf(szMsg,
  67. "%02d-%02d-%04d %02d:%02d:%02d Hang fault for %ls\r\n",
  68. sst.wDay, sst.wMonth, sst.wYear, sst.wHour, sst.wMinute,
  69. sst.wSecond, wszExe);
  70. SetFilePointer(hFaultLog, 0, NULL, FILE_END);
  71. WriteFile(hFaultLog, szMsg, cb, &cbWritten, NULL);
  72. CloseHandle(hFaultLog);
  73. }
  74. wszBuffer[0] = L'\0';
  75. }
  76. // see if we're disabled. We do not allow notify only mode. It's really
  77. // pointless to do so, given that the user explicitly wanted the app
  78. // terminated and the 'Do you really want to kill this app' dialog that
  79. // had to have popped up told him we thot it was hung.
  80. if (oCfg.get_DoReport() == eedDisabled)
  81. {
  82. DBG_MSG("DoReport disabled");
  83. goto done;
  84. }
  85. if (oCfg.get_ShowUI() == eedDisabled)
  86. {
  87. LPCWSTR wszULPath = oCfg.get_DumpPath(NULL, 0);
  88. // check and make sure that we have a corporate path specified. If we
  89. // don't, bail
  90. if (wszULPath == NULL || *wszULPath == L'\0')
  91. {
  92. DBG_MSG("ShowUI disabled and no CER path");
  93. goto done;
  94. }
  95. }
  96. // find out what the exe path is
  97. hProc = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE,
  98. dwpid);
  99. if (hProc == NULL)
  100. {
  101. if (ERROR_ACCESS_DENIED == GetLastError())
  102. {
  103. DBG_MSG("Could not open process: ACCESS_DENIED");
  104. }
  105. else
  106. DBG_MSG("Could not open process");
  107. goto done;
  108. }
  109. TESTHR(hr, GetExePath(hProc, wszExe, sizeofSTRW(wszExe)));
  110. if (FAILED(hr))
  111. goto done;
  112. // check to see if the hung thread is DW or other part of our reporting chain
  113. // if so, no point in reporting it...
  114. for(pwsz = wszExe + wcslen(wszExe);
  115. pwsz >= wszExe && *pwsz != L'\\';
  116. pwsz--);
  117. if (*pwsz == L'\\')
  118. pwsz++;
  119. if (_wcsicmp(pwsz, L"dwwin.exe") == 0 ||
  120. _wcsicmp(pwsz, L"dumprep.exe") == 0)
  121. {
  122. DBG_MSG("We are hung- BAIL OUT!!");
  123. goto done;
  124. }
  125. if (FreezeAllThreads(dwpid, 0, &st) == FALSE)
  126. {
  127. DBG_MSG("Could not freeze all threads");
  128. goto done;
  129. }
  130. fThreadsHeld = TRUE;
  131. // if we can't collect info on this puppy, then we'd just be notifying & I
  132. // went over that case above.
  133. if (oCfg.ShouldCollect(wszExe, &fMSApp) == FALSE)
  134. goto done;
  135. if (CreateTempDirAndFile(NULL, NULL, &wszDir) == 0)
  136. goto done;
  137. for (pwszApp = wszExe + wcslen(wszExe);
  138. *pwszApp != L'\\' && pwszApp > wszExe;
  139. pwszApp--);
  140. if (*pwszApp == L'\\')
  141. pwszApp++;
  142. cchDir = wcslen(wszDir);
  143. cch = cchDir + sizeofSTRW(c_wszManFileName) + 4;
  144. __try { wszManifest = (LPWSTR)_alloca(cch * sizeof(WCHAR)); }
  145. __except(EXCEPTION_STACK_OVERFLOW) { wszManifest = NULL; }
  146. if (wszManifest == NULL)
  147. {
  148. SetLastError(ERROR_OUTOFMEMORY);
  149. goto done;
  150. }
  151. wcscpy(wszManifest, wszDir);
  152. wszManifest[cchDir] = L'\\';
  153. wszManifest[cchDir + 1] = L'\0';
  154. wcscat(wszManifest, c_wszManFileName);
  155. cchDir = wcslen(wszDir);
  156. cch = 2 * cchDir + wcslen(pwszApp) + sizeofSTRW(c_wszACFileName) +
  157. sizeofSTRW(c_wszDumpSuffix) + 4;
  158. __try { wszFiles = (LPWSTR)_alloca(cch * sizeof(WCHAR)); }
  159. __except(EXCEPTION_STACK_OVERFLOW) { wszFiles = NULL; }
  160. if (wszFiles == NULL)
  161. {
  162. SetLastError(ERROR_OUTOFMEMORY);
  163. goto done;
  164. }
  165. wcscpy(wszFiles, wszDir);
  166. wszFiles[cchDir] = L'\\';
  167. wszFiles[cchDir + 1] = L'\0';
  168. wcscat(wszFiles, pwszApp);
  169. wcscat(wszFiles, c_wszDumpSuffix);
  170. // build a exception context...
  171. hth = OpenThread(THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION, FALSE,
  172. dwtid);
  173. if (hth == NULL)
  174. goto done;
  175. ZeroMemory(&cxt, sizeof(cxt));
  176. cxt.ContextFlags = CONTEXT_CONTROL;
  177. TESTBOOL(hr, GetThreadContext(hth, &cxt));
  178. if (FAILED(hr))
  179. goto done;
  180. ZeroMemory(&er, sizeof(er));
  181. er.ExceptionCode = 0xcfffffff;
  182. #ifdef _X86_
  183. // this cast is ok to do cuz we know we're on an x86 machine
  184. er.ExceptionAddress = (PVOID)cxt.Eip;
  185. #elif _IA64_
  186. // this cast is ok to do cuz we know we're on an ia64 machine
  187. er.ExceptionAddress = (PVOID)cxt.StIIP;
  188. #endif
  189. ep.ExceptionRecord = &er;
  190. ep.ContextRecord = &cxt;
  191. // generate the minidump
  192. ZeroMemory(&smdo, sizeof(smdo));
  193. smdo.cbSMDO = sizeof(smdo);
  194. #ifdef MANIFEST_HEAP
  195. smdo.ulThread = c_ulThreadWriteDefault;
  196. smdo.ulThreadEx = c_ulThreadWriteDefault;
  197. smdo.ulMod = c_ulModuleWriteDefault;
  198. #else
  199. smdo.ulThread = ThreadWriteThread | ThreadWriteContext | ThreadWriteStack;
  200. smdo.ulMod = ModuleWriteModule | ModuleWriteMiscRecord | ModuleWriteDataSeg;
  201. #endif
  202. smdo.dwThreadID = dwtid;
  203. smdo.dfOptions = dfCollectSig;
  204. smdo.pvFaultAddr = (UINT64)er.ExceptionAddress;
  205. #if defined(_X86_) || defined(_IA64_)
  206. smdo.pEP = (UINT64)&ep;
  207. smdo.fEPClient = FALSE;
  208. #endif
  209. smdo.wszModFullPath[0] = L'\0';
  210. wcscpy(smdo.wszAppFullPath, wszExe);
  211. wcscpy(smdo.wszMod, L"hungapp");
  212. #ifdef MANIFEST_HEAP
  213. if (InternalGenFullAndTriageMinidumps(hProc, dwpid, wszFiles,
  214. NULL, &smdo, f64bit) == FALSE)
  215. #else
  216. if (InternalGenerateMinidump(hProc, dwpid, wszFiles, &smdo) == FALSE)
  217. #endif
  218. goto done;
  219. ThawAllThreads(&st);
  220. fThreadsHeld = FALSE;
  221. // if the app requested it, notify it of that we're done fetching the
  222. // dump
  223. if (hNotify != NULL)
  224. SetEvent(hNotify);
  225. // log an event- don't care if it fails or not.
  226. TESTHR(hr, LogHang(smdo.wszApp, smdo.rgAppVer, smdo.wszMod, smdo.rgModVer,
  227. smdo.pvOffset, f64bit));
  228. // generate all the URLs & file paths we'll need for reporting
  229. TESTHR(hr, BuildManifestURLs(smdo.wszApp, smdo.wszMod, smdo.rgAppVer,
  230. smdo.rgModVer, smdo.pvOffset,
  231. f64bit, &wszStage1, &wszStage2,
  232. &wszCorpPath, &pbBuf));
  233. if (FAILED(hr))
  234. goto done;
  235. TESTHR(hr, GetVerName(smdo.wszAppFullPath, wszAppName,
  236. sizeofSTRW(wszAppName)));
  237. if (FAILED(hr))
  238. goto done;
  239. wszAppName[sizeofSTRW(wszAppName) - 1] = L'\0';
  240. // we created the wszDump buffer above big enuf to hold both the
  241. // dumpfile path as well as the app compat filename. So make
  242. // use of that right now.
  243. cchSep = wcslen(wszFiles);
  244. pwszAppCompat = wszFiles + cchSep + 1;
  245. wcscpy(pwszAppCompat, wszDir);
  246. pwszAppCompat[cchDir] = L'\\';
  247. pwszAppCompat[cchDir + 1] = L'\0';
  248. wcscat(pwszAppCompat, c_wszACFileName);
  249. // if we succeed, turn the NULL following the dump file path into
  250. // the DW separator character
  251. TESTBOOL(hr, GetAppCompatData(wszExe, smdo.wszModFullPath, pwszAppCompat));
  252. if (SUCCEEDED(hr))
  253. wszFiles[cchSep] = DW_FILESEP;
  254. // get the header text
  255. dw = LoadStringW(g_hInstance, IDS_HHDRTXT, wszBuffer,
  256. sizeofSTRW(wszBuffer));
  257. if (dw == 0)
  258. goto done;
  259. cch = dw + wcslen(wszAppName) + 1;
  260. __try { wszHdr = (LPWSTR)_alloca(cch * sizeof(WCHAR)); }
  261. __except(EXCEPTION_STACK_OVERFLOW) { wszHdr = NULL; }
  262. if (wszHdr == NULL)
  263. {
  264. SetLastError(ERROR_OUTOFMEMORY);
  265. goto done;
  266. }
  267. swprintf(wszHdr, wszBuffer, wszAppName);
  268. ZeroMemory(&dwmb, sizeof(dwmb));
  269. dwmb.wszTitle = wszAppName;
  270. dwmb.wszHdr = wszHdr;
  271. dwmb.nidErrMsg = IDS_HERRMSG;
  272. dwmb.wszStage1 = wszStage1;
  273. dwmb.wszStage2 = wszStage2;
  274. dwmb.wszBrand = c_wszDWBrand;
  275. dwmb.wszFileList = wszFiles;
  276. dwmb.fIsMSApp = fMSApp;
  277. dwmb.wszCorpPath = wszCorpPath;
  278. dwmb.wszEventSrc = c_wszHangEventSrc;
  279. // check and see if the system is shutting down. If so, CreateProcess is
  280. // gonna pop up some annoying UI that we can't get rid of, so we don't
  281. // want to call it if we know it's gonna happen.
  282. if (GetSystemMetrics(SM_SHUTTINGDOWN))
  283. goto done;
  284. frrvRet = StartDWManifest(oCfg, dwmb, wszManifest);
  285. done:
  286. // preserve the error code so that the following calls don't overwrite it
  287. dw = GetLastError();
  288. if (fThreadsHeld)
  289. ThawAllThreads(&st);
  290. if (hProc != NULL)
  291. CloseHandle(hProc);
  292. if (hth != NULL)
  293. CloseHandle(hth);
  294. if (wszFiles != NULL)
  295. {
  296. if (pwszAppCompat != NULL)
  297. {
  298. wszFiles[cchSep] = L'\0';
  299. DeleteFileW(pwszAppCompat);
  300. }
  301. #ifdef MANIFEST_HEAP
  302. DeleteFullAndTriageMiniDumps(wszFiles);
  303. #else
  304. DeleteFileW(wszFiles);
  305. #endif
  306. }
  307. if (wszManifest != NULL)
  308. DeleteFileW(wszManifest);
  309. if (wszDir != NULL)
  310. {
  311. DeleteTempDirAndFile(wszDir, FALSE);
  312. MyFree(wszDir);
  313. }
  314. if (pbBuf != NULL)
  315. MyFree(pbBuf);
  316. SetLastError(dw);
  317. return frrvRet;
  318. }