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

/******************************************************************************
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;
}