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.
345 lines
7.7 KiB
345 lines
7.7 KiB
/*++
|
|
|
|
Copyright (c) 1998 Microsoft Corporation
|
|
|
|
Module Name :
|
|
|
|
randfail.c
|
|
|
|
Abstract :
|
|
|
|
This module implements the initialization function for the random
|
|
failure library, plus the code to determine if it's time to fail.
|
|
|
|
Author :
|
|
|
|
Sam Neely
|
|
|
|
Revision History :
|
|
|
|
--*/
|
|
|
|
#include <windows.h>
|
|
#include <stdio.h>
|
|
#include "traceint.h"
|
|
#include "randint.h"
|
|
#include "exchmem.h"
|
|
|
|
static long s_nCount = 0;
|
|
long nFailRate = kDontFail;
|
|
DWORD dwRandFailTlsIndex=0xffffffff;
|
|
const DWORD g_dwMaxCallStack = 1024;
|
|
|
|
//
|
|
// Call stack buffer array
|
|
//
|
|
|
|
CHAR **g_ppchCallStack = NULL;
|
|
|
|
//
|
|
// Randfail call stack file and its handle
|
|
//
|
|
|
|
CHAR g_szRandFailFile[MAX_PATH+1];
|
|
HANDLE g_hRandFailFile = INVALID_HANDLE_VALUE;
|
|
HANDLE g_hRandFailMutex = INVALID_HANDLE_VALUE;
|
|
|
|
//
|
|
// Number of buffers allocated for randfail call stack
|
|
//
|
|
|
|
LONG g_cCallStack = 1;
|
|
|
|
//
|
|
// Current index in the buffer array
|
|
//
|
|
|
|
LONG g_iCallStack = 0;
|
|
|
|
VOID
|
|
DumpCallStack( DWORD_PTR *rgdwCall,
|
|
DWORD dwCallers,
|
|
PBYTE pbCallstack,
|
|
DWORD& cbCallstack )
|
|
/*++
|
|
Routine description:
|
|
|
|
Dump call stack into the given buffer.
|
|
|
|
Arguments:
|
|
|
|
rgdwCall - Array of caller's address
|
|
dwCallers - Number of callers
|
|
pbCallstack - The buffer to put the call stack string into
|
|
cbCallstack - In: How big the buffer is, Out: how much stuff I have put
|
|
in there
|
|
|
|
Return value:
|
|
|
|
None.
|
|
--*/
|
|
{
|
|
DWORD i;
|
|
CHAR Buffer[g_dwMaxCallStack];
|
|
DWORD dwLine = 0;
|
|
DWORD dwBufferAvail = cbCallstack - 2*sizeof(CHAR);
|
|
PBYTE pbStart = pbCallstack;
|
|
DWORD dwBytesWritten = 0;
|
|
BOOL fRetry = TRUE;
|
|
char szModuleName[MAX_PATH];
|
|
char* pszFileName;
|
|
char* pszExtension;
|
|
|
|
_ASSERT( pbStart );
|
|
_ASSERT( cbCallstack > 0 );
|
|
|
|
cbCallstack = 0;
|
|
|
|
//
|
|
// Get the executable's filename and point past the last slash
|
|
// in the path, if it's present. Also, whack off the extension
|
|
// if it's .EXE
|
|
//
|
|
if (GetModuleFileName(NULL, szModuleName, MAX_PATH) == 0) {
|
|
strcpy (szModuleName, "Unknown");
|
|
}
|
|
|
|
pszFileName = strrchr(szModuleName, '\\');
|
|
if (pszFileName == NULL) {
|
|
pszFileName = szModuleName;
|
|
} else {
|
|
pszFileName++;
|
|
}
|
|
|
|
pszExtension = strrchr(pszFileName, '.');
|
|
if (pszExtension) {
|
|
if (_stricmp(pszExtension+1, "exe") == 0) {
|
|
*pszExtension = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Format a header line
|
|
//
|
|
|
|
dwBytesWritten = _snprintf((char*)pbStart,
|
|
g_dwMaxCallStack,
|
|
"*** %s, Process: %d(%#x), Thread: %d(%#x) ***\r\n",
|
|
pszFileName,
|
|
GetCurrentProcessId(), GetCurrentProcessId(),
|
|
GetCurrentThreadId(), GetCurrentThreadId());
|
|
|
|
cbCallstack += dwBytesWritten;
|
|
pbStart += dwBytesWritten;
|
|
dwBufferAvail -= dwBytesWritten;
|
|
|
|
//
|
|
// Dump call stack
|
|
// Note that we skip the first two entries. These are the internal
|
|
// calls to ExchmemGetCallStack and g_TestTrace
|
|
for (i = 2; i < dwCallers && rgdwCall[i] != 0; i++)
|
|
{
|
|
ExchmemFormatSymbol(
|
|
GetCurrentProcess(),
|
|
rgdwCall[i],
|
|
Buffer,
|
|
g_dwMaxCallStack );
|
|
dwLine = strlen( Buffer );
|
|
if ( dwLine+2 < dwBufferAvail ) {
|
|
CopyMemory( pbStart, Buffer, dwLine );
|
|
*(pbStart+dwLine) = '\r';
|
|
*(pbStart+dwLine+1) = '\n';
|
|
dwBufferAvail -= (dwLine + 2*sizeof(CHAR));
|
|
pbStart += (dwLine + 2*sizeof(CHAR));
|
|
cbCallstack +=( dwLine + 2*sizeof(CHAR));
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Add an extra \r\n at the end
|
|
//
|
|
|
|
*(pbCallstack + cbCallstack) = '\r';
|
|
*(pbCallstack + cbCallstack + 1) = '\n';
|
|
cbCallstack += 2;
|
|
|
|
//
|
|
// Dump it to the log file as well, if we do have a log file
|
|
//
|
|
|
|
if ( INVALID_HANDLE_VALUE != g_hRandFailFile &&
|
|
INVALID_HANDLE_VALUE != g_hRandFailMutex ) {
|
|
|
|
WaitForSingleObject (g_hRandFailMutex, INFINITE);
|
|
|
|
DWORD dwOffset = SetFilePointer( g_hRandFailFile, 0, 0, FILE_END );
|
|
|
|
//
|
|
// if the file is too big then we need to truncate it
|
|
//
|
|
if (dwOffset > dwMaxFileSize)
|
|
{
|
|
SetFilePointer(g_hRandFailFile, 0, 0, FILE_BEGIN);
|
|
SetEndOfFile(g_hRandFailFile);
|
|
}
|
|
try_again:
|
|
BOOL b = WriteFile(
|
|
g_hRandFailFile,
|
|
pbCallstack,
|
|
cbCallstack,
|
|
&dwBytesWritten,
|
|
NULL );
|
|
|
|
if ( b == FALSE || dwBytesWritten != cbCallstack )
|
|
{
|
|
DWORD dwError = GetLastError();
|
|
|
|
if( dwError && fRetry )
|
|
{
|
|
fRetry = FALSE;
|
|
Sleep( 100 );
|
|
goto try_again;
|
|
}
|
|
INT_TRACE( "Error writing to file: %d, number of bytes %d:%d\n",
|
|
dwError,
|
|
cbCallstack,
|
|
dwBytesWritten );
|
|
}
|
|
|
|
ReleaseMutex(g_hRandFailMutex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// See if it's time for this API to fail
|
|
//
|
|
// Note: This routine was renamed from fTimeToFail to g_TestTrace
|
|
// to hide the symbol from someone dumping the dll
|
|
//
|
|
|
|
extern "C" __declspec(dllexport)
|
|
int
|
|
__stdcall
|
|
g_TestTrace(void) {
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check to see if it's time for an instrumented API to fail.
|
|
|
|
Note: This routine was renamed from fTimeToFail to g_TestTrace
|
|
to hide the symbol from someone dumping the dll
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
true if it's time for us to fail, false if not or we're disabled.
|
|
|
|
--*/
|
|
LONG l;
|
|
|
|
// Never fail?
|
|
if (nFailRate == kDontFail)
|
|
return 0;
|
|
|
|
// Have failures been suspended?
|
|
if (dwRandFailTlsIndex != 0xffffffff &&
|
|
TlsGetValue (dwRandFailTlsIndex) != NULL)
|
|
return 0;
|
|
|
|
// This is good enough for now..
|
|
l = InterlockedIncrement(&s_nCount) % nFailRate;
|
|
|
|
if ( l == 0 ) {
|
|
|
|
// We are going to fail
|
|
if ( g_ppchCallStack ) {
|
|
LONG i = 0;
|
|
const DWORD dwMaxCallStack = 20;
|
|
DWORD dwCallStackBuffer = g_dwMaxCallStack;
|
|
DWORD_PTR rgdwCaller[dwMaxCallStack];
|
|
|
|
i = InterlockedIncrement( &g_iCallStack );
|
|
if ( i <= g_cCallStack ) {
|
|
i--;
|
|
if ( g_ppchCallStack[i] ) {
|
|
ZeroMemory( rgdwCaller, sizeof(DWORD_PTR)*dwMaxCallStack );
|
|
ExchmemGetCallStack(rgdwCaller, dwMaxCallStack);
|
|
DumpCallStack( rgdwCaller, dwMaxCallStack, (PBYTE)g_ppchCallStack[i], dwCallStackBuffer );
|
|
}
|
|
} else {
|
|
InterlockedExchange( &g_iCallStack, g_cCallStack );
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
} else
|
|
return FALSE;
|
|
}
|
|
|
|
extern "C" __declspec(dllexport)
|
|
void
|
|
__stdcall
|
|
g_TestTraceDisable(void) {
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Function to temporarily suspend g_TestTrace's ability to return a
|
|
failure. This is used when you want to call one of the instrumented
|
|
APIs that you don't want to fail. This function is nestable up to
|
|
128(abitrary) levels deep.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
if (dwRandFailTlsIndex == 0xffffffff)
|
|
return;
|
|
|
|
SIZE_T OldValue = (SIZE_T)TlsGetValue(dwRandFailTlsIndex);
|
|
ASSERT (OldValue <= 128);
|
|
TlsSetValue(dwRandFailTlsIndex, (LPVOID)(OldValue+1));
|
|
}
|
|
|
|
|
|
extern "C" __declspec(dllexport)
|
|
void
|
|
__stdcall
|
|
g_TestTraceEnable(void) {
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Resume g_TestTrace's normal functionality if the nesting level has
|
|
returned to zero.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
if (dwRandFailTlsIndex == 0xffffffff)
|
|
return;
|
|
|
|
SIZE_T OldValue = (SIZE_T)TlsGetValue(dwRandFailTlsIndex);
|
|
ASSERT (OldValue > 0 && OldValue <= 128);
|
|
TlsSetValue(dwRandFailTlsIndex, (LPVOID)(OldValue-1));
|
|
}
|