|
|
/******************************************************************************
Copyright (c) 2000 Microsoft Corporation
Module Name: frhang.cpp
Abstract: Implements hang reporting
Revision History: created derekm 07/07/00
******************************************************************************/
#include "stdafx.h"
#ifdef THIS_FILE
#undef THIS_FILE
#endif
static char __szTraceSourceFile[] = __FILE__; #define THIS_FILE __szTraceSourceFile
#ifndef ARRAYSIZE
#define ARRAYSIZE(sz) (sizeof(sz)/sizeof(sz[0]))
#endif
///////////////////////////////////////////////////////////////////////////////
// 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, wszErrorSig = NULL; LPWSTR wszFiles = NULL, wszDir = NULL, wszManifest = NULL; LPWSTR pwszAppCompat = NULL; WCHAR wszExe[MAX_PATH], wszAppName[MAX_PATH]; WCHAR wszAppVer[MAX_PATH]; WCHAR *pwszApp; WCHAR wszBuffer[512]; DWORD dw, cch, cchDir, cchSep, cchwszFiles; 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) { TextLogOut("Hang fault for %ls\r\n", wszExe); }
// 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, and if we can't get that, no sense in
// going on further...
hProc = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, dwpid); if (hProc == NULL) { if (ERROR_ACCESS_DENIED == GetLastError()) { // this usually means we are doing an impersonation ("RunAs...")
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...
WCHAR *pwsz;
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; }
// freeze all the threads in the app so we can get a good snapshot of it &
// read the context
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 == GetExceptionCode() ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { wszManifest = NULL; } if (wszManifest == NULL) { SetLastError(ERROR_OUTOFMEMORY); goto done; }
StringCchCopyW(wszManifest, cch, wszDir); wszManifest[cchDir] = L'\\'; wszManifest[cchDir + 1] = L'\0'; StringCchCatNW(wszManifest, cch, c_wszManFileName, cch - wcslen(wszManifest));
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 == GetExceptionCode() ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { wszFiles = NULL; } if (wszFiles == NULL) { SetLastError(ERROR_OUTOFMEMORY); goto done; }
cchwszFiles = cch; StringCchCopyW(wszFiles, cch, wszDir); wszFiles[cchDir] = L'\\'; wszFiles[cchDir + 1] = L'\0'; StringCchCatNW(wszFiles, cch, pwszApp, cch - wcslen(wszFiles)); StringCchCatNW(wszFiles, cch, c_wszDumpSuffix, cch - wcslen(wszFiles));
// 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; #if defined(_X86_)
// this cast is ok to do cuz we know we're on an x86 machine
er.ExceptionAddress = (PVOID)cxt.Eip;
#elif defined(_AMD64_)
er.ExceptionAddress = (PVOID)cxt.Rip;
#elif defined(_IA64_)
// this cast is ok to do cuz we know we're on an ia64 machine
er.ExceptionAddress = (PVOID)cxt.StIIP;
#else
#error "No Target Architecture"
#endif
ep.ExceptionRecord = &er; ep.ContextRecord = &cxt;
// generate the minidump
ZeroMemory(&smdo, sizeof(smdo)); smdo.cbSMDO = sizeof(smdo); smdo.ulThread = c_ulThreadWriteDefault; smdo.ulThreadEx = c_ulThreadWriteDefault; smdo.ulMod = c_ulModuleWriteDefault; smdo.dwThreadID = dwtid; smdo.dfOptions = dfCollectSig; smdo.pvFaultAddr = (UINT64)er.ExceptionAddress; #if defined(_AMD64_) || defined(_X86_) || defined(_IA64_)
smdo.pEP = (UINT64)&ep; smdo.fEPClient = FALSE; #endif
smdo.wszModFullPath[0] = L'\0'; StringCbCopyW(smdo.wszAppFullPath, sizeof(smdo.wszAppFullPath), wszExe); StringCbCopyW(smdo.wszMod, sizeof(smdo.wszMod), L"hungapp"); if (InternalGenFullAndTriageMinidumps(hProc, dwpid, wszFiles, NULL, &smdo, f64bit) == FALSE) 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, GetErrorSignature(smdo.wszApp, smdo.wszMod, smdo.rgAppVer, smdo.rgModVer, smdo.pvOffset, f64bit, &wszErrorSig, 0)); if (FAILED(hr)) goto done;
TESTHR(hr, GetVerName(smdo.wszAppFullPath, wszAppName, sizeofSTRW(wszAppName), NULL, 0, NULL, 0, TRUE, FALSE)); 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; StringCchCopyW(pwszAppCompat, cchwszFiles - cchSep - 1, wszDir); pwszAppCompat[cchDir] = L'\\'; pwszAppCompat[cchDir + 1] = L'\0'; StringCchCatNW(pwszAppCompat, cchwszFiles - cchSep - 1, c_wszACFileName, cchwszFiles - cchSep - 1 - wcslen(pwszAppCompat));
// 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 == GetExceptionCode() ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { wszHdr = NULL; } if (wszHdr == NULL) { SetLastError(ERROR_OUTOFMEMORY); goto done; }
StringCchPrintfW(wszHdr, cch, 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.wszErrorSig = wszErrorSig; 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); } DeleteFullAndTriageMiniDumps(wszFiles); } if (wszManifest != NULL) DeleteFileW(wszManifest); if (wszDir != NULL) { DeleteTempDirAndFile(wszDir, FALSE); MyFree(wszDir); }
if (pbBuf != NULL) MyFree(pbBuf);
if (wszErrorSig != NULL) MyFree((LPVOID) wszErrorSig);
SetLastError(dw);
return frrvRet; }
|