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.
910 lines
27 KiB
910 lines
27 KiB
/****************************************************************************
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
dumprep.cpp
|
|
|
|
Abstract:
|
|
hang manager intermediate app
|
|
*** IMPORTANT NOTE: this links with the single threaded CRT static lib. If
|
|
it is changed to be multithreaded for some odd reason,
|
|
then the sources file must be modified to link to
|
|
libcmt.lib.
|
|
|
|
Revision History:
|
|
|
|
DerekM created 08/16/00
|
|
|
|
****************************************************************************/
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// tracing
|
|
|
|
#ifdef THIS_FILE
|
|
#undef THIS_FILE
|
|
#endif
|
|
static char __szTraceSourceFile[] = __FILE__;
|
|
#define THIS_FILE __szTraceSourceFile
|
|
|
|
|
|
#include "stdafx.h"
|
|
#include "malloc.h"
|
|
#include "faultrep.h"
|
|
#include "pfrcfg.h"
|
|
#include <shlwapi.h>
|
|
#include <strsafe.h>
|
|
|
|
|
|
enum EOp
|
|
{
|
|
eopNone = 0,
|
|
eopHang,
|
|
eopDump,
|
|
eopEvent
|
|
};
|
|
|
|
enum ECheckType
|
|
{
|
|
ctNone = -1,
|
|
ctKernel = 0,
|
|
ctUser,
|
|
ctShutdown,
|
|
ctNumChecks
|
|
};
|
|
|
|
struct SCheckData
|
|
{
|
|
LPCWSTR wszRegPath;
|
|
LPCWSTR wszRunVal;
|
|
LPCWSTR wszEventName;
|
|
LPCSTR szFnName;
|
|
BOOL fUseData;
|
|
BOOL fDelDump;
|
|
};
|
|
|
|
struct SQueuePruneData
|
|
{
|
|
LPWSTR wszVal;
|
|
FILETIME ftFault;
|
|
};
|
|
|
|
char *eopStr[] =
|
|
{
|
|
"eopNone",
|
|
"eopHang",
|
|
"eopDump",
|
|
"eopEvent"
|
|
};
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// constants
|
|
|
|
const char c_szKSFnName[] = "ReportEREventDW";
|
|
const char c_szUserFnName[] = "ReportFaultFromQueue";
|
|
|
|
SCheckData g_scd[ctNumChecks] =
|
|
{
|
|
{ c_wszRKKrnl, c_wszRVKFC, c_wszMutKrnlName, c_szKSFnName, FALSE, FALSE },
|
|
{ c_wszRKUser, c_wszRVUFC, c_wszMutUserName, c_szUserFnName, TRUE, TRUE },
|
|
{ c_wszRKShut, c_wszRVSEC, c_wszMutShutName, c_szKSFnName, FALSE, FALSE },
|
|
};
|
|
|
|
#define EV_ACCESS_ALL GENERIC_ALL | STANDARD_RIGHTS_ALL
|
|
#define EV_ACCESS_RS GENERIC_READ | SYNCHRONIZE
|
|
|
|
#define pfn_VALONLY pfn_REPORTEREVENTDW
|
|
#define pfn_VALDATA pfn_REPORTFAULTFROMQ
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// globals
|
|
|
|
BOOL g_fDeleteReg = TRUE;
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// misc
|
|
|
|
// **************************************************************************
|
|
LONG __stdcall ExceptionTrap(_EXCEPTION_POINTERS *ExceptionInfo)
|
|
{
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
}
|
|
|
|
|
|
// **************************************************************************
|
|
HRESULT PruneQ(HKEY hkeyQ, DWORD cQSize, DWORD cMaxQSize, DWORD cchMaxVal,
|
|
DWORD cbMaxData)
|
|
{
|
|
USE_TRACING("PruneQ");
|
|
|
|
SQueuePruneData *pqpd = NULL;
|
|
FILETIME ft;
|
|
HRESULT hr = NOERROR;
|
|
LPWSTR pwsz, pwszCurrent = NULL;
|
|
DWORD cchVal, cbData, dwType, cToDel = 0, cInDelList = 0;
|
|
DWORD i, iEntry, dw, cValid = 0;
|
|
|
|
VALIDATEPARM(hr, (hkeyQ == NULL));
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
if (cMaxQSize > cQSize)
|
|
goto done;
|
|
|
|
// don't need to do a +1 here (as in user faults) because we're not going
|
|
// to add anything to the queue after this. This just ensures that we
|
|
// don't report too many things
|
|
cToDel = cQSize - cMaxQSize;
|
|
|
|
// alloc the various buffers that we'll need:
|
|
// the delete list
|
|
// the current file we're working with
|
|
// the data blob associated with the current file
|
|
cbData = (sizeof(SQueuePruneData) + (cchMaxVal * sizeof(WCHAR))) * cToDel;
|
|
pwszCurrent = (LPWSTR)MyAlloc(cchMaxVal * sizeof(WCHAR));
|
|
pqpd = (SQueuePruneData *)MyAlloc(cbData);
|
|
if (pwszCurrent == NULL || pqpd == NULL)
|
|
{
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
hr = E_OUTOFMEMORY;
|
|
goto done;
|
|
}
|
|
|
|
// intiailize all the string pointers in the delete list
|
|
pwsz = (LPWSTR)((BYTE *)pqpd + (sizeof(SQueuePruneData) * cToDel));
|
|
for (i = 0; i < cToDel; i++)
|
|
{
|
|
pqpd[i].ftFault.dwHighDateTime = 0;
|
|
pqpd[i].ftFault.dwLowDateTime = 0;
|
|
pqpd[i].wszVal = pwsz;
|
|
pqpd[i].wszVal[0] = L'\0';
|
|
pwsz += cchMaxVal;
|
|
}
|
|
|
|
|
|
// ok, get a list of all the valid items and build an array in sorted order
|
|
// so that we can easily pick off the top n items
|
|
for(iEntry = 0; iEntry < cQSize; iEntry++)
|
|
{
|
|
cchVal = cchMaxVal;
|
|
cbData = sizeof(ft);
|
|
dw = RegEnumValueW(hkeyQ, iEntry, pwszCurrent, &cchVal, 0, &dwType,
|
|
(PBYTE)&ft, &cbData);
|
|
if (dw == ERROR_NO_MORE_ITEMS)
|
|
break;
|
|
else if (dw != ERROR_SUCCESS)
|
|
continue;
|
|
else if (dwType != REG_BINARY || cbData != sizeof(ft))
|
|
{
|
|
RegDeleteValueW(hkeyQ, pwszCurrent);
|
|
continue;
|
|
}
|
|
|
|
for (i = 0; i < cInDelList; i++)
|
|
{
|
|
if ((ft.dwHighDateTime < pqpd[i].ftFault.dwHighDateTime) ||
|
|
(ft.dwHighDateTime == pqpd[i].ftFault.dwHighDateTime &&
|
|
ft.dwLowDateTime < pqpd[i].ftFault.dwLowDateTime))
|
|
break;
|
|
}
|
|
|
|
// if it's in the middle of the current list, then we gotta move
|
|
// stuff around
|
|
if (cInDelList > 0 && i < cInDelList - 1)
|
|
{
|
|
LPWSTR pwszTemp = pqpd[cInDelList - 1].wszVal;
|
|
|
|
MoveMemory(&pqpd[i], &pqpd[i + 1],
|
|
(cInDelList - i) * sizeof(SQueuePruneData));
|
|
|
|
pqpd[i].wszVal = pwszTemp;
|
|
}
|
|
|
|
if (i < cToDel)
|
|
{
|
|
// note that this copy is safe cuz each string slot is the same
|
|
// size as the buffer pointed to by pwszCurrent and that buffer is
|
|
// protected from overflow by the size we pass into
|
|
// RegEnumValueW()
|
|
StringCbCopyW(pqpd[i].wszVal, cchMaxVal * sizeof(WCHAR), pwszCurrent);
|
|
pqpd[i].ftFault = ft;
|
|
|
|
if (cInDelList < cToDel)
|
|
cInDelList++;
|
|
}
|
|
|
|
cValid++;
|
|
}
|
|
|
|
// if there aren't enuf valid entries to warrant a purge, then don't purge
|
|
if (cValid < cMaxQSize)
|
|
goto done;
|
|
|
|
cToDel = MyMin(cToDel, cValid - cMaxQSize + 1);
|
|
|
|
// purge enuf entries that we go down to 1 below our max (since we have to
|
|
// be adding 1 to get here- don't want that 1 to drive us over the limit
|
|
for(i = 0; i < cToDel; i++)
|
|
{
|
|
if (pqpd[i].wszVal != NULL)
|
|
RegDeleteValueW(hkeyQ, pqpd[i].wszVal);
|
|
}
|
|
|
|
done:
|
|
if (pqpd != NULL)
|
|
MyFree(pqpd);
|
|
if (pwszCurrent != NULL)
|
|
MyFree(pwszCurrent);
|
|
|
|
return hr;
|
|
}
|
|
|
|
// **************************************************************************
|
|
HRESULT CheckQSizeAndPrune(HKEY hkeyQ)
|
|
{
|
|
USE_TRACING("CheckQueueSizeAndPrune");
|
|
|
|
CPFFaultClientCfg oCfg;
|
|
HRESULT hr = NOERROR;
|
|
DWORD cMaxQSize = 0, cDefMaxQSize = 10;
|
|
DWORD cQSize, cchMaxVal, cbMaxData, dwType;
|
|
DWORD cb, dw;
|
|
HKEY hkey = NULL;
|
|
|
|
VALIDATEPARM(hr, (hkeyQ == NULL));
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
// read the policy settings for UI and reporting
|
|
TESTHR(hr, oCfg.Read(eroPolicyRO));
|
|
|
|
cMaxQSize = oCfg.get_MaxUserQueueSize();
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
cMaxQSize = cDefMaxQSize;
|
|
hr = NOERROR;
|
|
}
|
|
else
|
|
{
|
|
// there are other ways of disabling these reporting modes, so don't
|
|
// honor a like we would for user faults
|
|
if (cMaxQSize == 0)
|
|
cMaxQSize = 1;
|
|
|
|
// -1 means there is no limit
|
|
else if (cMaxQSize == (DWORD)-1)
|
|
{
|
|
hr = NOERROR;
|
|
goto done;
|
|
}
|
|
|
|
else if (cMaxQSize > c_cMaxQueue)
|
|
cMaxQSize = c_cMaxQueue;
|
|
}
|
|
|
|
__try
|
|
{
|
|
// determine what the Q size is.
|
|
TESTERR(hr, RegQueryInfoKey(hkeyQ, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
&cQSize, &cchMaxVal, &cbMaxData, NULL, NULL));
|
|
if (SUCCEEDED(hr) && (cQSize >= cMaxQSize))
|
|
{
|
|
cchMaxVal++;
|
|
TESTHR(hr, PruneQ(hkeyQ, cQSize, cMaxQSize, cchMaxVal, cbMaxData));
|
|
}
|
|
else
|
|
{
|
|
hr = NOERROR;
|
|
}
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
}
|
|
|
|
done:
|
|
return hr;
|
|
}
|
|
|
|
|
|
// **************************************************************************
|
|
void DeleteQueuedEvents(HKEY hkey, LPWSTR wszVal, DWORD cchMaxVal,
|
|
ECheckType ct)
|
|
{
|
|
DWORD cchVal, dw;
|
|
HKEY hkeyRun = NULL;
|
|
HRESULT hr = NOERROR;
|
|
|
|
USE_TRACING("DeleteQueuedEvents");
|
|
|
|
VALIDATEPARM(hr, (hkey == NULL || wszVal == NULL));
|
|
if (FAILED(hr))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
goto done;
|
|
}
|
|
|
|
for(;;)
|
|
{
|
|
cchVal = cchMaxVal;
|
|
dw = RegEnumValueW(hkey, 0, wszVal, &cchVal, NULL, NULL,
|
|
NULL, NULL);
|
|
if (dw != ERROR_SUCCESS && dw != ERROR_NO_MORE_ITEMS)
|
|
{
|
|
SetLastError(dw);
|
|
goto done;
|
|
}
|
|
|
|
if (dw == ERROR_NO_MORE_ITEMS)
|
|
break;
|
|
|
|
RegDeleteValueW(hkey, wszVal);
|
|
if (ct == ctUser)
|
|
DeleteFullAndTriageMiniDumps(wszVal);
|
|
}
|
|
|
|
// gotta delete our value out of the Run key so we don't run
|
|
// unnecessarily again...
|
|
dw = RegOpenKeyExW(HKEY_LOCAL_MACHINE, c_wszRKRun, 0,
|
|
KEY_ALL_ACCESS, &hkeyRun);
|
|
if (dw != ERROR_SUCCESS)
|
|
goto done;
|
|
|
|
RegDeleteValueW(hkeyRun, g_scd[ct].wszRunVal);
|
|
|
|
done:
|
|
if (hkeyRun != NULL)
|
|
RegCloseKey(hkeyRun);
|
|
|
|
return;
|
|
}
|
|
|
|
// **************************************************************************
|
|
void ReportEvents(HMODULE hmod, ECheckType ct)
|
|
{
|
|
CPFFaultClientCfg oCfg;
|
|
EFaultRepRetVal frrv;
|
|
pfn_VALONLY pfnVO = NULL;
|
|
pfn_VALDATA pfnVD = NULL;
|
|
EEventType eet = eetKernelFault;
|
|
HRESULT hr = NOERROR;
|
|
HANDLE hmut = NULL;
|
|
LPWSTR wszVal = NULL;
|
|
LPBYTE pbData = NULL, pbDataToUse = NULL;
|
|
EEnDis eedReport, eedUI;
|
|
DWORD cchVal = 0, cchMaxVal = 0, cbMaxData = 0, cVals = 0;
|
|
DWORD dw, cbData = 0, *pcbData = NULL, iRegRead = 0;
|
|
DWORD dwType;
|
|
HKEY hkey;
|
|
|
|
USE_TRACING("ReportEvents");
|
|
|
|
VALIDATEPARM(hr, ((ct <= ctNone && ct >= ctNumChecks) || hmod == NULL));
|
|
if (FAILED(hr))
|
|
return;
|
|
|
|
// assume hmod is valid cuz we do a check in wWinMain to make sure it is
|
|
// before calling this fn
|
|
if (g_scd[ct].fUseData)
|
|
pfnVD = (pfn_VALDATA)GetProcAddress(hmod, g_scd[ct].szFnName);
|
|
else
|
|
pfnVO = (pfn_VALONLY)GetProcAddress(hmod, g_scd[ct].szFnName);
|
|
|
|
VALIDATEPARM(hr, (pfnVD == NULL && pfnVO == NULL));
|
|
if (FAILED(hr))
|
|
return;
|
|
|
|
// read the policy settings for UI and reporting
|
|
TESTHR(hr, oCfg.Read(eroPolicyRO));
|
|
if (FAILED(hr))
|
|
return;
|
|
|
|
eedReport = oCfg.get_DoReport();
|
|
eedUI = oCfg.get_ShowUI();
|
|
|
|
if (eedUI != eedEnabled && eedUI != eedDisabled &&
|
|
eedUI != eedEnabledNoCheck)
|
|
eedUI = eedEnabled;
|
|
|
|
if (eedReport != eedEnabled && eedReport != eedDisabled)
|
|
eedReport = eedEnabled;
|
|
|
|
// only want one user at a time going thru this
|
|
hmut = OpenMutexW(SYNCHRONIZE, FALSE, g_scd[ct].wszEventName);
|
|
VALIDATEPARM(hr, (hmut == NULL));
|
|
if (FAILED(hr))
|
|
return;
|
|
|
|
// the default value above is eetKernelFault, so only need to change if
|
|
// it's a shutdown
|
|
if (ct == ctShutdown)
|
|
eet = eetShutdown;
|
|
|
|
__try
|
|
{
|
|
__try
|
|
{
|
|
// give this wait five minutes. If the code doesn't complete by
|
|
// then, then we're either held up by DW (which means an admin
|
|
// aleady passed thru here) or something has barfed and is holding
|
|
// the mutex.
|
|
dw = WaitForSingleObject(hmut, 300000);
|
|
if (dw != WAIT_OBJECT_0 && dw != WAIT_ABANDONED)
|
|
__leave;
|
|
|
|
dw = RegOpenKeyExW(HKEY_LOCAL_MACHINE, g_scd[ct].wszRegPath, 0,
|
|
KEY_ALL_ACCESS, &hkey);
|
|
if (dw != ERROR_SUCCESS)
|
|
__leave;
|
|
|
|
// make sure we only have a fixed number of errors to report.
|
|
if (ct == ctShutdown || ct == ctKernel)
|
|
CheckQSizeAndPrune(hkey);
|
|
|
|
// determine how big the valuename is & allocate a buffer for it
|
|
dw = RegQueryInfoKeyW(hkey, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
&cVals, &cchMaxVal, &cbMaxData, NULL, NULL);
|
|
if (dw != ERROR_SUCCESS || cVals == 0 || cchMaxVal == 0)
|
|
__leave;
|
|
|
|
cchMaxVal++;
|
|
|
|
// get us some buffers to hold the data bits we're interested in...
|
|
wszVal = (LPWSTR)MyAlloc(cchMaxVal * sizeof(WCHAR));
|
|
if (wszVal == NULL)
|
|
__leave;
|
|
|
|
// if we're completely disabled, then nuke all the queued stuff
|
|
// and bail
|
|
if (eedUI == eedDisabled && eedReport == eedDisabled)
|
|
{
|
|
DeleteQueuedEvents(hkey, wszVal, cchMaxVal, ct);
|
|
__leave;
|
|
}
|
|
|
|
if (g_scd[ct].fUseData)
|
|
{
|
|
pbData = (LPBYTE) MyAlloc(cbMaxData);
|
|
if (pbData == NULL)
|
|
__leave;
|
|
|
|
pbDataToUse = pbData;
|
|
pcbData = &cbData;
|
|
}
|
|
|
|
iRegRead = 0;
|
|
|
|
do
|
|
{
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
|
|
cchVal = cchMaxVal;
|
|
cbData = cbMaxData;
|
|
dw = RegEnumValueW(hkey, iRegRead, wszVal, &cchVal, NULL,
|
|
&dwType, pbDataToUse, pcbData);
|
|
if (dw != ERROR_SUCCESS && dw != ERROR_NO_MORE_ITEMS)
|
|
__leave;
|
|
|
|
if (dw == ERROR_NO_MORE_ITEMS)
|
|
break;
|
|
|
|
// make sure that the file exists
|
|
hFile = CreateFileW(wszVal, GENERIC_READ,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
|
|
OPEN_EXISTING, 0, NULL);
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
BOOL fInc = TRUE;
|
|
|
|
if (GetLastError() == ERROR_FILE_NOT_FOUND ||
|
|
GetLastError() == ERROR_PATH_NOT_FOUND)
|
|
{
|
|
dw = RegDeleteValueW(hkey, wszVal);
|
|
if (dw == ERROR_SUCCESS ||
|
|
dw == ERROR_FILE_NOT_FOUND ||
|
|
dw == ERROR_PATH_NOT_FOUND)
|
|
fInc = FALSE;
|
|
}
|
|
|
|
if (fInc)
|
|
iRegRead++;
|
|
|
|
continue;
|
|
}
|
|
CloseHandle(hFile);
|
|
hFile = INVALID_HANDLE_VALUE;
|
|
|
|
if (g_scd[ct].fUseData)
|
|
{
|
|
// if the type isn't REG_BINARY, then someone wrote an
|
|
// invalid blob to the registry. We have to ignore it.
|
|
if (dwType == REG_BINARY)
|
|
frrv = (*pfnVD)(wszVal, pbData, cbData);
|
|
else
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
frrv = frrvOk;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
frrv = (*pfnVO)(eet, wszVal, NULL);
|
|
}
|
|
|
|
// if the call succeeds (or the data we fed to it was invalid)
|
|
// then nuke the reg key & dump file
|
|
if (GetLastError() == ERROR_INVALID_PARAMETER ||
|
|
(g_fDeleteReg && frrv == frrvOk))
|
|
{
|
|
dw = RegDeleteValueW(hkey, wszVal);
|
|
if (dw != ERROR_SUCCESS && dw != ERROR_FILE_NOT_FOUND &&
|
|
dw != ERROR_PATH_NOT_FOUND)
|
|
{
|
|
iRegRead++;
|
|
continue;
|
|
}
|
|
|
|
if (g_scd[ct].fDelDump && g_fDeleteReg)
|
|
DeleteFullAndTriageMiniDumps(wszVal);
|
|
}
|
|
#if 0
|
|
// if we timed out, then clean up the reg key. If this is the
|
|
// last report we're gonna make, also clean up the Run key
|
|
else if (g_fDeleteReg && frrv == frrvErrTimeout)
|
|
{
|
|
RegDeleteValueW(hkey, wszVal);
|
|
dw = RegQueryInfoKeyW(hkey, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
&cVals, NULL, NULL, NULL, NULL);
|
|
if (dw != ERROR_SUCCESS || cVals > 0)
|
|
__leave;
|
|
else
|
|
break;
|
|
|
|
}
|
|
#endif
|
|
else
|
|
{
|
|
// don't delete the Run key if we got an error and didn't
|
|
// delete the fault key
|
|
if (frrv != frrvOk)
|
|
__leave;
|
|
}
|
|
}
|
|
while(1);
|
|
|
|
RegCloseKey(hkey);
|
|
hkey = NULL;
|
|
|
|
// gotta delete our value out of the Run key so we don't run
|
|
// unnecessarily again...
|
|
dw = RegOpenKeyExW(HKEY_LOCAL_MACHINE, c_wszRKRun, 0,
|
|
KEY_ALL_ACCESS, &hkey);
|
|
if (dw != ERROR_SUCCESS)
|
|
__leave;
|
|
|
|
RegDeleteValueW(hkey, g_scd[ct].wszRunVal);
|
|
}
|
|
|
|
__finally
|
|
{
|
|
}
|
|
}
|
|
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
}
|
|
|
|
if (hmut != NULL)
|
|
{
|
|
ReleaseMutex(hmut);
|
|
CloseHandle(hmut);
|
|
}
|
|
if (hkey != NULL)
|
|
RegCloseKey(hkey);
|
|
if (pbData != NULL)
|
|
MyFree(pbData);
|
|
if (wszVal != NULL)
|
|
MyFree(wszVal);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// wmain
|
|
|
|
// **************************************************************************
|
|
int __cdecl wmain(int argc, WCHAR **argv)
|
|
{
|
|
EFaultRepRetVal frrv = frrvErrNoDW;
|
|
SMDumpOptions smdo, *psmdo = &smdo;
|
|
ECheckType ct = ctNone;
|
|
HMODULE hmod = NULL;
|
|
HANDLE hevNotify = NULL, hproc = NULL, hmem = NULL;
|
|
LPWSTR wszDump = NULL;
|
|
WCHAR wszMod[MAX_PATH];
|
|
WCHAR wszCmdLine[MAX_PATH];
|
|
|
|
DWORD dwpid, dwtid;
|
|
BOOL f64bit = FALSE;
|
|
int i;
|
|
EOp eop = eopNone;
|
|
HRESULT hr = NOERROR;
|
|
|
|
// we don't want to have any faults get trapped anywhere.
|
|
SetErrorMode(SEM_NOGPFAULTERRORBOX | SEM_NOALIGNMENTFAULTEXCEPT |
|
|
SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
|
|
SetUnhandledExceptionFilter(ExceptionTrap);
|
|
|
|
INIT_TRACING
|
|
|
|
USE_TRACING("DumpRep.wmain");
|
|
|
|
VALIDATEPARM(hr, (argc < 2 || argc > 8));
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
dwpid = _wtol(argv[1]);
|
|
|
|
ZeroMemory(&smdo, sizeof(smdo));
|
|
|
|
for (i = 2; i < argc; i++)
|
|
{
|
|
if (argv[i][0] != L'-')
|
|
continue;
|
|
|
|
switch(argv[i][1])
|
|
{
|
|
// debug flag to prevent deletion of reg entries
|
|
case L'E':
|
|
case L'e':
|
|
#if defined(NO_WAY_DEBUG) || defined(NO_WAY__DEBUG)
|
|
g_fDeleteReg = FALSE;
|
|
#endif
|
|
break;
|
|
|
|
// user or kernel faults or shutdowns
|
|
case L'K':
|
|
case L'k':
|
|
case L'U':
|
|
case L'u':
|
|
case L'S':
|
|
case L's':
|
|
if (eop != eopNone)
|
|
goto done;
|
|
|
|
eop = eopEvent;
|
|
|
|
ErrorTrace(0, " eopEvent = %S", argv[i] );
|
|
|
|
|
|
// to workaround the desktop hanging while all Run processes
|
|
// do their thing, we spawn a another copy of ourselves and
|
|
// immediately exit.
|
|
if (argv[i][2] != L'G' && argv[i][2] != L'g')
|
|
{
|
|
PROCESS_INFORMATION pi;
|
|
STARTUPINFOW si;
|
|
PWCHAR pFileName;
|
|
|
|
if (GetModuleFileNameW(NULL, wszMod, sizeofSTRW(wszMod)) == 0)
|
|
{
|
|
goto done;
|
|
}
|
|
|
|
// Now create commandline to be passed to CreateProcessW
|
|
if (!(pFileName = wcschr(wszMod,L'\\')))
|
|
{
|
|
pFileName = wszMod;
|
|
}
|
|
StringCbCopyW(wszCmdLine, sizeof(wszCmdLine), pFileName);
|
|
|
|
if (argv[i][1] == L'K' || argv[i][1] == L'k')
|
|
StringCbCatW(wszCmdLine, sizeof(wszMod), L" 0 -KG");
|
|
else if (argv[i][1] == L'U' || argv[i][1] == L'u')
|
|
StringCbCatW(wszCmdLine, sizeof(wszMod), L" 0 -UG");
|
|
else
|
|
StringCbCatW(wszCmdLine, sizeof(wszMod), L" 0 -SG");
|
|
|
|
ZeroMemory(&si, sizeof(si));
|
|
si.cb = sizeof(si);
|
|
|
|
ErrorTrace(0, " spawning \'%S\'", wszCmdLine );
|
|
|
|
if (CreateProcessW(wszMod, wszCmdLine, NULL, NULL, FALSE, 0, NULL,
|
|
NULL, &si, &pi))
|
|
{
|
|
CloseHandle(pi.hThread);
|
|
CloseHandle(pi.hProcess);
|
|
}
|
|
|
|
goto done;
|
|
}
|
|
|
|
else
|
|
{
|
|
if (argv[i][1] == L'K' || argv[i][1] == L'k')
|
|
ct = ctKernel;
|
|
else if (argv[i][1] == L'U' || argv[i][1] == L'u')
|
|
ct = ctUser;
|
|
else
|
|
ct = ctShutdown;
|
|
}
|
|
break;
|
|
|
|
// hangs
|
|
case L'H':
|
|
case L'h':
|
|
if (i + 1 >= argc || eop != eopNone)
|
|
goto done;
|
|
|
|
eop = eopHang;
|
|
|
|
#ifdef _WIN64
|
|
if (argv[i][2] == L'6')
|
|
f64bit = TRUE;
|
|
#endif
|
|
dwtid = _wtol(argv[++i]);
|
|
|
|
if (argc > i + 1)
|
|
{
|
|
hevNotify = OpenEventW(EVENT_MODIFY_STATE | SYNCHRONIZE,
|
|
FALSE, argv[++i]);
|
|
}
|
|
break;
|
|
|
|
// dumps
|
|
case L'D':
|
|
case L'd':
|
|
if (i + 3 >= argc || wszDump != NULL || eop != eopNone)
|
|
goto done;
|
|
|
|
eop = eopDump;
|
|
|
|
ZeroMemory(&smdo, sizeof(smdo));
|
|
smdo.ulMod = _wtol(argv[++i]);
|
|
smdo.ulThread = _wtol(argv[++i]);
|
|
wszDump = argv[++i];
|
|
|
|
if (argv[i - 3][2] == L'T' || argv[i - 3][2] == L't')
|
|
{
|
|
if (i + 1 >= argc)
|
|
goto done;
|
|
|
|
smdo.dwThreadID = _wtol(argv[++i]);
|
|
smdo.dfOptions = dfFilterThread;
|
|
}
|
|
else if (argv[i - 3][2] == L'S' || argv[i - 3][2] == L's')
|
|
{
|
|
if (i + 2 >= argc)
|
|
goto done;
|
|
|
|
smdo.dwThreadID = _wtol(argv[++i]);
|
|
smdo.ulThreadEx = _wtol(argv[++i]);
|
|
smdo.dfOptions = dfFilterThreadEx;
|
|
}
|
|
else if (argv[i - 3][2] == L'M' || argv[i - 3][2] == L'm')
|
|
{
|
|
HANDLE hmemRemote = NULL;
|
|
LPVOID pvMem = NULL;
|
|
LPWSTR wszShareSdmoName;
|
|
|
|
if (i + 1 >= argc)
|
|
goto done;
|
|
|
|
hproc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwpid);
|
|
if (hproc == NULL)
|
|
{
|
|
DWORD dwAwShit = GetLastError();
|
|
goto done;
|
|
}
|
|
|
|
#ifdef _WIN64
|
|
// hmemRemote = (HANDLE)_wtoi64(argv[++i]);
|
|
#else
|
|
// hmemRemote = (HANDLE)_wtol(argv[++i]);
|
|
#endif
|
|
wszShareSdmoName = argv[++i];
|
|
if (wszShareSdmoName == NULL)
|
|
{
|
|
goto done;
|
|
}
|
|
hmem = OpenFileMappingW(FILE_MAP_WRITE,
|
|
FALSE,
|
|
wszShareSdmoName);
|
|
if ((hmem == NULL) ||
|
|
(hmem == INVALID_HANDLE_VALUE))
|
|
{
|
|
DWORD dwAwShit = GetLastError();
|
|
goto done;
|
|
}
|
|
|
|
pvMem = MapViewOfFile(hmem, FILE_MAP_WRITE,
|
|
0, 0, 0);
|
|
if (pvMem == NULL)
|
|
goto done;
|
|
|
|
psmdo = (SMDumpOptions *)pvMem;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
ErrorTrace(0, " eop = %s", eopStr[eop]);
|
|
|
|
// if we didn't get an operation, no point in doing anything else...
|
|
if (eop == eopNone)
|
|
goto done;
|
|
|
|
if (GetSystemDirectoryW(wszMod, sizeofSTRW(wszMod)) == 0)
|
|
{
|
|
goto done;
|
|
}
|
|
StringCbCatW(wszMod, sizeof(wszMod), L"\\faultrep.dll");
|
|
|
|
hmod = LoadLibraryExW(wszMod, NULL, 0);
|
|
VALIDATEPARM(hr, (hmod == NULL));
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
switch(eop)
|
|
{
|
|
// user or kernel faults:
|
|
case eopEvent:
|
|
ReportEvents(hmod, ct);
|
|
break;
|
|
|
|
// dumps
|
|
case eopDump:
|
|
{
|
|
pfn_CREATEMINIDUMPW pfnCM;
|
|
|
|
VALIDATEPARM(hr, (wszDump == NULL));
|
|
if (FAILED(hr))
|
|
goto done;
|
|
|
|
pfnCM = (pfn_CREATEMINIDUMPW)GetProcAddress(hmod,
|
|
"CreateMinidumpW");
|
|
VALIDATEPARM(hr, (pfnCM == NULL));
|
|
if (SUCCEEDED(hr))
|
|
frrv = (*pfnCM)(dwpid, wszDump, psmdo) ? frrvOk : frrvErr;
|
|
|
|
break;
|
|
}
|
|
|
|
// hangs
|
|
case eopHang:
|
|
{
|
|
pfn_REPORTHANG pfnRH;
|
|
|
|
pfnRH = (pfn_REPORTHANG)GetProcAddress(hmod, "ReportHang");
|
|
VALIDATEPARM(hr, (pfnRH == NULL));
|
|
if (SUCCEEDED(hr))
|
|
frrv = (*pfnRH)(dwpid, dwtid, f64bit, hevNotify);
|
|
|
|
break;
|
|
}
|
|
|
|
// err, shouldn't get here
|
|
default:
|
|
break;
|
|
}
|
|
|
|
done:
|
|
if (hmod != NULL)
|
|
FreeLibrary(hmod);
|
|
if (hproc != NULL)
|
|
CloseHandle(hproc);
|
|
if (hmem != NULL)
|
|
CloseHandle(hmem);
|
|
if (hevNotify != NULL)
|
|
CloseHandle(hevNotify);
|
|
if (psmdo != NULL && psmdo != &smdo)
|
|
UnmapViewOfFile((LPVOID)psmdo);
|
|
|
|
return frrv;
|
|
}
|