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
383 lines
12 KiB
/******************************************************************************
|
|
|
|
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;
|
|
}
|