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.

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