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.
2795 lines
72 KiB
2795 lines
72 KiB
/*++
|
|
|
|
Copyright (c) 1997-1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
debug.c
|
|
|
|
Abstract:
|
|
|
|
This routine reads input from STDIN and initializes the debug structure.
|
|
It reads a list of subsystems to debug and a severity level.
|
|
|
|
Author:
|
|
|
|
Billy Fuller
|
|
|
|
Environment
|
|
|
|
User mode, winnt32
|
|
*/
|
|
#include <ntreppch.h>
|
|
#pragma hdrstop
|
|
|
|
#include "debug.h"
|
|
|
|
// #include <imagehlp.h>
|
|
#include <dbghelp.h>
|
|
#include <frs.h>
|
|
#include <winbase.h>
|
|
#include <mapi.h>
|
|
#include <ntfrsapi.h>
|
|
#include <info.h>
|
|
|
|
extern PCHAR LatestChanges[];
|
|
extern ULONG MaxCoRetryTimeoutCount;
|
|
extern ULONG MaxCoRetryTimeoutMinutes;
|
|
extern DWORD CommTimeoutInMilliSeconds;
|
|
extern ULONG DsPollingLongInterval;
|
|
extern ULONG DsPollingShortInterval;
|
|
extern DWORD PartnerClockSkew;
|
|
extern ULONG ChangeOrderAgingDelay;
|
|
extern DWORD ReplicaTombstone;
|
|
|
|
|
|
|
|
//
|
|
// Track the thread IDs of known threads for the debug log header.
|
|
//
|
|
typedef struct _KNOWN_THREAD {
|
|
PWCHAR Name; // print Name of thread.
|
|
DWORD Id; // Id returned by CreateThread()
|
|
PTHREAD_START_ROUTINE EntryPoint; // entry point
|
|
} KNOWN_THREAD, *PKNOWN_THREAD;
|
|
|
|
KNOWN_THREAD KnownThreadArray[20];
|
|
|
|
//
|
|
// Send mail will not work if the Default User is unable to send
|
|
// mail on this machine.
|
|
//
|
|
MapiRecipDesc Recips =
|
|
{0, MAPI_TO, 0, 0, 0, NULL};
|
|
|
|
MapiMessage Message =
|
|
{ 0, 0, 0, NULL, NULL, NULL, 0, 0, 1, &Recips, 0, 0 };
|
|
|
|
LPMAPILOGON MailLogon;
|
|
LPMAPILOGOFF MailLogoff;
|
|
LPMAPISENDMAIL MailSend;
|
|
HANDLE MailLib;
|
|
LHANDLE MailSession;
|
|
|
|
|
|
|
|
WCHAR DbgExePathW[MAX_PATH+1];
|
|
CHAR DbgExePathA[MAX_PATH+1];
|
|
CHAR DbgSearchPath[MAX_PATH+1];
|
|
|
|
//
|
|
// Flush the trace log every so many lines.
|
|
//
|
|
LONG DbgFlushInterval = 100000;
|
|
|
|
#define DEFAULT_DEBUG_MAX_LOG (20000)
|
|
|
|
LONG StackTraceCount = 100;
|
|
LONG DbgRaiseCount = 50;
|
|
|
|
|
|
OSVERSIONINFOEXW OsInfo;
|
|
SYSTEM_INFO SystemInfo;
|
|
PCHAR ProcessorArchName[12] = {"INTEL", "MIPS", "Alpha", "PPC", "SHX",
|
|
"ARM", "IA64", "Alpha64", "MSIL", "AMD64",
|
|
"IA32-on-WIN64", "unknown"};
|
|
|
|
//
|
|
// Suffixes for saved log files and saved assertion files
|
|
// E.g., (ntfrs_0005.log)
|
|
// (DebugInfo.LogFile, DebugInfo.LogFiles, LOG_FILE_SUFFIX)
|
|
//
|
|
#define LOG_FILE_FORMAT L"%ws_%04d%ws"
|
|
#define LOG_FILE_SUFFIX L".log"
|
|
#define ASSERT_FILE_SUFFIX L"_assert.log"
|
|
|
|
|
|
//
|
|
// Disable the generation of compressed staging files for any local changes.
|
|
//
|
|
BOOL DisableCompressionStageFiles;
|
|
ULONG GOutLogRepeatInterval;
|
|
|
|
//
|
|
// Client side ldap search timeout in minutes. Reg value "Ldap Search Timeout In Minutes". Default is 10 minutes.
|
|
//
|
|
DWORD LdapSearchTimeoutInMinutes;
|
|
|
|
//
|
|
// Client side ldap_connect timeout in seconds. Reg value "Ldap Bind Timeout In Seconds". Default is 30 seconds.
|
|
//
|
|
DWORD LdapBindTimeoutInSeconds;
|
|
|
|
SC_HANDLE
|
|
FrsOpenServiceHandle(
|
|
IN PTCHAR MachineName,
|
|
IN PTCHAR ServiceName
|
|
);
|
|
|
|
|
|
VOID
|
|
FrsPrintRpcStats(
|
|
IN ULONG Severity,
|
|
IN PNTFRSAPI_INFO Info, OPTIONAL
|
|
IN DWORD Tabs
|
|
);
|
|
|
|
#if DBG
|
|
|
|
//
|
|
// Collection of debug info from the registry and the CLI
|
|
//
|
|
DEBUGARG DebugInfo;
|
|
|
|
//
|
|
// allow multiple servers on one machine
|
|
//
|
|
PWCHAR ServerName = NULL;
|
|
PWCHAR IniFileName = NULL;
|
|
GUID *ServerGuid = NULL;
|
|
|
|
|
|
|
|
VOID
|
|
DbgLogDisable(
|
|
VOID
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Disable DPRINT log.
|
|
|
|
Arguments:
|
|
None.
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DbgLogDisable:"
|
|
|
|
DebLock();
|
|
if (HANDLE_IS_VALID(DebugInfo.LogFILE)) {
|
|
FrsFlushFile(L"LogFILE", DebugInfo.LogFILE);
|
|
DbgFlushInterval = DebugInfo.LogFlushInterval;
|
|
FRS_CLOSE(DebugInfo.LogFILE);
|
|
}
|
|
DebugInfo.LogFILE = INVALID_HANDLE_VALUE;
|
|
DebUnLock();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
VOID
|
|
DbgOpenLogFile(
|
|
VOID
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Open the log file by creating names like ntfrs0001.log.
|
|
NumFiles as a 4 digit decimal number and Suffix is like ".log".
|
|
|
|
DebLock() must be held (DO NOT CALL DPRINT IN THIS FUNCTION!)
|
|
|
|
Arguments:
|
|
Base
|
|
Suffix
|
|
NumFiles
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DbgOpenLogFile:"
|
|
|
|
WCHAR LogPath[MAX_PATH + 1];
|
|
SECURITY_ATTRIBUTES SecurityAttributes;
|
|
|
|
if (DebugInfo.Disabled) {
|
|
DebugInfo.LogFILE = INVALID_HANDLE_VALUE;
|
|
return;
|
|
}
|
|
|
|
if (_snwprintf(LogPath, MAX_PATH, LOG_FILE_FORMAT, DebugInfo.LogFile,
|
|
DebugInfo.LogFiles, LOG_FILE_SUFFIX) < 0) {
|
|
|
|
DebugInfo.LogFILE = INVALID_HANDLE_VALUE;
|
|
return;
|
|
}
|
|
//
|
|
// Set this handle to be inheritable so that it can be passed
|
|
// to new processes that ntfrs starts (lodctr, unlodctr) and cause
|
|
// them to write their errors in the ntfrs debug logs.
|
|
//
|
|
SecurityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
SecurityAttributes.bInheritHandle = TRUE;
|
|
SecurityAttributes.lpSecurityDescriptor = NULL; // not same as NULL DACL
|
|
|
|
DebugInfo.LogFILE = CreateFile(LogPath, // lpszName
|
|
GENERIC_READ | GENERIC_WRITE, // fdwAccess
|
|
FILE_SHARE_READ, // fdwShareMode
|
|
&SecurityAttributes, // lpsa
|
|
CREATE_ALWAYS, // fdwCreate
|
|
FILE_FLAG_BACKUP_SEMANTICS, // fdwAttrAndFlags
|
|
NULL); // hTemplateFile
|
|
}
|
|
|
|
|
|
VOID
|
|
DbgShiftLogFiles(
|
|
IN PWCHAR Base,
|
|
IN PWCHAR Suffix,
|
|
IN PWCHAR RemoteBase,
|
|
IN ULONG NumFiles
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Shift the files through the range of log/assert file names
|
|
(Base_5_Suffix -> Base_4_Suffix -> ... Base_0_Suffix
|
|
|
|
DebLock() must be held (DO NOT CALL DPRINT IN THIS FUNCTION!)
|
|
|
|
Arguments:
|
|
Base
|
|
Suffix
|
|
NumFiles
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DbgShiftLogFiles:"
|
|
|
|
ULONG i;
|
|
WCHAR FromPath[MAX_PATH + 1];
|
|
WCHAR ToPath[MAX_PATH + 1];
|
|
ULONGLONG Now;
|
|
|
|
//
|
|
// No history
|
|
//
|
|
if ((NumFiles < 2) || DebugInfo.Disabled) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Save the log file as an assert file
|
|
//
|
|
for (i = 2; i <= NumFiles; ++i) {
|
|
if (_snwprintf(ToPath, MAX_PATH, LOG_FILE_FORMAT, Base, i-1, Suffix) > 0) {
|
|
if (_snwprintf(FromPath, MAX_PATH, LOG_FILE_FORMAT, Base, i, Suffix) > 0) {
|
|
MoveFileEx(FromPath, ToPath, MOVEFILE_REPLACE_EXISTING |
|
|
MOVEFILE_WRITE_THROUGH);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Copy the last log file to a remote share
|
|
// WARN - the system time is used to create a unique file
|
|
// name. This means that the remote share can
|
|
// fill up!
|
|
//
|
|
if (!RemoteBase) {
|
|
return;
|
|
}
|
|
|
|
GetSystemTimeAsFileTime((FILETIME *)&Now);
|
|
|
|
|
|
if (_snwprintf(FromPath, MAX_PATH, LOG_FILE_FORMAT, Base, NumFiles-1, Suffix) > 0) {
|
|
if (_snwprintf(ToPath,
|
|
MAX_PATH,
|
|
L"%ws%ws%08x%_%08x",
|
|
RemoteBase,
|
|
Suffix,
|
|
PRINTQUAD(Now)) > 0) {
|
|
CopyFileEx(FromPath, ToPath, NULL, NULL, FALSE, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
DbgSendMail(
|
|
IN PCHAR Subject,
|
|
IN PCHAR Content
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Send mail as the default user.
|
|
|
|
Arguments:
|
|
Subject
|
|
Message
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DbgSendMail:"
|
|
|
|
DWORD MStatus;
|
|
WCHAR FullPathToDll[MAX_PATH + 1];
|
|
|
|
//
|
|
// Nobody to send mail to
|
|
//
|
|
if (!DebugInfo.Recipients) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Load the mail library and find our entry points
|
|
//
|
|
FullPathToDll[0] = L'\0';
|
|
GetSystemDirectory(FullPathToDll,sizeof(FullPathToDll)/sizeof(FullPathToDll[0]));
|
|
if ((wcslen(FullPathToDll) == 0 )||
|
|
(wcslen(FullPathToDll) + wcslen(L"\\mapi32.dll"))
|
|
>= sizeof(FullPathToDll)) {
|
|
return;
|
|
}
|
|
wcscat(FullPathToDll,L"\\mapi32.dll");
|
|
|
|
MailLib = LoadLibrary(FullPathToDll);
|
|
if(!HANDLE_IS_VALID(MailLib)) {
|
|
DPRINT_WS(0, ":S: Load mapi32.dll failed;", GetLastError());
|
|
return;
|
|
}
|
|
MailLogon = (LPMAPILOGON)GetProcAddress(MailLib, "MAPILogon");
|
|
MailLogoff = (LPMAPILOGOFF)GetProcAddress(MailLib, "MAPILogoff");
|
|
MailSend = (LPMAPISENDMAIL)GetProcAddress(MailLib, "MAPISendMail");
|
|
|
|
if (!MailLogon || !MailLogoff || !MailSend) {
|
|
DPRINT(0, ":S: ERROR - Could not find mail symbols in mapi32.dll\n");
|
|
FreeLibrary(MailLib);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Log on with the specified profile
|
|
//
|
|
MStatus = MailLogon(0, DebugInfo.Profile, 0, 0, 0, &MailSession);
|
|
if(MStatus) {
|
|
DPRINT1_WS(0, ":S: ERROR - MailLogon failed; MStatus %d;",
|
|
MStatus, GetLastError());
|
|
FreeLibrary(MailLib);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Send the mail
|
|
//
|
|
Recips.lpszName = DebugInfo.Recipients;
|
|
Message.lpszSubject = Subject;
|
|
Message.lpszNoteText = Content;
|
|
MStatus = MailSend(MailSession, 0, &Message, 0, 0);
|
|
|
|
if(MStatus) {
|
|
DPRINT1_WS(0, ":S: ERROR - MailSend failed MStatus %d;", MStatus, GetLastError());
|
|
}
|
|
|
|
//
|
|
// Log off and free the library
|
|
//
|
|
MailLogoff(MailSession, 0, 0, 0);
|
|
FreeLibrary(MailLib);
|
|
}
|
|
|
|
|
|
VOID
|
|
DbgSymbolPrint(
|
|
IN ULONG Severity,
|
|
IN PCHAR Debsub,
|
|
IN UINT LineNo,
|
|
IN ULONG_PTR Addr
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Print a symbol
|
|
|
|
Arguments:
|
|
Addr
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DbgSymbolPrint:"
|
|
|
|
ULONG_PTR Displacement = 0;
|
|
|
|
struct MyMymbol {
|
|
IMAGEHLP_SYMBOL Symbol;
|
|
char Path[MAX_PATH];
|
|
} MySymbol;
|
|
|
|
|
|
try {
|
|
ZeroMemory(&MySymbol, sizeof(MySymbol));
|
|
MySymbol.Symbol.SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
|
|
MySymbol.Symbol.MaxNameLength = MAX_PATH;
|
|
|
|
if (!SymGetSymFromAddr(ProcessHandle, Addr, &Displacement, &MySymbol.Symbol)) {
|
|
DebPrint(Severity, "++ \t 0x%08x: Unknown Symbol (WStatus %s)\n",
|
|
Debsub, LineNo, Addr, ErrLabelW32(GetLastError()));
|
|
} else
|
|
DebPrint(Severity, "++ \t 0x%08x: %s\n",
|
|
Debsub, LineNo, Addr, MySymbol.Symbol.Name);
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
DebPrint(Severity, "++ \t 0x%08x: Unknown Symbol (WStatus %s)\n",
|
|
Debsub, LineNo, Addr, ErrLabelW32(GetExceptionCode()));
|
|
/* FALL THROUGH */
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
DbgModulePrint(
|
|
IN PWCHAR Prepense,
|
|
IN ULONG Addr
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Print info about a module containing Addr
|
|
|
|
Arguments:
|
|
Prepense - prettypring; printed at the beginning of each line
|
|
Addr
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DbgModulePrint:"
|
|
|
|
IMAGEHLP_MODULE mi;
|
|
|
|
try {
|
|
ZeroMemory(&mi, sizeof(mi));
|
|
mi.SizeOfStruct = sizeof(IMAGEHLP_MODULE);
|
|
|
|
if (!SymGetModuleInfo(ProcessHandle, Addr, &mi)) {
|
|
DPRINT1_WS(0, "++ %ws <unknown module;", Prepense, GetLastError());
|
|
} else
|
|
DPRINT2(0, "++ %ws Module is %ws\n", Prepense, mi.ModuleName);
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
/* FALL THROUGH */
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
DbgStackPrint(
|
|
IN ULONG Severity,
|
|
IN PCHAR Debsub,
|
|
IN UINT LineNo,
|
|
IN PULONG_PTR Stack,
|
|
IN ULONG Depth
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Print the previously acquired stack trace.
|
|
|
|
Arguments:
|
|
Prepense - prettypring; printed at the beginning of each line
|
|
Stack - "return PC" from each frame
|
|
Depth - Only this many frames
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DbgStackPrint:"
|
|
|
|
ULONG i;
|
|
|
|
try {
|
|
for (i = 0; i < Depth && *Stack; ++i, ++Stack) {
|
|
DbgSymbolPrint(Severity, Debsub, LineNo, *Stack);
|
|
}
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
/* FALL THROUGH */
|
|
}
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
DbgStackTrace(
|
|
IN PULONG_PTR Stack,
|
|
IN ULONG Depth
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Trace the stack back up to Depth frames. The current frame is included.
|
|
|
|
Arguments:
|
|
Stack - Saves the "return PC" from each frame
|
|
Depth - Only this many frames
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DbgStackTrace:"
|
|
|
|
ULONG WStatus;
|
|
HANDLE ThreadHandle;
|
|
STACKFRAME Frame;
|
|
ULONG i = 0;
|
|
CONTEXT Context;
|
|
ULONG FrameAddr;
|
|
|
|
//
|
|
// I don't know how to generate a stack for an alpha, yet. So, just
|
|
// to get into the build, disable the stack trace on alphas.
|
|
//
|
|
if (Stack) {
|
|
*Stack = 0;
|
|
}
|
|
#if ALPHA
|
|
return;
|
|
#elif IA64
|
|
|
|
//
|
|
// Need stack dump init for IA64.
|
|
//
|
|
|
|
return;
|
|
|
|
#else
|
|
|
|
//
|
|
// init
|
|
//
|
|
|
|
ZeroMemory(&Context, sizeof(Context));
|
|
ThreadHandle = GetCurrentThread();
|
|
|
|
try { try {
|
|
Context.ContextFlags = CONTEXT_FULL;
|
|
if (!GetThreadContext(ThreadHandle, &Context)) {
|
|
DPRINT_WS(0, "++ Can't get context;", GetLastError());
|
|
}
|
|
|
|
//
|
|
// let's start clean
|
|
//
|
|
ZeroMemory(&Frame, sizeof(STACKFRAME));
|
|
|
|
//
|
|
// from nt\private\windows\screg\winreg\server\stkwalk.c
|
|
//
|
|
Frame.AddrPC.Segment = 0;
|
|
Frame.AddrPC.Mode = AddrModeFlat;
|
|
|
|
#ifdef _M_IX86
|
|
Frame.AddrFrame.Offset = Context.Ebp;
|
|
Frame.AddrFrame.Mode = AddrModeFlat;
|
|
|
|
Frame.AddrStack.Offset = Context.Esp;
|
|
Frame.AddrStack.Mode = AddrModeFlat;
|
|
|
|
Frame.AddrPC.Offset = (DWORD)Context.Eip;
|
|
#elif defined(_M_MRX000)
|
|
Frame.AddrPC.Offset = (DWORD)Context.Fir;
|
|
#elif defined(_M_ALPHA)
|
|
Frame.AddrPC.Offset = (DWORD)Context.Fir;
|
|
#endif
|
|
|
|
|
|
|
|
#if 0
|
|
//
|
|
// setup the program counter
|
|
//
|
|
Frame.AddrPC.Mode = AddrModeFlat;
|
|
Frame.AddrPC.Segment = (WORD)Context.SegCs;
|
|
Frame.AddrPC.Offset = (ULONG)Context.Eip;
|
|
|
|
//
|
|
// setup the frame pointer
|
|
//
|
|
Frame.AddrFrame.Mode = AddrModeFlat;
|
|
Frame.AddrFrame.Segment = (WORD)Context.SegSs;
|
|
Frame.AddrFrame.Offset = (ULONG)Context.Ebp;
|
|
|
|
//
|
|
// setup the stack pointer
|
|
//
|
|
Frame.AddrStack.Mode = AddrModeFlat;
|
|
Frame.AddrStack.Segment = (WORD)Context.SegSs;
|
|
Frame.AddrStack.Offset = (ULONG)Context.Esp;
|
|
|
|
#endif
|
|
|
|
for (i = 0; i < (Depth - 1); ++i) {
|
|
if (!StackWalk(
|
|
IMAGE_FILE_MACHINE_I386, // DWORD MachineType
|
|
ProcessHandle, // HANDLE hProcess
|
|
ThreadHandle, // HANDLE hThread
|
|
&Frame, // LPSTACKFRAME StackFrame
|
|
NULL, //(PVOID)&Context, // PVOID ContextRecord
|
|
NULL, // PREAD_PROCESS_MEMORY_ROUTINE ReadMemoryRoutine
|
|
SymFunctionTableAccess, // PFUNCTION_TABLE_ACCESS_ROUTINE FunctionTableAccessRoutine
|
|
SymGetModuleBase, // PGET_MODULE_BASE_ROUTINE GetModuleBaseRoutine
|
|
NULL)) { // PTRANSLATE_ADDRESS_ROUTINE TranslateAddress
|
|
|
|
WStatus = GetLastError();
|
|
|
|
//DPRINT1_WS(0, "++ Can't get stack address for level %d;", i, WStatus);
|
|
break;
|
|
}
|
|
if (StackTraceCount-- > 0) {
|
|
DPRINT1(5, "++ Frame.AddrReturn.Offset: %08x \n", Frame.AddrReturn.Offset);
|
|
DbgSymbolPrint(5, DEBSUB, __LINE__, Frame.AddrReturn.Offset);
|
|
//DPRINT1(5, "++ Frame.AddrPC.Offset: %08x \n", Frame.AddrPC.Offset);
|
|
//DbgSymbolPrint(5, DEBSUB, __LINE__, Frame.AddrPC.Offset);
|
|
}
|
|
|
|
*Stack++ = Frame.AddrReturn.Offset;
|
|
*Stack = 0;
|
|
//
|
|
// Base of stack?
|
|
//
|
|
if (!Frame.AddrReturn.Offset) {
|
|
break;
|
|
}
|
|
}
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
/* FALL THROUGH */
|
|
} } finally {
|
|
FRS_CLOSE(ThreadHandle);
|
|
}
|
|
return;
|
|
#endif ALPHA
|
|
}
|
|
|
|
|
|
VOID
|
|
DbgPrintStackTrace(
|
|
IN ULONG Severity,
|
|
IN PCHAR Debsub,
|
|
IN UINT LineNo
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Acquire and print the stack
|
|
|
|
Arguments:
|
|
Severity
|
|
Debsub
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DbgPrintStackTrace:"
|
|
|
|
ULONG_PTR Stack[32];
|
|
|
|
DbgStackTrace(Stack, ARRAY_SZ(Stack) );
|
|
DbgStackPrint(Severity, Debsub, LineNo, Stack, ARRAY_SZ(Stack) );
|
|
}
|
|
|
|
|
|
VOID
|
|
DbgStackInit(
|
|
VOID
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Initialize anything necessary to get a stack trace
|
|
|
|
Arguments:
|
|
None.
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DbgStackInit:"
|
|
|
|
//
|
|
// Initialize the symbol subsystem
|
|
//
|
|
if (!SymInitialize(ProcessHandle, NULL, FALSE)) {
|
|
DPRINT_WS(0, ":S: Could not initialize symbol subsystem (imagehlp)" ,GetLastError());
|
|
}
|
|
|
|
//
|
|
// Load our symbols
|
|
//
|
|
if (!SymLoadModule(ProcessHandle, NULL, DbgExePathA, "FRS", 0, 0)) {
|
|
DPRINT1_WS(0, ":S: Could not load symbols for %s", DbgExePathA ,GetLastError());
|
|
}
|
|
|
|
//
|
|
// Search path
|
|
//
|
|
if (!SymGetSearchPath(ProcessHandle, DbgSearchPath, MAX_PATH)) {
|
|
DPRINT_WS(0, ":S: Can't get search path; error %s", GetLastError());
|
|
} else {
|
|
DPRINT1(0, ":S: Symbol search path is %s\n", DbgSearchPath);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void
|
|
DbgShowConfig(
|
|
VOID
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Display the OS Version info and the Processor Architecture info.
|
|
|
|
Arguments:
|
|
None.
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DbgShowConfig:"
|
|
|
|
|
|
ULONG ProductType;
|
|
ULONG Arch;
|
|
|
|
if (DebugInfo.BuildLab != NULL) {
|
|
DPRINT1(0, ":H: BuildLab : %s\n", DebugInfo.BuildLab);
|
|
}
|
|
|
|
DPRINT4(0, ":H: OS Version %d.%d (%d) - %w\n",
|
|
OsInfo.dwMajorVersion,OsInfo.dwMinorVersion,OsInfo.dwBuildNumber,OsInfo.szCSDVersion);
|
|
|
|
ProductType = (ULONG) OsInfo.wProductType;
|
|
DPRINT4(0, ":H: SP (%hd.%hd) SM: 0x%04hx PT: 0x%02x\n",
|
|
OsInfo.wServicePackMajor,OsInfo.wServicePackMinor,OsInfo.wSuiteMask, ProductType);
|
|
|
|
Arch = SystemInfo.wProcessorArchitecture;
|
|
if (Arch >= ARRAY_SZ(ProcessorArchName)) {
|
|
Arch = ARRAY_SZ(ProcessorArchName)-1;
|
|
}
|
|
|
|
DPRINT5(0, ":H: Processor: %s Level: 0x%04hx Revision: 0x%04hx Processor num/mask: %d/%08x\n",
|
|
ProcessorArchName[Arch], SystemInfo.wProcessorLevel,
|
|
SystemInfo.wProcessorRevision, SystemInfo.dwNumberOfProcessors,
|
|
SystemInfo.dwActiveProcessorMask);
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
DbgCaptureThreadInfo(
|
|
PWCHAR ArgName,
|
|
PTHREAD_START_ROUTINE EntryPoint
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Search the KnownThreadArray for an entry with a matching name. If not found,
|
|
Search the FRS Thread list for the thread with the matching entry point.
|
|
If found make an entry in the KnownThreadArray so it is available when
|
|
printing out the debug log header.
|
|
|
|
Arguments:
|
|
ArgName -- Printable name of thread.
|
|
Main -- Entry point of thread.
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DbgCaptureThreadInfo:"
|
|
|
|
PFRS_THREAD FrsThread;
|
|
ULONG i;
|
|
|
|
if (ArgName == NULL) {
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SZ(KnownThreadArray); i++) {
|
|
//
|
|
// Any room left?
|
|
//
|
|
if ((KnownThreadArray[i].Name == NULL) ||
|
|
(WSTR_EQ(ArgName, KnownThreadArray[i].Name))) {
|
|
|
|
FrsThread = ThSupGetThread(EntryPoint);
|
|
if (FrsThread == NULL) {
|
|
return;
|
|
}
|
|
|
|
KnownThreadArray[i].EntryPoint = FrsThread->Main;
|
|
KnownThreadArray[i].Id = FrsThread->Id;
|
|
KnownThreadArray[i].Name = ArgName;
|
|
|
|
ThSupReleaseRef(FrsThread);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
DbgCaptureThreadInfo2(
|
|
PWCHAR ArgName,
|
|
PTHREAD_START_ROUTINE EntryPoint,
|
|
ULONG ThreadId
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Search the KnownThreadArray for an entry with a matching name.
|
|
If found make an entry in the KnownThreadArray so it is available when
|
|
printing out the debug log header.
|
|
|
|
Arguments:
|
|
ArgName -- Printable name of thread.
|
|
Main -- Entry point of thread.
|
|
ThreadId - Thread ID of the thread to add to the list.
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DbgCaptureThreadInfo2:"
|
|
|
|
ULONG i;
|
|
|
|
if (ArgName == NULL) {
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < ARRAY_SZ(KnownThreadArray); i++) {
|
|
//
|
|
// Any room left or
|
|
// See if we already have this one and update the thread ID if so.
|
|
// If it was a cmd server thread it may have exited after timeout.
|
|
//
|
|
if ((KnownThreadArray[i].Name == NULL) ||
|
|
(WSTR_EQ(ArgName, KnownThreadArray[i].Name))) {
|
|
KnownThreadArray[i].EntryPoint = EntryPoint;
|
|
KnownThreadArray[i].Id = ThreadId;
|
|
KnownThreadArray[i].Name = ArgName;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
DbgPrintThreadIds(
|
|
IN ULONG Severity
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Print the known thread IDs.
|
|
|
|
Arguments:
|
|
Severity
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DbgPrintThreadIds:"
|
|
|
|
ULONG i;
|
|
|
|
DPRINT(Severity, ":H: Known thread IDs -\n");
|
|
|
|
//
|
|
// Dump the known thread IDs.
|
|
//
|
|
for (i = 0; i < ARRAY_SZ(KnownThreadArray); i++) {
|
|
if (KnownThreadArray[i].Name != NULL) {
|
|
DPRINT2(Severity, ":H: %-20ws : %d\n",
|
|
KnownThreadArray[i].Name, KnownThreadArray[i].Id);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
DbgPrintInfo(
|
|
IN ULONG Severity
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Print the debug info struct
|
|
|
|
Arguments:
|
|
Severity
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DbgPrintInfo:"
|
|
|
|
SYSTEMTIME SystemTime;
|
|
ULONG Unamesize = MAX_PATH + 1;
|
|
ULONG i;
|
|
WCHAR Uname[MAX_PATH + 1];
|
|
CHAR TimeBuf[MAX_PATH];
|
|
|
|
//
|
|
// Username
|
|
//
|
|
if (!GetUserName(Uname, &Unamesize)) {
|
|
Uname[0] = L'\0';
|
|
}
|
|
|
|
TimeBuf[0] = '\0';
|
|
|
|
GetLocalTime(&SystemTime);
|
|
|
|
if (_snprintf(TimeBuf, MAX_PATH, "%2d/%2d-%02d:%02d:%02d ",
|
|
SystemTime.wMonth, SystemTime.wDay, SystemTime.wHour,
|
|
SystemTime.wMinute, SystemTime.wSecond) < 0) {
|
|
TimeBuf[0] = '\0';
|
|
}
|
|
|
|
DPRINT3(Severity, ":H: Service running on %ws as %ws at %s\n",
|
|
ComputerName, Uname, TimeBuf);
|
|
|
|
DPRINT(Severity, "\n");
|
|
DPRINT(Severity, ":H: ***** COMPILE INFORMATION:\n");
|
|
DPRINT1(Severity, ":H: \tModule %s\n", NtFrsModule);
|
|
DPRINT2(Severity, ":H: \tCompile Date %s %s\n", NtFrsDate, NtFrsTime);
|
|
|
|
i = 0;
|
|
while (LatestChanges[i] != NULL) {
|
|
DPRINT1(Severity, ":H: %s\n", LatestChanges[i]);
|
|
i++;
|
|
}
|
|
|
|
DPRINT(Severity, "\n");
|
|
DbgShowConfig();
|
|
|
|
DPRINT(Severity, "\n");
|
|
DPRINT(Severity, ":H: ***** DEBUG INFORMATION:\n");
|
|
DPRINT1(Severity, ":H: Assert Files : %d\n", DebugInfo.AssertFiles);
|
|
if (DebugInfo.AssertSeconds) {
|
|
DPRINT1(Severity, ":H: Assert Seconds : Assert after %d seconds\n", DebugInfo.AssertSeconds);
|
|
} else {
|
|
DPRINT(Severity, ":H: Assert Seconds : Don't force an assert\n");
|
|
}
|
|
DPRINT1(Severity, ":H: Assert Share : %ws\n", DebugInfo.AssertShare);
|
|
DPRINT1(Severity, ":H: ChangeOrderAgingDelay (ms) : %d\n", ChangeOrderAgingDelay);
|
|
DPRINT1(Severity, ":H: Check Mem : %s\n", (DebugInfo.Mem) ? "TRUE" : "FALSE");
|
|
DPRINT1(Severity, ":H: Check Queues : %s\n", (DebugInfo.Queues) ? "TRUE" : "FALSE");
|
|
DPRINT1(Severity, ":H: CommTimeoutInMilliSeconds : %d\n", CommTimeoutInMilliSeconds);
|
|
DPRINT1(Severity, ":H: Compact Mem : %s\n", (DebugInfo.MemCompact) ? "TRUE" : "FALSE");
|
|
DPRINT1(Severity, ":H: CompressStagingFiles : %s\n", (!DisableCompressionStageFiles) ? "TRUE" : "FALSE");
|
|
DPRINT1(Severity, ":H: Copy Logs : %s\n", (DebugInfo.CopyLogs) ? "TRUE" : "FALSE");
|
|
DPRINT1(Severity, ":H: Disabled : %s\n", (DebugInfo.Disabled) ? "TRUE" : "FALSE");
|
|
DPRINT1(Severity, ":H: DsPollingLongInterval (ms) : %d\n", DsPollingLongInterval);
|
|
DPRINT1(Severity, ":H: DsPollingShortInterval(ms) : %d\n", DsPollingShortInterval);
|
|
DPRINT1(Severity, ":H: EnableInstallOverride : %s\n", (DebugInfo.EnableInstallOverride) ? "TRUE" : "FALSE");
|
|
DPRINT1(Severity, ":H: EnableJrnlWrapAutoRestore : %s\n", (DebugInfo.EnableJrnlWrapAutoRestore) ? "TRUE" : "FALSE");
|
|
DPRINT1(Severity, ":H: EnableRenameUpdates : %s\n", (DebugInfo.EnableRenameUpdates) ? "TRUE" : "FALSE");
|
|
DPRINT1(Severity, ":H: FetchRetryReset : %d\n", DebugInfo.FetchRetryReset);
|
|
DPRINT1(Severity, ":H: FetchRetryResetInc : %d\n", DebugInfo.FetchRetryInc);
|
|
DPRINT1(Severity, ":H: FetchRetryTrigger : %d\n", DebugInfo.FetchRetryTrigger);
|
|
DPRINT1(Severity, ":H: Force VvJoin : %s\n", (DebugInfo.ForceVvJoin) ? "TRUE" : "FALSE");
|
|
DPRINT1(Severity, ":H: Interval : %d\n", DebugInfo.Interval);
|
|
DPRINT1(Severity, ":H: Log Dir : %ws\n", DebugInfo.LogDir ? DebugInfo.LogDir : L"<null>");
|
|
DPRINT1(Severity, ":H: Log File : %ws\n", DebugInfo.LogFile);
|
|
DPRINT1(Severity, ":H: Log Files : %d\n", DebugInfo.LogFiles);
|
|
DPRINT1(Severity, ":H: Log Flush Int. : %d\n", DebugInfo.LogFlushInterval);
|
|
DPRINT1(Severity, ":H: Log Lines : %d\n", DebugInfo.LogLines);
|
|
DPRINT1(Severity, ":H: Log Severity : %d\n", DebugInfo.LogSeverity);
|
|
DPRINT1(Severity, ":H: Max Log Lines : %d\n", DebugInfo.MaxLogLines);
|
|
DPRINT1(Severity, ":H: MaxCoRetryTimeoutCount : %d\n", MaxCoRetryTimeoutCount);
|
|
DPRINT1(Severity, ":H: MaxCoRetryTimeoutMinutes : %d\n", MaxCoRetryTimeoutMinutes);
|
|
DPRINT1(Severity, ":H: OutlogChangeHistory : %d\n", DebugInfo.OutlogChangeHistory);
|
|
DPRINT1(Severity, ":H: PartnerClockSkew (m) : %d\n", PartnerClockSkew);
|
|
DPRINT1(Severity, ":H: Profile : %s\n", DebugInfo.Profile);
|
|
DPRINT1(Severity, ":H: Recipients : %s\n", DebugInfo.Recipients);
|
|
DPRINT1(Severity, ":H: ReclaimStagingSpace : %s\n", (DebugInfo.ReclaimStagingSpace) ? "TRUE" : "FALSE");
|
|
DPRINT1(Severity, ":H: ReplicaTombstone (d) : %d\n", ReplicaTombstone);
|
|
DPRINT1(Severity, ":H: Restart : %s\n", (DebugInfo.Restart) ? "TRUE" : "FALSE");
|
|
if (DebugInfo.RestartSeconds) {
|
|
DPRINT1(Severity, ":H: Restart Seconds : Restart if assert after "
|
|
"%d seconds\n", DebugInfo.RestartSeconds);
|
|
} else {
|
|
DPRINT(Severity, ":H: Restart Seconds: Don't Restart\n");
|
|
}
|
|
DPRINT1(Severity, ":H: SaveOutlogChangeHistory : %s\n", (DebugInfo.SaveOutlogChangeHistory) ? "TRUE" : "FALSE");
|
|
DPRINT1(Severity, ":H: Severity : %d\n", DebugInfo.Severity);
|
|
DPRINT1(Severity, ":H: StagingLimitInKb : %d\n", StagingLimitInKb);
|
|
DPRINT1(Severity, ":H: Suppress : %s\n", (DebugInfo.Suppress) ? "TRUE" : "FALSE");
|
|
DPRINT1(Severity, ":H: SuppressIdenticalUpdt : %s\n", (DebugInfo.SuppressIdenticalUpdt) ? "TRUE" : "FALSE");
|
|
DPRINT1(Severity, ":H: Systems : %ws\n", DebugInfo.Systems);
|
|
DPRINT1(Severity, ":H: TestFid : %d\n", DebugInfo.TestFid);
|
|
|
|
if (DebugInfo.TestCodeName != NULL) {
|
|
DPRINT1(Severity, ":H: TestCodeName : %s\n", DebugInfo.TestCodeName ? DebugInfo.TestCodeName : "<null>");
|
|
DPRINT1(Severity, ":H: TestSubCodeNumber : %d\n", DebugInfo.TestSubCodeNumber);
|
|
DPRINT1(Severity, ":H: TestTriggerCount : %d\n", DebugInfo.TestTriggerCount);
|
|
DPRINT1(Severity, ":H: TestTriggerRefresh : %d\n", DebugInfo.TestTriggerRefresh);
|
|
}
|
|
|
|
DPRINT1(Severity, ":H: Thread Id : %d\n", DebugInfo.ThreadId);
|
|
DPRINT1(Severity, ":H: Total Log Lines : %d\n", DebugInfo.TotalLogLines);
|
|
DPRINT1(Severity, ":H: UnjoinTrigger : %d\n", DebugInfo.UnjoinTrigger);
|
|
DPRINT1(Severity, ":H: VvJoinTests : %s\n", (DebugInfo.VvJoinTests) ? "TRUE" : "FALSE");
|
|
// DPRINT1(Severity, ":H: Command Line : %ws\n", DebugInfo.CommandLine);
|
|
DPRINT(Severity, "\n");
|
|
|
|
DbgPrintThreadIds(Severity);
|
|
|
|
DEBUG_FLUSH();
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
DbgPrintAllStats(
|
|
VOID
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Print the stats we know about
|
|
|
|
Arguments:
|
|
None.
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DbgPrintAllStats:"
|
|
|
|
DbgPrintInfo(DebugInfo.LogSeverity);
|
|
FrsPrintAllocStats(DebugInfo.LogSeverity, NULL, 0);
|
|
FrsPrintRpcStats(DebugInfo.LogSeverity, NULL, 0);
|
|
}
|
|
|
|
|
|
VOID
|
|
DbgFlush(
|
|
VOID
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Flush the output buffers
|
|
|
|
Arguments:
|
|
None.
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DbgFlush:"
|
|
|
|
DebLock();
|
|
try {
|
|
if (HANDLE_IS_VALID(DebugInfo.LogFILE)) {
|
|
FrsFlushFile(L"LogFILE", DebugInfo.LogFILE);
|
|
DbgFlushInterval = DebugInfo.LogFlushInterval;
|
|
}
|
|
} finally{
|
|
DebUnLock();
|
|
}
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
DbgStartService(
|
|
IN PWCHAR ServiceName
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Start a service on this machine.
|
|
|
|
Arguments:
|
|
ServiceName - the service to start
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DbgStartService:"
|
|
|
|
SC_HANDLE ServiceHandle;
|
|
|
|
//
|
|
// Open the service.
|
|
//
|
|
ServiceHandle = FrsOpenServiceHandle(NULL, ServiceName);
|
|
if (!HANDLE_IS_VALID(ServiceHandle)) {
|
|
DPRINT1(0, ":S: Couldn't open service %ws\n", ServiceName);
|
|
return;
|
|
}
|
|
//
|
|
// Start the service
|
|
//
|
|
if (!StartService(ServiceHandle, 0, NULL)) {
|
|
DPRINT1_WS(0, ":S: Couldn't start %ws;", ServiceName, GetLastError());
|
|
CloseServiceHandle(ServiceHandle);
|
|
return;
|
|
}
|
|
CloseServiceHandle(ServiceHandle);
|
|
DPRINT1(4, ":S: Started %ws\n", ServiceName);
|
|
}
|
|
|
|
|
|
VOID
|
|
DbgStopService(
|
|
IN PWCHAR ServiceName
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Stop a service on this machine.
|
|
|
|
Arguments:
|
|
ServiceName - the service to stop
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DbgStopService:"
|
|
|
|
|
|
BOOL Status;
|
|
SC_HANDLE ServiceHandle;
|
|
SERVICE_STATUS ServiceStatus;
|
|
|
|
//
|
|
// Open the service.
|
|
//
|
|
ServiceHandle = FrsOpenServiceHandle(NULL, ServiceName);
|
|
if (!HANDLE_IS_VALID(ServiceHandle)) {
|
|
DPRINT1(0, ":S: Couldn't stop service %ws\n", ServiceName);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Stop the service
|
|
//
|
|
Status = ControlService(ServiceHandle, SERVICE_CONTROL_STOP, &ServiceStatus);
|
|
if (!WIN_SUCCESS(Status)) {
|
|
DPRINT1_WS(0, ":S: Couldn't stop %ws;", ServiceName, GetLastError());
|
|
CloseServiceHandle(ServiceHandle);
|
|
return;
|
|
}
|
|
CloseServiceHandle(ServiceHandle);
|
|
DPRINT1(4, ":S: Stopped %ws\n", ServiceName);
|
|
}
|
|
|
|
|
|
|
|
ULONG
|
|
DbgForceAssert(
|
|
IN PVOID Ignored
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Force an assert after some seconds
|
|
|
|
Arguments:
|
|
Ignored
|
|
|
|
Return Value:
|
|
ERROR_SUCCESS
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DbgForceAssert:"
|
|
|
|
BOOL ForcingAssert = TRUE;
|
|
|
|
//
|
|
// Wait for a shutdown event
|
|
//
|
|
WaitForSingleObject(ShutDownEvent, DebugInfo.AssertSeconds * 1000);
|
|
if (!FrsIsShuttingDown) {
|
|
DPRINT(0, ":S: FORCING ASSERT\n");
|
|
FRS_ASSERT(!ForcingAssert);
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
VOID
|
|
DbgQueryLogParams(
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
Read the registry for new values for the logging params.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DbgQueryLogParams:"
|
|
|
|
PWCHAR WStr, WStr1;
|
|
PCHAR AStr;
|
|
PWCHAR NewLogDirStr = NULL;
|
|
PWCHAR File;
|
|
DWORD WStatus;
|
|
DWORD NewDisabled;
|
|
BOOL OpenNewLog = FALSE;
|
|
ULARGE_INTEGER FreeBytes;
|
|
ULARGE_INTEGER TotalBytes;
|
|
|
|
//
|
|
// Get new state before taking debug lock since function does DPRINTs.
|
|
//
|
|
CfgRegReadDWord(FKC_DEBUG_DISABLE, NULL, 0, &NewDisabled);
|
|
|
|
//
|
|
// Check for a change in Disable Debug
|
|
//
|
|
DebLock();
|
|
if ((BOOL)NewDisabled != DebugInfo.Disabled) {
|
|
|
|
DebugInfo.Disabled = NewDisabled;
|
|
|
|
if (DebugInfo.Disabled) {
|
|
//
|
|
// Stop logging.
|
|
//
|
|
if (HANDLE_IS_VALID(DebugInfo.LogFILE)) {
|
|
FrsFlushFile(L"LogFILE", DebugInfo.LogFILE);
|
|
DbgFlushInterval = DebugInfo.LogFlushInterval;
|
|
FRS_CLOSE(DebugInfo.LogFILE);
|
|
}
|
|
DebugInfo.LogFILE = INVALID_HANDLE_VALUE;
|
|
|
|
} else {
|
|
//
|
|
// Start logging.
|
|
//
|
|
OpenNewLog = TRUE;
|
|
}
|
|
}
|
|
|
|
DebUnLock();
|
|
|
|
//
|
|
// Quit now if logging is disabled.
|
|
//
|
|
if (DebugInfo.Disabled) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Log File Directory (only if not running multiple servers on one machine)
|
|
//
|
|
if (ServerName == NULL) {
|
|
CfgRegReadString(FKC_DEBUG_LOG_FILE, NULL, 0, &NewLogDirStr);
|
|
if (NewLogDirStr != NULL) {
|
|
if ((DebugInfo.LogDir == NULL) ||
|
|
WSTR_NE(NewLogDirStr, DebugInfo.LogDir)) {
|
|
OpenNewLog = TRUE;
|
|
} else {
|
|
NewLogDirStr = FrsFree(NewLogDirStr);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Number of log files
|
|
//
|
|
CfgRegReadDWord(FKC_DEBUG_LOG_FILES, NULL, 0, &DebugInfo.LogFiles);
|
|
|
|
//
|
|
// Flush the trace log after every n lines.
|
|
//
|
|
CfgRegReadDWord(FKC_DEBUG_LOG_FLUSH_INTERVAL, NULL, 0, &DebugInfo.LogFlushInterval);
|
|
|
|
//
|
|
// Severity for console print.
|
|
//
|
|
CfgRegReadDWord(FKC_DEBUG_SEVERITY, NULL, 0, &DebugInfo.Severity);
|
|
|
|
//
|
|
// Log Severity
|
|
//
|
|
CfgRegReadDWord(FKC_DEBUG_LOG_SEVERITY, NULL, 0, &DebugInfo.LogSeverity);
|
|
|
|
//
|
|
// Systems - selected list of functions to trace. trace all if NULL.
|
|
//
|
|
CfgRegReadString(FKC_DEBUG_SYSTEMS, NULL, 0, &WStr);
|
|
if (WStr != NULL) {
|
|
AStr = DebugInfo.Systems;
|
|
DebLock();
|
|
DebugInfo.Systems = (wcslen(WStr)) ? FrsWtoA(WStr) : NULL;
|
|
DebUnLock();
|
|
WStr = FrsFree(WStr);
|
|
AStr = FrsFree(AStr);
|
|
}
|
|
|
|
//
|
|
// Maximum Log Messages
|
|
//
|
|
CfgRegReadDWord(FKC_DEBUG_MAX_LOG, NULL, 0, &DebugInfo.MaxLogLines);
|
|
|
|
//
|
|
// Debugger serial line Print (assume suppress so no dprints leak out)
|
|
//
|
|
DebugInfo.Suppress = TRUE;
|
|
CfgRegReadDWord(FKC_DEBUG_SUPPRESS, NULL, 0, &DebugInfo.Suppress);
|
|
|
|
//
|
|
// Enable break into debugger if present.
|
|
//
|
|
CfgRegReadDWord(FKC_DEBUG_BREAK, NULL, 0, &DebugInfo.Break);
|
|
|
|
//
|
|
// Open a new log if logging was just turned on or if we were.
|
|
// having trouble logging due to errors like out of disk space.
|
|
// Save old log files and open a new log file.
|
|
//
|
|
if ((OpenNewLog || !HANDLE_IS_VALID(DebugInfo.LogFILE)) &&
|
|
(HANDLE_IS_VALID(DebugInfo.LogFile) || (NewLogDirStr != NULL))) {
|
|
|
|
WStr = DebugInfo.LogFile;
|
|
WStr1 = DebugInfo.LogDir;
|
|
DebLock();
|
|
|
|
if (NewLogDirStr != NULL) {
|
|
//
|
|
// Add the filename prefix to the end of the dir path.
|
|
//
|
|
DebugInfo.LogDir = NewLogDirStr;
|
|
DebugInfo.LogFile = FrsWcsCat(NewLogDirStr, NTFRS_DBG_LOG_FILE);
|
|
}
|
|
|
|
//
|
|
// Create new debug directory
|
|
//
|
|
if (!CreateDirectory(DebugInfo.LogDir, NULL)) {
|
|
WStatus = GetLastError();
|
|
|
|
if (!WIN_ALREADY_EXISTS(WStatus)) {
|
|
DebugInfo.LogFile = FrsFree(DebugInfo.LogFile);
|
|
DebugInfo.LogDir = FrsFree(DebugInfo.LogDir);
|
|
}
|
|
}
|
|
|
|
if (DebugInfo.LogFile != NULL) {
|
|
if (HANDLE_IS_VALID(DebugInfo.LogFILE)) {
|
|
FRS_CLOSE(DebugInfo.LogFILE);
|
|
}
|
|
|
|
DbgShiftLogFiles(DebugInfo.LogFile,
|
|
LOG_FILE_SUFFIX,
|
|
(DebugInfo.CopyLogs) ? DebugInfo.AssertShare : NULL,
|
|
DebugInfo.LogFiles);
|
|
DbgOpenLogFile();
|
|
}
|
|
|
|
DebUnLock();
|
|
|
|
if (NewLogDirStr != NULL) {
|
|
WStr = FrsFree(WStr);
|
|
WStr1 = FrsFree(WStr1);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Raise a eventlog message if there isn't enough disk space on the logging volume
|
|
// to accomodate all the log files.
|
|
//
|
|
if (DebugInfo.LogDir != NULL) {
|
|
FreeBytes.QuadPart = QUADZERO;
|
|
TotalBytes.QuadPart = QUADZERO;
|
|
if (GetDiskFreeSpaceEx(DebugInfo.LogDir,
|
|
&FreeBytes,
|
|
&TotalBytes,
|
|
NULL)) {
|
|
//
|
|
// Complain if we have less than 10 MB free.
|
|
//
|
|
if (FreeBytes.QuadPart < (10 * 1000 * 1000)) {
|
|
EPRINT1(EVENT_FRS_LOG_SPACE, DebugInfo.LogDir);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
DbgQueryDynamicConfigParams(
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
Read the registry for new values for config params that can change
|
|
while service is running.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DbgQueryDynamicConfigParams:"
|
|
|
|
DWORD CompressStagingFiles = 1;
|
|
PWCHAR WStr = NULL;
|
|
|
|
|
|
//
|
|
// Pick up new debug logging related parameters.
|
|
//
|
|
DbgQueryLogParams();
|
|
|
|
//
|
|
// Get boolean to tell us if we should do automatic restore when
|
|
// we hit journal wrap
|
|
//
|
|
CfgRegReadDWord(FKC_ENABLE_JOURNAL_WRAP_AUTOMATIC_RESTORE, NULL, 0, &DebugInfo.EnableJrnlWrapAutoRestore);
|
|
|
|
//
|
|
// Check if automatic cleanup of staging files is enabled or disabled.
|
|
//
|
|
CfgRegReadDWord(FKC_RECLAIM_STAGING_SPACE, NULL, 0, &DebugInfo.ReclaimStagingSpace);
|
|
|
|
//
|
|
// Check if a new value for outlog history time is set.
|
|
//
|
|
CfgRegReadDWord(FKC_OUTLOG_CHANGE_HISTORY, NULL, 0, &DebugInfo.OutlogChangeHistory);
|
|
|
|
//
|
|
// Check if saving outlog history is disabled.
|
|
//
|
|
CfgRegReadDWord(FKC_SAVE_OUTLOG_CHANGE_HISTORY, NULL, 0, &DebugInfo.SaveOutlogChangeHistory);
|
|
|
|
//
|
|
// Check if a new value for install override is set.
|
|
//
|
|
CfgRegReadDWord(FKC_ENABLE_INSTALL_OVERRIDE, NULL, 0, &DebugInfo.EnableInstallOverride);
|
|
|
|
//
|
|
// Check if a new value for forced rename on file updates is set.
|
|
//
|
|
CfgRegReadDWord(FKC_ENABLE_RENAME_BASED_UPDATES, NULL, 0, &DebugInfo.EnableRenameUpdates);
|
|
|
|
//
|
|
// Check if suppress identical updates is disabled.
|
|
//
|
|
CfgRegReadDWord(FKC_SUPPRESS_IDENTICAL_UPDATES, NULL, 0, &DebugInfo.SuppressIdenticalUpdt);
|
|
|
|
//
|
|
// Read the new value for the compression parameter.
|
|
//
|
|
CfgRegReadDWord(FKC_COMPRESS_STAGING_FILES, NULL, 0, &CompressStagingFiles);
|
|
DisableCompressionStageFiles = (CompressStagingFiles == 0);
|
|
|
|
//
|
|
// Pick up the Outlog file repeat interval.
|
|
//
|
|
CfgRegReadDWord(FKC_OUTLOG_REPEAT_INTERVAL, NULL, 0, &GOutLogRepeatInterval);
|
|
|
|
//
|
|
// Find out which reparse points to replicate.
|
|
//
|
|
CfgRegReadReparseTagInfo();
|
|
|
|
//
|
|
// Get the Test code params. They consist of a code name, a sub-code
|
|
// number, a trigger count and a trigger count refresh value.
|
|
//
|
|
CfgRegReadString(FKC_DEBUG_TEST_CODE_NAME, NULL, 0, &WStr);
|
|
if (WStr != NULL) {
|
|
DebugInfo.TestCodeName = (wcslen(WStr)) ? FrsWtoA(WStr) : NULL;
|
|
WStr = FrsFree(WStr);
|
|
}
|
|
|
|
CfgRegReadDWord(FKC_DEBUG_TEST_CODE_NUMBER, NULL, 0, &DebugInfo.TestSubCodeNumber);
|
|
|
|
CfgRegReadDWord(FKC_DEBUG_TEST_TRIGGER_COUNT, NULL, 0, &DebugInfo.TestTriggerCount);
|
|
|
|
CfgRegReadDWord(FKC_DEBUG_TEST_TRIGGER_REFRESH, NULL, 0, &DebugInfo.TestTriggerRefresh);
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
DbgInitLogTraceFile(
|
|
IN LONG argc,
|
|
IN PWCHAR *argv
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
|
|
Initialize enough of the debug subsystem to start the log file.
|
|
The rest can wait until we have synced up with the service controller.
|
|
|
|
Arguments:
|
|
argc - from main
|
|
argv - from main; in wide char format
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DbgInitLogTraceFile:"
|
|
|
|
PWCHAR WStr;
|
|
PWCHAR File;
|
|
DWORD WStatus;
|
|
|
|
//
|
|
// Configure initial debug params until we read registry.
|
|
//
|
|
DebugInfo.AssertSeconds = 0;
|
|
DebugInfo.RestartSeconds = 0;
|
|
DebugInfo.Queues = 0;
|
|
DebugInfo.DbsOutOfSpace = DBG_DBS_OUT_OF_SPACE_OP_NONE;
|
|
DebugInfo.DbsOutOfSpaceTrigger = 0;
|
|
|
|
//
|
|
// Get the logging config params.
|
|
// Registry overrides defaults and CLI overrides registry.
|
|
//
|
|
|
|
//
|
|
// Disable Debug
|
|
//
|
|
CfgRegReadDWord(FKC_DEBUG_DISABLE, NULL, 0, &DebugInfo.Disabled);
|
|
if (FrsSearchArgv(argc, argv, L"disabledebug", NULL)) {
|
|
DebugInfo.Disabled = TRUE;
|
|
}
|
|
|
|
//
|
|
// Log File (really the dir containing the log files)
|
|
//
|
|
FrsSearchArgv(argc, argv, L"logfile", &DebugInfo.LogDir);
|
|
if (DebugInfo.LogDir == NULL) {
|
|
CfgRegReadString(FKC_DEBUG_LOG_FILE, NULL, 0, &DebugInfo.LogDir);
|
|
}
|
|
|
|
if (DebugInfo.LogDir != NULL) {
|
|
//
|
|
// Add the filename prefix to the end of the dir path.
|
|
//
|
|
DebugInfo.LogFile = FrsWcsCat(DebugInfo.LogDir, NTFRS_DBG_LOG_FILE);
|
|
} else {
|
|
DebugInfo.LogFile = NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// Share for copying log/assert files
|
|
//
|
|
FrsSearchArgv(argc, argv, L"assertshare", &DebugInfo.AssertShare);
|
|
if (DebugInfo.AssertShare == NULL) {
|
|
CfgRegReadString(FKC_DEBUG_ASSERT_SHARE, NULL, 0, &DebugInfo.AssertShare);
|
|
}
|
|
|
|
//
|
|
// Copy log files into assert share
|
|
//
|
|
CfgRegReadDWord(FKC_DEBUG_COPY_LOG_FILES, NULL, 0, &DebugInfo.CopyLogs);
|
|
|
|
//
|
|
// Number of assert files
|
|
//
|
|
if (!FrsSearchArgvDWord(argc, argv, L"assertfiles", &DebugInfo.AssertFiles)) {
|
|
CfgRegReadDWord(FKC_DEBUG_ASSERT_FILES, NULL, 0, &DebugInfo.AssertFiles);
|
|
}
|
|
|
|
//
|
|
// Number of log files
|
|
//
|
|
if (!FrsSearchArgvDWord(argc, argv, L"logfiles", &DebugInfo.LogFiles)) {
|
|
CfgRegReadDWord(FKC_DEBUG_LOG_FILES, NULL, 0, &DebugInfo.LogFiles);
|
|
}
|
|
|
|
//
|
|
// Flush the trace log after every n lines.
|
|
//
|
|
if (!FrsSearchArgvDWord(argc, argv, L"logflushinterval", &DebugInfo.LogFlushInterval)) {
|
|
CfgRegReadDWord(FKC_DEBUG_LOG_FLUSH_INTERVAL, NULL, 0, &DebugInfo.LogFlushInterval);
|
|
}
|
|
|
|
//
|
|
// Create the dir path to the share where Assert Logs are copied.
|
|
//
|
|
if ((DebugInfo.AssertShare != NULL) &&
|
|
wcslen(DebugInfo.AssertShare) && wcslen(ComputerName)) {
|
|
|
|
WStr = FrsWcsCat3(DebugInfo.AssertShare, L"\\", ComputerName);
|
|
FrsFree(DebugInfo.AssertShare);
|
|
DebugInfo.AssertShare = WStr;
|
|
WStr = NULL;
|
|
}
|
|
|
|
//
|
|
// Severity for console print.
|
|
//
|
|
if (!FrsSearchArgvDWord(argc, argv, L"severity", &DebugInfo.Severity)) {
|
|
CfgRegReadDWord(FKC_DEBUG_SEVERITY, NULL, 0, &DebugInfo.Severity);
|
|
}
|
|
|
|
//
|
|
// Log Severity
|
|
//
|
|
if (!FrsSearchArgvDWord(argc, argv, L"logseverity", &DebugInfo.LogSeverity)) {
|
|
CfgRegReadDWord(FKC_DEBUG_LOG_SEVERITY, NULL, 0, &DebugInfo.LogSeverity);
|
|
}
|
|
|
|
//
|
|
// Systems - selected list of functions to trace. trace all if NULL.
|
|
//
|
|
DebugInfo.Systems = NULL;
|
|
if (!FrsSearchArgv(argc, argv, L"systems", &WStr)) {
|
|
CfgRegReadString(FKC_DEBUG_SYSTEMS, NULL, 0, &WStr);
|
|
}
|
|
if (WStr != NULL) {
|
|
DebugInfo.Systems = (wcslen(WStr)) ? FrsWtoA(WStr) : NULL;
|
|
WStr = FrsFree(WStr);
|
|
}
|
|
|
|
//
|
|
// Maximum Log Messages
|
|
//
|
|
DebugInfo.MaxLogLines = DEFAULT_DEBUG_MAX_LOG;
|
|
if (!FrsSearchArgvDWord(argc, argv, L"maxloglines", &DebugInfo.MaxLogLines)) {
|
|
CfgRegReadDWord(FKC_DEBUG_MAX_LOG, NULL, 0, &DebugInfo.MaxLogLines);
|
|
}
|
|
|
|
//
|
|
// Debugger serial line Print (assume suppress so no dprints leak out)
|
|
//
|
|
DebugInfo.Suppress = TRUE;
|
|
if (!FrsSearchArgv(argc, argv, L"debuggerprint", NULL)) {
|
|
CfgRegReadDWord(FKC_DEBUG_SUPPRESS, NULL, 0, &DebugInfo.Suppress);
|
|
} else {
|
|
DebugInfo.Suppress = FALSE;
|
|
}
|
|
|
|
//
|
|
// Enable break into debugger if present.
|
|
//
|
|
DebugInfo.Break = TRUE;
|
|
if (!FrsSearchArgv(argc, argv, L"break", NULL)) {
|
|
CfgRegReadDWord(FKC_DEBUG_BREAK, NULL, 0, &DebugInfo.Break);
|
|
}
|
|
|
|
//
|
|
// Turn on debug log if desired.
|
|
// Save old log files and open a new log file.
|
|
//
|
|
if (DebugInfo.LogFile != NULL) {
|
|
//
|
|
// Create the debug directory
|
|
//
|
|
if (!CreateDirectory(DebugInfo.LogDir, NULL)) {
|
|
WStatus = GetLastError();
|
|
|
|
if (!WIN_ALREADY_EXISTS(WStatus)) {
|
|
//
|
|
// Need some way to report the following.
|
|
//
|
|
//DPRINT1_WS(0, ":S: CreateDirectory(Logfile) %ws -- failed;",
|
|
// DebugInfo.LogFile, WStatus);
|
|
|
|
EPRINT1(EVENT_FRS_BAD_DEBUG_DIR, DebugInfo.LogDir);
|
|
DebugInfo.LogFile = FrsFree(DebugInfo.LogFile);
|
|
DebugInfo.LogDir = FrsFree(DebugInfo.LogDir);
|
|
}
|
|
}
|
|
|
|
if (DebugInfo.LogFile != NULL) {
|
|
DbgShiftLogFiles(DebugInfo.LogFile,
|
|
LOG_FILE_SUFFIX,
|
|
(DebugInfo.CopyLogs) ? DebugInfo.AssertShare : NULL,
|
|
DebugInfo.LogFiles);
|
|
DbgOpenLogFile();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Executable's full path for symbols.
|
|
//
|
|
DbgExePathW[0] = L'\0';
|
|
if (GetFullPathNameW(argv[0], MAX_PATH-4, DbgExePathW, &File) == 0) {
|
|
DPRINT1(0, ":S: Could not get the full pathname for %ws\n", argv[0]);
|
|
}
|
|
|
|
if (!wcsstr(DbgExePathW, L".exe")) {
|
|
wcscat(DbgExePathW, L".exe");
|
|
}
|
|
DPRINT1(0, ":S: Full pathname for %ws\n", DbgExePathW);
|
|
|
|
if (_snprintf(DbgExePathA, sizeof(DbgExePathA), "%ws", DbgExePathW) < 0) {
|
|
DbgExePathA[sizeof(DbgExePathA) - 1] = '\0';
|
|
DPRINT1(0, ":S: Image path too long to get symbols for traceback: %ws\n", DbgExePathW);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Init the symbol support for stack traceback if we are tracking mem allocs.
|
|
// Don't use the memory subsystem until symbols are enabled
|
|
//
|
|
if (DebugInfo.Mem) {
|
|
DbgStackInit();
|
|
}
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
DbgMustInit(
|
|
IN LONG argc,
|
|
IN PWCHAR *argv
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Initialize the debug subsystem
|
|
|
|
Arguments:
|
|
argc - from main
|
|
argv - from main; in wide char format
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DbgMustInit:"
|
|
|
|
ULONG Len;
|
|
LONG i;
|
|
PWCHAR Wcs;
|
|
PWCHAR WStr;
|
|
DWORD WStatus;
|
|
|
|
//
|
|
// Init the known thread array.
|
|
//
|
|
for (i = 0; i < ARRAY_SZ(KnownThreadArray); i++) {
|
|
KnownThreadArray[i].Name = NULL;
|
|
}
|
|
DbgCaptureThreadInfo2(L"First", NULL, GetCurrentThreadId());
|
|
|
|
//
|
|
// Get some config info for the debug log header.
|
|
//
|
|
OsInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
|
|
GetVersionExW((POSVERSIONINFOW) &OsInfo);
|
|
|
|
GetSystemInfo(&SystemInfo);
|
|
|
|
//
|
|
// When not running as a service, the exe will restart itself
|
|
// after an assertion failure. When running as a service, the
|
|
// service controller will restart the service.
|
|
//
|
|
// Rebuild the command line for restart.
|
|
//
|
|
if (!RunningAsAService) {
|
|
#define RESTART_PARAM L" /restart"
|
|
|
|
Len = wcslen(RESTART_PARAM) + 2;
|
|
for (i = 0; i < argc; ++i) {
|
|
Len += wcslen(argv[i]) + 1;
|
|
}
|
|
DebugInfo.CommandLine = FrsAlloc(Len * sizeof(WCHAR));
|
|
|
|
for (i = 0; i < argc; ++i) {
|
|
//
|
|
// Give our parent process time to die so that it will
|
|
// release its handles on the journal, database, ...
|
|
//
|
|
if (wcsstr(argv[i], L"restart")) {
|
|
Sleep(5 * 1000);
|
|
continue;
|
|
}
|
|
wcscat(DebugInfo.CommandLine, argv[i]);
|
|
wcscat(DebugInfo.CommandLine, L" ");
|
|
}
|
|
wcscat(DebugInfo.CommandLine, RESTART_PARAM);
|
|
}
|
|
|
|
//
|
|
// Get rest of config params. Command line takes precedence over registrr.
|
|
//
|
|
//
|
|
// Restart the service iff it has asserted and has run at least this long
|
|
// to avoid assert loops.
|
|
//
|
|
if (!FrsSearchArgvDWord(argc, argv, L"restartseconds", &DebugInfo.RestartSeconds)) {
|
|
CfgRegReadDWord(FKC_DEBUG_RESTART_SECONDS, NULL, 0, &DebugInfo.RestartSeconds);
|
|
}
|
|
|
|
//
|
|
// Sendmail recipient (future)
|
|
//
|
|
DebugInfo.Recipients = NULL;
|
|
CfgRegReadString(FKC_DEBUG_RECIPIENTS, NULL, 0, &WStr);
|
|
if (WStr != NULL) {
|
|
DebugInfo.Recipients = (wcslen(WStr)) ? FrsWtoA(WStr) : NULL;
|
|
WStr = FrsFree(WStr);
|
|
}
|
|
|
|
//
|
|
// Sendmail Profile (future)
|
|
//
|
|
DebugInfo.Profile = NULL;
|
|
CfgRegReadString(FKC_DEBUG_PROFILE, NULL, 0, &WStr);
|
|
if (WStr != NULL) {
|
|
DebugInfo.Profile = (wcslen(WStr)) ? FrsWtoA(WStr) : NULL;
|
|
WStr = FrsFree(WStr);
|
|
}
|
|
|
|
//
|
|
// Buildlab info
|
|
//
|
|
DebugInfo.BuildLab = NULL;
|
|
CfgRegReadString(FKC_DEBUG_BUILDLAB, NULL, 0, &WStr);
|
|
if (WStr != NULL) {
|
|
DebugInfo.BuildLab = (wcslen(WStr)) ? FrsWtoA(WStr) : NULL;
|
|
WStr = FrsFree(WStr);
|
|
}
|
|
|
|
//
|
|
// Use the hardwired config file if there is no Directory Service.
|
|
//
|
|
if (FrsSearchArgv(argc, argv, L"nods", &WStr)) {
|
|
NoDs = TRUE;
|
|
if (WStr != NULL) {
|
|
IniFileName = wcslen(WStr) ? WStr : NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Single machine is pretending to be several machines
|
|
//
|
|
if (FrsSearchArgv(argc, argv, L"server", &WStr)) {
|
|
if ((WStr != NULL) && (wcslen(WStr) > 0)) {
|
|
NoDs = TRUE;
|
|
ServerName = WStr;
|
|
}
|
|
}
|
|
|
|
#ifdef DS_FREE
|
|
|
|
NoDs = TRUE;
|
|
#endif DS_FREE
|
|
|
|
//
|
|
// The following parameters are testing / debugging.
|
|
//
|
|
|
|
//
|
|
// Check queues
|
|
//
|
|
DebugInfo.Queues = TRUE;
|
|
if (!FrsSearchArgv(argc, argv, L"queues", NULL)) {
|
|
CfgRegReadDWord(FKC_DEBUG_QUEUES, NULL, 0, &DebugInfo.Queues);
|
|
}
|
|
|
|
//
|
|
// Enable VvJoin Tests
|
|
//
|
|
DebugInfo.VvJoinTests = FrsSearchArgv(argc, argv, L"vvjointests", NULL);
|
|
|
|
//
|
|
// forcevvjoin on every join
|
|
//
|
|
DebugInfo.ForceVvJoin = FrsSearchArgv(argc, argv, L"vvjoin", NULL);
|
|
|
|
//
|
|
// Enable rename fid test
|
|
//
|
|
DebugInfo.TestFid = FrsSearchArgv(argc, argv, L"testfid", NULL);
|
|
|
|
//
|
|
// forceunjoin on one cxtion after N remote co's
|
|
//
|
|
DebugInfo.UnjoinTrigger = 0;
|
|
FrsSearchArgvDWord(argc, argv, L"unjoin", &DebugInfo.UnjoinTrigger);
|
|
|
|
//
|
|
// forceunjoin on one cxtion after N remote co's
|
|
//
|
|
DebugInfo.FetchRetryReset = 0;
|
|
if (!FrsSearchArgvDWord(argc, argv, L"fetchretry", &DebugInfo.FetchRetryReset)) {
|
|
}
|
|
DebugInfo.FetchRetryTrigger = DebugInfo.FetchRetryReset;
|
|
DebugInfo.FetchRetryInc = DebugInfo.FetchRetryReset;
|
|
|
|
//
|
|
// Set interval for toggling the schedule
|
|
//
|
|
FrsSearchArgvDWord(argc, argv, L"interval", &DebugInfo.Interval);
|
|
|
|
//
|
|
// Force an assert after N seconds (0 == don't assert)
|
|
//
|
|
DebugInfo.AssertSeconds = 0;
|
|
if (!FrsSearchArgvDWord(argc, argv, L"assertseconds", &DebugInfo.AssertSeconds)) {
|
|
CfgRegReadDWord(FKC_DEBUG_ASSERT_SECONDS, NULL, 0, &DebugInfo.AssertSeconds);
|
|
}
|
|
|
|
//
|
|
// Force REAL out of space errors on database operations
|
|
//
|
|
DebugInfo.DbsOutOfSpace = 0;
|
|
if (!FrsSearchArgvDWord(argc, argv, L"dbsoutOfSpace", &DebugInfo.DbsOutOfSpace)) {
|
|
CfgRegReadDWord(FKC_DEBUG_DBS_OUT_OF_SPACE, NULL, 0, &DebugInfo.DbsOutOfSpace);
|
|
}
|
|
|
|
//
|
|
// Trigger phoney out of space errors on database operations
|
|
//
|
|
DebugInfo.DbsOutOfSpaceTrigger = 0;
|
|
if (!FrsSearchArgvDWord(argc, argv, L"outofspacetrigger", &DebugInfo.DbsOutOfSpaceTrigger)) {
|
|
CfgRegReadDWord(FKC_DEBUG_DBS_OUT_OF_SPACE_TRIGGER, NULL, 0, &DebugInfo.DbsOutOfSpaceTrigger);
|
|
}
|
|
|
|
//
|
|
// Enable compression. Default will be on.
|
|
//
|
|
CfgRegReadDWord(FKC_DEBUG_DISABLE_COMPRESSION, NULL, 0, &DebugInfo.DisableCompression);
|
|
|
|
//
|
|
// Check if automatic cleanup of staging files is enabled or disabled.
|
|
//
|
|
CfgRegReadDWord(FKC_RECLAIM_STAGING_SPACE, NULL, 0, &DebugInfo.ReclaimStagingSpace);
|
|
|
|
//
|
|
// Check if a new value for outlog history time is set.
|
|
//
|
|
CfgRegReadDWord(FKC_OUTLOG_CHANGE_HISTORY, NULL, 0, &DebugInfo.OutlogChangeHistory);
|
|
|
|
//
|
|
// Check if saving outlog history is disabled.
|
|
//
|
|
CfgRegReadDWord(FKC_SAVE_OUTLOG_CHANGE_HISTORY, NULL, 0, &DebugInfo.SaveOutlogChangeHistory);
|
|
|
|
//
|
|
// Check if a new value for install override is set.
|
|
//
|
|
CfgRegReadDWord(FKC_ENABLE_INSTALL_OVERRIDE, NULL, 0, &DebugInfo.EnableInstallOverride);
|
|
|
|
//
|
|
// Check if a new value for forced rename on file updates is set.
|
|
//
|
|
CfgRegReadDWord(FKC_ENABLE_RENAME_BASED_UPDATES, NULL, 0, &DebugInfo.EnableRenameUpdates);
|
|
|
|
//
|
|
// Check if suppress identical updates is disabled.
|
|
//
|
|
CfgRegReadDWord(FKC_SUPPRESS_IDENTICAL_UPDATES, NULL, 0, &DebugInfo.SuppressIdenticalUpdt);
|
|
|
|
//
|
|
// Ldap Search timeout. Default is 10 minutes.
|
|
//
|
|
CfgRegReadDWord(FKC_LDAP_SEARCH_TIMEOUT_IN_MINUTES, NULL, 0, &LdapSearchTimeoutInMinutes);
|
|
|
|
//
|
|
// Ldap Bind timeout. Default is 30 seconds.
|
|
//
|
|
CfgRegReadDWord(FKC_LDAP_BIND_TIMEOUT_IN_SECONDS, NULL, 0, &LdapBindTimeoutInSeconds);
|
|
|
|
//
|
|
// Get boolean to tell us if we should do automatic restore when
|
|
// we hit journal wrap
|
|
//
|
|
CfgRegReadDWord(FKC_ENABLE_JOURNAL_WRAP_AUTOMATIC_RESTORE, NULL, 0, &DebugInfo.EnableJrnlWrapAutoRestore);
|
|
|
|
//
|
|
// Display the debug parameters.
|
|
//
|
|
DbgPrintInfo(0);
|
|
|
|
|
|
|
|
//
|
|
// Remember our start time (in minutes)
|
|
//
|
|
// 100-nsecs / (10 (microsecs) * 1000 (msecs) * 1000 (secs) * 60 (min)
|
|
//
|
|
GetSystemTimeAsFileTime((FILETIME *)&DebugInfo.StartSeconds);
|
|
DebugInfo.StartSeconds /= (10 * 1000 * 1000);
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
DbgMinimumInit(
|
|
VOID
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Called at the beginning of MainMinimumInit()
|
|
|
|
Arguments:
|
|
None.
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DbgMinimumInit:"
|
|
|
|
HANDLE ThreadHandle;
|
|
DWORD ThreadId;
|
|
//
|
|
// This thread forces an assert after DebugInfo.AssertSeconds
|
|
//
|
|
if (DebugInfo.AssertSeconds) {
|
|
ThreadHandle = (HANDLE)CreateThread(NULL,
|
|
0,
|
|
DbgForceAssert,
|
|
NULL,
|
|
0,
|
|
&ThreadId);
|
|
|
|
FRS_ASSERT(HANDLE_IS_VALID(ThreadHandle));
|
|
|
|
DbgCaptureThreadInfo2(L"ForceAssert", DbgForceAssert, ThreadId);
|
|
FRS_CLOSE(ThreadHandle);
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
DoDebug(
|
|
IN ULONG Sev,
|
|
IN UCHAR *DebSub
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Should we print this line
|
|
|
|
Arguments:
|
|
sev
|
|
debsub
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DoDebug:"
|
|
|
|
//
|
|
// Debugging has been disabled
|
|
//
|
|
if (DebugInfo.Disabled) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Not important enough
|
|
//
|
|
if (Sev > DebugInfo.Severity && Sev > DebugInfo.LogSeverity) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Not tracing this subsystem
|
|
//
|
|
if (DebSub &&
|
|
DebugInfo.Systems &&
|
|
(strstr(DebugInfo.Systems, DebSub) == NULL)) {
|
|
return FALSE;
|
|
}
|
|
//
|
|
// Not tracing this thread
|
|
//
|
|
if (DebugInfo.ThreadId &&
|
|
GetCurrentThreadId() != DebugInfo.ThreadId) {
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
DebPrintLine(
|
|
IN ULONG Sev,
|
|
IN PCHAR Line
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Print a line of debug output to various combinations
|
|
of standard out, debugger, kernel debugger, and a log file.
|
|
|
|
Arguments:
|
|
Sev
|
|
Line
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DebPrintLine:"
|
|
|
|
DWORD BytesWritten = 0;
|
|
//
|
|
// stdout
|
|
//
|
|
if ((Sev <= DebugInfo.Severity) && !RunningAsAService) {
|
|
printf("%s", Line);
|
|
}
|
|
|
|
//
|
|
// log file
|
|
//
|
|
if (HANDLE_IS_VALID(DebugInfo.LogFILE) && Sev <= DebugInfo.LogSeverity) {
|
|
//
|
|
// Number of messages exceeded; save the old file and
|
|
// start afresh. The existing old file is deleted.
|
|
//
|
|
if (DebugInfo.LogLines > DebugInfo.MaxLogLines) {
|
|
FrsFlushFile(L"LogFILE", DebugInfo.LogFILE);
|
|
DbgFlushInterval = DebugInfo.LogFlushInterval;
|
|
FRS_CLOSE(DebugInfo.LogFILE);
|
|
|
|
DbgShiftLogFiles(DebugInfo.LogFile,
|
|
LOG_FILE_SUFFIX,
|
|
(DebugInfo.CopyLogs) ? DebugInfo.AssertShare : NULL,
|
|
DebugInfo.LogFiles);
|
|
|
|
DbgOpenLogFile();
|
|
DebugInfo.LogLines = 0;
|
|
DebugInfo.PrintStats = TRUE;
|
|
}
|
|
|
|
if (HANDLE_IS_VALID(DebugInfo.LogFILE)) {
|
|
WriteFile(DebugInfo.LogFILE,
|
|
Line,
|
|
strlen(Line),
|
|
&BytesWritten,
|
|
NULL);
|
|
//
|
|
// Flush the log file every DebugInfo.LogFlushInterval lines and on
|
|
// every severity 0 message.
|
|
//
|
|
if ((--DbgFlushInterval < 0) || (Sev ==0)) {
|
|
if (!WIN_SUCCESS(FrsFlushFile(L"LogFILE", DebugInfo.LogFILE))) {
|
|
FRS_CLOSE(DebugInfo.LogFILE);
|
|
}
|
|
DbgFlushInterval = DebugInfo.LogFlushInterval;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// debugger
|
|
//
|
|
if ((Sev <= DebugInfo.Severity) && !DebugInfo.Suppress) {
|
|
OutputDebugStringA(Line);
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
DebFormatLine(
|
|
IN ULONG Sev,
|
|
IN BOOL Format,
|
|
IN PCHAR DebSub,
|
|
IN UINT LineNo,
|
|
IN PCHAR Line,
|
|
IN ULONG LineSize,
|
|
IN PUCHAR Str,
|
|
IN va_list argptr
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Format the line of output
|
|
|
|
Arguments:
|
|
DebSub
|
|
LineNo
|
|
Line
|
|
LineSize
|
|
Str
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DebFormatLine:"
|
|
|
|
ULONG LineUsed;
|
|
SYSTEMTIME SystemTime;
|
|
BOOL Ret = TRUE;
|
|
|
|
try {
|
|
if (Format) {
|
|
//
|
|
// Increment the line count here to prevent counting
|
|
// the several DPRINTs that don't have a newline.
|
|
//
|
|
++DebugInfo.LogLines;
|
|
++DebugInfo.TotalLogLines;
|
|
GetLocalTime(&SystemTime);
|
|
|
|
if (_snprintf(Line,
|
|
LineSize,
|
|
"<%-31s%4u: %5u: S%1u: %02d:%02d:%02d> ",
|
|
(DebSub) ? DebSub : "NoName",
|
|
GetCurrentThreadId(),
|
|
LineNo,
|
|
Sev,
|
|
SystemTime.wHour,
|
|
SystemTime.wMinute,
|
|
SystemTime.wSecond) < 0) {
|
|
Line[LineSize - 1] = '\0';
|
|
Ret = FALSE;
|
|
} else {
|
|
LineUsed = strlen(Line);
|
|
}
|
|
|
|
} else {
|
|
LineUsed = 0;
|
|
}
|
|
|
|
if (Ret) {
|
|
if (((LineUsed + 1) >= LineSize) ||
|
|
(_vsnprintf(Line + LineUsed, LineSize - LineUsed, Str, argptr) < 0)) {
|
|
Ret = FALSE;
|
|
}
|
|
}
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Ret = FALSE;
|
|
}
|
|
|
|
return Ret;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
DebFormatTrackingLine(
|
|
IN PCHAR Line,
|
|
IN ULONG LineSize,
|
|
IN PUCHAR Str,
|
|
IN va_list argptr
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Format the line of output
|
|
|
|
Arguments:
|
|
Line
|
|
LineSize
|
|
Str
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DebFormatTrackingLine:"
|
|
|
|
ULONG LineUsed = 0;
|
|
SYSTEMTIME SystemTime;
|
|
BOOL Ret = TRUE;
|
|
|
|
try {
|
|
//
|
|
// Increment the line count here to prevent counting
|
|
// the several DPRINTs that don't have a newline.
|
|
//
|
|
++DebugInfo.LogLines;
|
|
++DebugInfo.TotalLogLines;
|
|
GetLocalTime(&SystemTime);
|
|
|
|
if (_snprintf(Line,
|
|
LineSize,
|
|
"%2d/%2d-%02d:%02d:%02d ",
|
|
SystemTime.wMonth,
|
|
SystemTime.wDay,
|
|
SystemTime.wHour,
|
|
SystemTime.wMinute,
|
|
SystemTime.wSecond) < 0) {
|
|
Line[LineSize - 1] = '\0';
|
|
Ret = FALSE;
|
|
} else {
|
|
LineUsed = strlen(Line);
|
|
}
|
|
|
|
|
|
if (Ret) {
|
|
if (((LineUsed + 1) >= LineSize) ||
|
|
(_vsnprintf(Line + LineUsed, LineSize - LineUsed, Str, argptr) < 0)) {
|
|
Ret = FALSE;
|
|
}
|
|
}
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Ret = FALSE;
|
|
}
|
|
|
|
return Ret;
|
|
}
|
|
|
|
|
|
VOID
|
|
DebPrintTrackingNoLock(
|
|
IN ULONG Sev,
|
|
IN PUCHAR Str,
|
|
IN ... )
|
|
/*++
|
|
Routine Description:
|
|
Format and print a line of tracking output to various combinations
|
|
of standard out, debugger, kernel debugger, and a log file. The
|
|
debug print lock is held and the caller filtered lines that
|
|
shouldn't be printed.
|
|
|
|
Arguments:
|
|
Sev - severity level
|
|
Str - printf format
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DebPrintTrackingNoLock:"
|
|
|
|
|
|
CHAR Buf[512];
|
|
DWORD BufUsed = 0;
|
|
|
|
//
|
|
// varargs stuff
|
|
//
|
|
va_list argptr;
|
|
va_start(argptr, Str);
|
|
|
|
//
|
|
// Print the line to some combination of stdout, file, and debugger
|
|
//
|
|
if (DebFormatTrackingLine(Buf, sizeof(Buf), Str, argptr)) {
|
|
DebPrintLine(Sev, Buf);
|
|
}
|
|
|
|
va_end(argptr);
|
|
}
|
|
|
|
|
|
VOID
|
|
DebLock(
|
|
VOID
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Acquire the print lock
|
|
|
|
Arguments:
|
|
None.
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DebLock:"
|
|
|
|
EnterCriticalSection(&DebugInfo.Lock);
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
DebTryLock(
|
|
VOID
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Try to acquire the print lock
|
|
|
|
Arguments:
|
|
None.
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DebTryLock:"
|
|
|
|
return TryEnterCriticalSection(&DebugInfo.Lock);
|
|
}
|
|
|
|
|
|
VOID
|
|
DebUnLock(
|
|
VOID
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Release the print lock
|
|
|
|
Arguments:
|
|
None.
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DebUnLock:"
|
|
|
|
BOOL PrintStats;
|
|
|
|
//
|
|
// Print summary stats close to the beginning of each
|
|
// log file. The stats may show up a few lines into
|
|
// the new log file when callers hold the DebLock() across
|
|
// several lines.
|
|
//
|
|
// Be careful not to recurse if MaxLogLines is smaller
|
|
// than the number of lines in the stats.
|
|
//
|
|
if (DebugInfo.PrintStats) {
|
|
if (DebugInfo.PrintingStats) {
|
|
DebugInfo.PrintStats = FALSE;
|
|
} else {
|
|
DebugInfo.PrintingStats = TRUE;
|
|
}
|
|
}
|
|
PrintStats = DebugInfo.PrintStats;
|
|
LeaveCriticalSection(&DebugInfo.Lock);
|
|
|
|
if (PrintStats) {
|
|
DbgPrintAllStats();
|
|
EnterCriticalSection(&DebugInfo.Lock);
|
|
DebugInfo.PrintingStats = FALSE;
|
|
LeaveCriticalSection(&DebugInfo.Lock);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
DebPrintNoLock(
|
|
IN ULONG Sev,
|
|
IN BOOL Format,
|
|
IN PUCHAR Str,
|
|
IN PCHAR DebSub,
|
|
IN UINT LineNo,
|
|
IN ... )
|
|
/*++
|
|
Routine Description:
|
|
Format and print a line of debug output to various combinations
|
|
of standard out, debugger, kernel debugger, and a log file. The
|
|
debug print lock is held and the caller filtered lines that
|
|
shouldn't be printed.
|
|
|
|
Arguments:
|
|
Sev - severity filter
|
|
Format - Add format info?
|
|
Str - printf format
|
|
DebSub - module name
|
|
LineNo
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DebPrintNoLock:"
|
|
|
|
|
|
CHAR Buf[512];
|
|
DWORD BufUsed = 0;
|
|
|
|
//
|
|
// varargs stuff
|
|
//
|
|
va_list argptr;
|
|
va_start(argptr, LineNo);
|
|
|
|
//
|
|
// Print the line to some combination of stdout, file, and debugger
|
|
//
|
|
if (DebFormatLine(Sev, Format, DebSub, LineNo, Buf, sizeof(Buf), Str, argptr)) {
|
|
DebPrintLine(Sev, Buf);
|
|
}
|
|
|
|
va_end(argptr);
|
|
}
|
|
|
|
|
|
VOID
|
|
DebPrint(
|
|
IN ULONG Sev,
|
|
IN PUCHAR Str,
|
|
IN PCHAR DebSub,
|
|
IN UINT LineNo,
|
|
IN ... )
|
|
/*++
|
|
Routine Description:
|
|
Format and print a line of debug output to various combinations
|
|
of standard out, debugger, kernel debugger, and a log file.
|
|
|
|
Arguments:
|
|
sev - severity filter
|
|
str - printf format
|
|
debsub - module name
|
|
LineNo
|
|
|
|
Return Value:
|
|
None.
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DebPrint:"
|
|
|
|
|
|
CHAR Buf[512];
|
|
DWORD BufUsed = 0;
|
|
|
|
//
|
|
// varargs stuff
|
|
//
|
|
va_list argptr;
|
|
va_start(argptr, LineNo);
|
|
|
|
//
|
|
// Don't print this
|
|
//
|
|
if (!DoDebug(Sev, DebSub)) {
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Print the line to some combination of stdout, file, and debugger
|
|
//
|
|
DebLock();
|
|
if (DebFormatLine(Sev, TRUE, DebSub, LineNo, Buf, sizeof(Buf), Str, argptr)) {
|
|
DebPrintLine(Sev, Buf);
|
|
}
|
|
|
|
DebUnLock();
|
|
va_end(argptr);
|
|
|
|
#if 0
|
|
|
|
static int failedload = FALSE;
|
|
static TRANSMITSPECIALFRAME_FN lpfnTransmitSpecialFrame = NULL;
|
|
//
|
|
// Calling nal.dll from inside lsa causes a deadlock during startup
|
|
//
|
|
if ( /* (!RunningAsAService) &&*/ // davidor - lets try it.
|
|
(NmDebugTest(sev, DebSub)))
|
|
{
|
|
if (failedload == FALSE) {
|
|
|
|
//
|
|
// Only try to load the NetMon trace routine once.
|
|
//
|
|
|
|
if (!lpfnTransmitSpecialFrame) {
|
|
HINSTANCE hInst;
|
|
|
|
hInst = LoadLibrary (L"NAL.DLL" );
|
|
if (hInst) {
|
|
lpfnTransmitSpecialFrame =
|
|
(TRANSMITSPECIALFRAME_FN)GetProcAddress ( hInst, "TransmitSpecialFrame" );
|
|
}
|
|
}
|
|
|
|
if (lpfnTransmitSpecialFrame) {
|
|
int length;
|
|
int length2;
|
|
unsigned char buff[256];
|
|
|
|
if (DebSub) {
|
|
length = _snprintf(buff, sizeof(buff), "<%s%u:%u> ", DebSub, tid, uLineNo);
|
|
buff[sizeof(buff) - 1] = '\0';
|
|
} else {
|
|
length = 0;
|
|
}
|
|
|
|
length2 = _vsnprintf(buff + length, sizeof(buff) - length, str, argptr );
|
|
|
|
lpfnTransmitSpecialFrame(FRAME_TYPE_COMMENT,
|
|
0,
|
|
buff,
|
|
length + length2 + 1 );
|
|
} else {
|
|
failedload = TRUE; // that was our one and only try to load the routine.
|
|
}
|
|
}
|
|
}
|
|
#endif 0
|
|
}
|
|
|
|
|
|
VOID
|
|
DbgDoAssert(
|
|
IN PCHAR Exp,
|
|
IN UINT Line,
|
|
IN PCHAR Debsub
|
|
)
|
|
/*++
|
|
Routine Description:
|
|
Assertion failure; print a message and exit after allowing some
|
|
time for shutdown.
|
|
|
|
Arguments:
|
|
Exp - failing assertion expression
|
|
Line - line number of failing expression
|
|
Debsub - module name of failing expression
|
|
|
|
Return Value:
|
|
Doesn't return
|
|
--*/
|
|
{
|
|
#undef DEBSUB
|
|
#define DEBSUB "DbgDoAssert:"
|
|
|
|
|
|
PWCHAR ExpW;
|
|
PWCHAR DebsubW;
|
|
WCHAR LineW[32];
|
|
|
|
//
|
|
// Inform the world
|
|
//
|
|
FrsIsAsserting = TRUE;
|
|
|
|
|
|
ExpW = FrsAtoW(Exp);
|
|
DebsubW = FrsAtoW(Debsub);
|
|
_snwprintf(LineW, 32, L"%d", Line);
|
|
LineW[ARRAY_SZ(LineW)-1] = L'\0';
|
|
//
|
|
// Post an error log entry followed by recovery steps.
|
|
//
|
|
EPRINT3(EVENT_FRS_ASSERT, DebsubW, LineW, ExpW);
|
|
EPRINT1(EVENT_FRS_IN_ERROR_STATE, JetPath);
|
|
FrsFree(ExpW);
|
|
FrsFree(DebsubW);
|
|
|
|
//
|
|
// Stack trace
|
|
//
|
|
if (!DebugInfo.Mem) {
|
|
//
|
|
// Init the symbols here since mem alloc tracing is off.
|
|
//
|
|
DbgStackInit();
|
|
}
|
|
DbgPrintStackTrace(0, Debsub, Line);
|
|
|
|
//
|
|
// Failing expression
|
|
//
|
|
DebPrint(0, ":S: ASSERTION FAILURE: %s\n", Debsub, Line, Exp);
|
|
|
|
//
|
|
// Save the log file as an assert file
|
|
//
|
|
#if 0
|
|
// disable saving assert logs under separate name; too confusing
|
|
//
|
|
if (HANDLE_IS_VALID(DebugInfo.LogFILE)) {
|
|
DebLock();
|
|
FrsFlushFile(L"LogFILE", DebugInfo.LogFILE);
|
|
DbgFlushInterval = DebugInfo.LogFlushInterval;
|
|
FRS_CLOSE(DebugInfo.LogFILE);
|
|
DbgShiftLogFiles(DebugInfo.LogFile,
|
|
ASSERT_FILE_SUFFIX,
|
|
DebugInfo.AssertShare,
|
|
DebugInfo.AssertFiles);
|
|
|
|
DebugInfo.LogFILE = CreateFile(LogPath,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
FILE_FLAG_BACKUP_SEMANTICS,
|
|
NULL);
|
|
DebugInfo.LogLines = 0;
|
|
DebugInfo.PrintStats = TRUE;
|
|
DebUnLock();
|
|
}
|
|
#endif 0
|
|
|
|
DEBUG_FLUSH();
|
|
|
|
|
|
//
|
|
// Break into the debugger, if any
|
|
//
|
|
if (DebugInfo.Break && IsDebuggerPresent()) {
|
|
DebugBreak();
|
|
}
|
|
|
|
//
|
|
// Shutting down during an assert seldom completes; a critical thread
|
|
// is usually the thread that has asserted. One can't simply return
|
|
// from an assert. So exit the process and trust jet and ntfrs to
|
|
// recover at start up.
|
|
//
|
|
// FrsIsShuttingDown = TRUE;
|
|
// SetEvent(ShutDownEvent);
|
|
// ExitThread(1);
|
|
|
|
//
|
|
// Raise an exception.
|
|
//
|
|
if (--DbgRaiseCount <= 0) {
|
|
exit(1);
|
|
}
|
|
|
|
XRAISEGENEXCEPTION( ERROR_OPERATION_ABORTED );
|
|
|
|
}
|
|
#endif
|