|
|
/******************************************************************************
Copyright (c) 2000 Microsoft Corporation
Module Name: frhang.cpp
Abstract: Implements hang reporting
Revision History: created derekm 07/07/00
******************************************************************************/
#include "stdafx.h"
#include "dbghelp.h"
///////////////////////////////////////////////////////////////////////////////
// exported functions
// **************************************************************************
EFaultRepRetVal APIENTRY ReportHang(DWORD dwpid, DWORD dwtid, BOOL f64bit, HANDLE hNotify) { USE_TRACING("ReportHang");
EXCEPTION_POINTERS ep; CPFFaultClientCfg oCfg; EXCEPTION_RECORD er; EFaultRepRetVal frrvRet = frrvErrNoDW; SDWManifestBlob dwmb; SSuspendThreads st; SMDumpOptions smdo; CONTEXT cxt; HRESULT hr = NOERROR; HANDLE hProc = NULL, hth = NULL; LPWSTR wszStage1, wszStage2, wszCorpPath, wszHdr; LPWSTR wszFiles = NULL, wszDir = NULL, wszManifest = NULL; LPWSTR pwszAppCompat = NULL; WCHAR wszExe[MAX_PATH], wszAppName[MAX_PATH]; WCHAR wszAppVer[MAX_PATH]; WCHAR *pwszApp, *pwsz; WCHAR wszBuffer[512]; DWORD dw, cch, cchDir, cchSep; BOOL fMSApp = FALSE, fThreadsHeld = FALSE; BYTE *pbBuf = NULL;
ZeroMemory(&st, sizeof(st));
VALIDATEPARM(hr, (dwpid == 0 || dwtid == 0)); if (FAILED(hr)) { SetLastError(ERROR_INVALID_PARAMETER); goto done; }
TESTHR(hr, oCfg.Read(eroPolicyRO)); if (FAILED(hr)) goto done;
if (oCfg.get_TextLog() == eedEnabled) { HANDLE hFaultLog = INVALID_HANDLE_VALUE;
// assume system is on a local drive with a base path of "X:\"
GetSystemDirectoryW(wszBuffer, sizeofSTRW(wszBuffer)); wszBuffer[3] = L'\0'; wcscat(wszBuffer, c_wszLogFileName); hFaultLog = CreateFileW(wszBuffer, GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_ALWAYS, 0, NULL); if (hFaultLog != INVALID_HANDLE_VALUE) { SYSTEMTIME sst; DWORD cb, cbWritten; char szMsg[512];
GetSystemTime(&sst); GetModuleFileNameW(NULL, wszExe, sizeofSTRW(wszExe)); cb = wsprintf(szMsg, "%02d-%02d-%04d %02d:%02d:%02d Hang fault for %ls\r\n", sst.wDay, sst.wMonth, sst.wYear, sst.wHour, sst.wMinute, sst.wSecond, wszExe); SetFilePointer(hFaultLog, 0, NULL, FILE_END); WriteFile(hFaultLog, szMsg, cb, &cbWritten, NULL); CloseHandle(hFaultLog); }
wszBuffer[0] = L'\0'; }
// see if we're disabled. We do not allow notify only mode. It's really
// pointless to do so, given that the user explicitly wanted the app
// terminated and the 'Do you really want to kill this app' dialog that
// had to have popped up told him we thot it was hung.
if (oCfg.get_DoReport() == eedDisabled) { DBG_MSG("DoReport disabled"); goto done; }
if (oCfg.get_ShowUI() == eedDisabled) { LPCWSTR wszULPath = oCfg.get_DumpPath(NULL, 0);
// check and make sure that we have a corporate path specified. If we
// don't, bail
if (wszULPath == NULL || *wszULPath == L'\0') { DBG_MSG("ShowUI disabled and no CER path"); goto done; } }
// find out what the exe path is
hProc = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, dwpid); if (hProc == NULL) { if (ERROR_ACCESS_DENIED == GetLastError()) { DBG_MSG("Could not open process: ACCESS_DENIED"); } else DBG_MSG("Could not open process"); goto done; }
TESTHR(hr, GetExePath(hProc, wszExe, sizeofSTRW(wszExe))); if (FAILED(hr)) goto done;
// check to see if the hung thread is DW or other part of our reporting chain
// if so, no point in reporting it...
for(pwsz = wszExe + wcslen(wszExe); pwsz >= wszExe && *pwsz != L'\\'; pwsz--); if (*pwsz == L'\\') pwsz++; if (_wcsicmp(pwsz, L"dwwin.exe") == 0 || _wcsicmp(pwsz, L"dumprep.exe") == 0) { DBG_MSG("We are hung- BAIL OUT!!"); goto done; }
if (FreezeAllThreads(dwpid, 0, &st) == FALSE) { DBG_MSG("Could not freeze all threads"); goto done; } fThreadsHeld = TRUE;
// if we can't collect info on this puppy, then we'd just be notifying & I
// went over that case above.
if (oCfg.ShouldCollect(wszExe, &fMSApp) == FALSE) goto done;
if (CreateTempDirAndFile(NULL, NULL, &wszDir) == 0) goto done;
for (pwszApp = wszExe + wcslen(wszExe); *pwszApp != L'\\' && pwszApp > wszExe; pwszApp--); if (*pwszApp == L'\\') pwszApp++;
cchDir = wcslen(wszDir); cch = cchDir + sizeofSTRW(c_wszManFileName) + 4; __try { wszManifest = (LPWSTR)_alloca(cch * sizeof(WCHAR)); } __except(EXCEPTION_STACK_OVERFLOW) { wszManifest = NULL; } if (wszManifest == NULL) { SetLastError(ERROR_OUTOFMEMORY); goto done; }
wcscpy(wszManifest, wszDir); wszManifest[cchDir] = L'\\'; wszManifest[cchDir + 1] = L'\0'; wcscat(wszManifest, c_wszManFileName);
cchDir = wcslen(wszDir); cch = 2 * cchDir + wcslen(pwszApp) + sizeofSTRW(c_wszACFileName) + sizeofSTRW(c_wszDumpSuffix) + 4; __try { wszFiles = (LPWSTR)_alloca(cch * sizeof(WCHAR)); } __except(EXCEPTION_STACK_OVERFLOW) { wszFiles = NULL; } if (wszFiles == NULL) { SetLastError(ERROR_OUTOFMEMORY); goto done; }
wcscpy(wszFiles, wszDir); wszFiles[cchDir] = L'\\'; wszFiles[cchDir + 1] = L'\0'; wcscat(wszFiles, pwszApp); wcscat(wszFiles, c_wszDumpSuffix);
// build a exception context...
hth = OpenThread(THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION, FALSE, dwtid); if (hth == NULL) goto done;
ZeroMemory(&cxt, sizeof(cxt)); cxt.ContextFlags = CONTEXT_CONTROL; TESTBOOL(hr, GetThreadContext(hth, &cxt)); if (FAILED(hr)) goto done;
ZeroMemory(&er, sizeof(er)); er.ExceptionCode = 0xcfffffff; #ifdef _X86_
// this cast is ok to do cuz we know we're on an x86 machine
er.ExceptionAddress = (PVOID)cxt.Eip;
#elif _IA64_
// this cast is ok to do cuz we know we're on an ia64 machine
er.ExceptionAddress = (PVOID)cxt.StIIP; #endif
ep.ExceptionRecord = &er; ep.ContextRecord = &cxt;
// generate the minidump
ZeroMemory(&smdo, sizeof(smdo)); smdo.cbSMDO = sizeof(smdo); #ifdef MANIFEST_HEAP
smdo.ulThread = c_ulThreadWriteDefault; smdo.ulThreadEx = c_ulThreadWriteDefault; smdo.ulMod = c_ulModuleWriteDefault; #else
smdo.ulThread = ThreadWriteThread | ThreadWriteContext | ThreadWriteStack; smdo.ulMod = ModuleWriteModule | ModuleWriteMiscRecord | ModuleWriteDataSeg; #endif
smdo.dwThreadID = dwtid; smdo.dfOptions = dfCollectSig; smdo.pvFaultAddr = (UINT64)er.ExceptionAddress; #if defined(_X86_) || defined(_IA64_)
smdo.pEP = (UINT64)&ep; smdo.fEPClient = FALSE; #endif
smdo.wszModFullPath[0] = L'\0'; wcscpy(smdo.wszAppFullPath, wszExe); wcscpy(smdo.wszMod, L"hungapp"); #ifdef MANIFEST_HEAP
if (InternalGenFullAndTriageMinidumps(hProc, dwpid, wszFiles, NULL, &smdo, f64bit) == FALSE) #else
if (InternalGenerateMinidump(hProc, dwpid, wszFiles, &smdo) == FALSE) #endif
goto done;
ThawAllThreads(&st); fThreadsHeld = FALSE;
// if the app requested it, notify it of that we're done fetching the
// dump
if (hNotify != NULL) SetEvent(hNotify);
// log an event- don't care if it fails or not.
TESTHR(hr, LogHang(smdo.wszApp, smdo.rgAppVer, smdo.wszMod, smdo.rgModVer, smdo.pvOffset, f64bit));
// generate all the URLs & file paths we'll need for reporting
TESTHR(hr, BuildManifestURLs(smdo.wszApp, smdo.wszMod, smdo.rgAppVer, smdo.rgModVer, smdo.pvOffset, f64bit, &wszStage1, &wszStage2, &wszCorpPath, &pbBuf)); if (FAILED(hr)) goto done;
TESTHR(hr, GetVerName(smdo.wszAppFullPath, wszAppName, sizeofSTRW(wszAppName))); if (FAILED(hr)) goto done;
wszAppName[sizeofSTRW(wszAppName) - 1] = L'\0';
// we created the wszDump buffer above big enuf to hold both the
// dumpfile path as well as the app compat filename. So make
// use of that right now.
cchSep = wcslen(wszFiles); pwszAppCompat = wszFiles + cchSep + 1; wcscpy(pwszAppCompat, wszDir); pwszAppCompat[cchDir] = L'\\'; pwszAppCompat[cchDir + 1] = L'\0'; wcscat(pwszAppCompat, c_wszACFileName);
// if we succeed, turn the NULL following the dump file path into
// the DW separator character
TESTBOOL(hr, GetAppCompatData(wszExe, smdo.wszModFullPath, pwszAppCompat)); if (SUCCEEDED(hr)) wszFiles[cchSep] = DW_FILESEP;
// get the header text
dw = LoadStringW(g_hInstance, IDS_HHDRTXT, wszBuffer, sizeofSTRW(wszBuffer)); if (dw == 0) goto done;
cch = dw + wcslen(wszAppName) + 1; __try { wszHdr = (LPWSTR)_alloca(cch * sizeof(WCHAR)); } __except(EXCEPTION_STACK_OVERFLOW) { wszHdr = NULL; } if (wszHdr == NULL) { SetLastError(ERROR_OUTOFMEMORY); goto done; }
swprintf(wszHdr, wszBuffer, wszAppName);
ZeroMemory(&dwmb, sizeof(dwmb)); dwmb.wszTitle = wszAppName; dwmb.wszHdr = wszHdr; dwmb.nidErrMsg = IDS_HERRMSG; dwmb.wszStage1 = wszStage1; dwmb.wszStage2 = wszStage2; dwmb.wszBrand = c_wszDWBrand; dwmb.wszFileList = wszFiles; dwmb.fIsMSApp = fMSApp; dwmb.wszCorpPath = wszCorpPath; dwmb.wszEventSrc = c_wszHangEventSrc;
// check and see if the system is shutting down. If so, CreateProcess is
// gonna pop up some annoying UI that we can't get rid of, so we don't
// want to call it if we know it's gonna happen.
if (GetSystemMetrics(SM_SHUTTINGDOWN)) goto done;
frrvRet = StartDWManifest(oCfg, dwmb, wszManifest);
done: // preserve the error code so that the following calls don't overwrite it
dw = GetLastError();
if (fThreadsHeld) ThawAllThreads(&st);
if (hProc != NULL) CloseHandle(hProc); if (hth != NULL) CloseHandle(hth); if (wszFiles != NULL) { if (pwszAppCompat != NULL) { wszFiles[cchSep] = L'\0'; DeleteFileW(pwszAppCompat); } #ifdef MANIFEST_HEAP
DeleteFullAndTriageMiniDumps(wszFiles); #else
DeleteFileW(wszFiles); #endif
} if (wszManifest != NULL) DeleteFileW(wszManifest); if (wszDir != NULL) { DeleteTempDirAndFile(wszDir, FALSE); MyFree(wszDir); }
if (pbBuf != NULL) MyFree(pbBuf);
SetLastError(dw);
return frrvRet; }
|