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.
 
 
 
 
 
 

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));
}