mirror of https://github.com/lianthony/NT4.0
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.
410 lines
12 KiB
410 lines
12 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: debugc.c
|
|
*
|
|
* Copyright (c) 1985-93, Microsoft Corporation
|
|
*
|
|
* This module contains random debugging related functions.
|
|
*
|
|
* History:
|
|
* 17-May-1991 DarrinM Created.
|
|
* 22-Jan-1992 IanJa ANSI/Unicode neutral (all debug output is ANSI)
|
|
* 11-Mar-1993 JerrySh Pulled functions from user\server.
|
|
\***************************************************************************/
|
|
|
|
#ifdef DEBUG
|
|
|
|
VOID
|
|
UserRtlRaiseStatus(
|
|
NTSTATUS Status);
|
|
|
|
/***************************************************************************\
|
|
* VRipOutput
|
|
*
|
|
* Formats a variable argument string and calls RipOutput.
|
|
*
|
|
* History:
|
|
* 19-Mar-1996 adams Created.
|
|
\***************************************************************************/
|
|
|
|
BOOL _cdecl VRipOutput(
|
|
DWORD idErr,
|
|
DWORD flags,
|
|
LPSTR pszFile,
|
|
int iLine,
|
|
LPSTR pszFmt,
|
|
...)
|
|
{
|
|
char szT[160];
|
|
va_list arglist;
|
|
|
|
va_start(arglist, pszFmt);
|
|
vsprintf(szT, pszFmt, arglist);
|
|
va_end(arglist);
|
|
return RipOutput(idErr, flags, pszFile, iLine, szT, NULL);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* RipOutput
|
|
*
|
|
* Sets the last error if it is non-zero, prints a message to
|
|
* the debugger, and prompts for more debugging actions.
|
|
*
|
|
* History:
|
|
* 01-23-91 DarrinM Created.
|
|
* 04-15-91 DarrinM Added exception handling support.
|
|
* 03-19-96 adams Made flags a separate argument, cleanup.
|
|
\***************************************************************************/
|
|
|
|
LPCSTR aszComponents[] = {
|
|
"Unknown", // 0x00000000
|
|
"USER32", // RIP_USER 0x00010000
|
|
"USERSRV", // RIP_USERSRV 0x00020000
|
|
"USERRTL", // RIP_USERRTL 0x00030000
|
|
"GDI32", // RIP_GDI 0x00040000
|
|
"GDISRV", // RIP_GDISRV 0x00050000
|
|
"GDIRTL", // RIP_GDIRTL 0x00060000
|
|
"KERNEL32", // RIP_BASE 0x00070000
|
|
"BASESRV", // RIP_BASESRV 0x00080000
|
|
"BASERTL", // RIP_BASERTL 0x00090000
|
|
"DISPLAYDRV", // RIP_DISPLAYDRV 0x000A0000
|
|
"CONSRV", // RIP_CONSRV 0x000B0000
|
|
"USERKRNL", // RIP_USERKRNL 0x000C0000
|
|
#ifdef FE_IME
|
|
"IMM32", // RIP_IMM 0x000D0000
|
|
#else
|
|
"Unknown", // 0x000D0000
|
|
#endif
|
|
"Unknown", // 0x000E0000
|
|
"Unknown", // 0x000F0000
|
|
};
|
|
|
|
BOOL RipOutput(
|
|
DWORD idErr,
|
|
DWORD flags,
|
|
LPSTR pszFile,
|
|
int iLine,
|
|
LPSTR pszErr,
|
|
PEXCEPTION_POINTERS pexi)
|
|
{
|
|
static char *szLevels[8] = {
|
|
"<none>",
|
|
"Errors",
|
|
"Warnings",
|
|
"Errors and Warnings",
|
|
"Verbose",
|
|
"Errors and Verbose",
|
|
"Warnings and Verbose",
|
|
"Errors, Warnings, and Verbose"
|
|
};
|
|
|
|
char szT[160];
|
|
BOOL fPrint;
|
|
BOOL fPrompt;
|
|
BOOL afDummy[5];
|
|
BOOL fUseDummy = FALSE;
|
|
DWORD dwT;
|
|
DWORD dwP;
|
|
LPSTR pszFormat;
|
|
LPSTR pszType;
|
|
BOOL fBreak = FALSE;
|
|
|
|
/*
|
|
* Do not clear the Last Error!
|
|
*/
|
|
if (idErr) {
|
|
UserSetLastError(idErr);
|
|
}
|
|
|
|
/*
|
|
* If we have not initialized yet, gpsi will be NULL. Fix up
|
|
* gpsi so it will point to something meaningful.
|
|
*/
|
|
if (gpsi == NULL) {
|
|
fUseDummy = TRUE;
|
|
gpsi = (PSERVERINFO)afDummy;
|
|
gpsi->RipFlags = 0;
|
|
}
|
|
|
|
UserAssert(flags & (RIP_ERROR | RIP_WARNING | RIP_VERBOSE));
|
|
if (flags & RIP_ERROR) {
|
|
fPrint = TEST_RIP_FLAG(RIPF_PRINTONERROR);
|
|
fPrompt = TEST_RIP_FLAG(RIPF_PROMPTONERROR);
|
|
pszType = "Error";
|
|
} else if (flags & RIP_WARNING) {
|
|
fPrint = TEST_RIP_FLAG(RIPF_PRINTONWARNING);
|
|
fPrompt = TEST_RIP_FLAG(RIPF_PROMPTONWARNING);
|
|
pszType = "Warning";
|
|
} else if (flags & RIP_VERBOSE) {
|
|
fPrint = TEST_RIP_FLAG(RIPF_PRINTONVERBOSE);
|
|
fPrompt = TEST_RIP_FLAG(RIPF_PROMPTONVERBOSE);
|
|
pszType = "Verbose";
|
|
}
|
|
|
|
/*
|
|
* Print the formatted error string.
|
|
*/
|
|
if (fPrint || fPrompt) {
|
|
#ifdef _USERK_
|
|
{
|
|
PETHREAD pet;
|
|
|
|
if (pet = PsGetCurrentThread()) {
|
|
dwT = (DWORD)pet->Cid.UniqueThread;
|
|
dwP = (DWORD)pet->Cid.UniqueProcess;
|
|
} else {
|
|
dwT = dwP = 0;
|
|
}
|
|
}
|
|
#else
|
|
{
|
|
PTEB pteb;
|
|
if (pteb = NtCurrentTeb()) {
|
|
dwT = (DWORD)pteb->ClientId.UniqueThread;
|
|
dwP = (DWORD)pteb->ClientId.UniqueProcess;
|
|
} else {
|
|
dwT = dwP = 0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (idErr) {
|
|
if (TEST_RIP_FLAG(RIPF_PRINTFILELINE) && (pexi == NULL)) {
|
|
pszFormat = "PID:%#lx.%lx %s[%s,LastErr=%ld] %s\n %s, line %d\n";
|
|
} else {
|
|
pszFormat = "PID:%#lx.%lx %s[%s,LastErr=%ld] %s\n";
|
|
}
|
|
|
|
wsprintfA(
|
|
szT,
|
|
pszFormat,
|
|
dwP,
|
|
dwT,
|
|
aszComponents[(flags & RIP_COMPBITS) >> 0x10],
|
|
pszType,
|
|
idErr,
|
|
pszErr,
|
|
pszFile,
|
|
iLine);
|
|
} else {
|
|
if (TEST_RIP_FLAG(RIPF_PRINTFILELINE) && (pexi == NULL)) {
|
|
pszFormat = "PID:%#lx.%lx %s[%s] %s\n %s, line %d\n";
|
|
} else {
|
|
pszFormat = "PID:%#lx.%lx %s[%s] %s\n";
|
|
}
|
|
|
|
wsprintfA(
|
|
szT,
|
|
pszFormat,
|
|
dwP,
|
|
dwT,
|
|
aszComponents[(flags & RIP_COMPBITS) >> 0x10],
|
|
pszType,
|
|
pszErr,
|
|
pszFile,
|
|
iLine);
|
|
}
|
|
|
|
KdPrint((szT));
|
|
}
|
|
|
|
while (fPrompt) {
|
|
/*
|
|
* We have some special options for handling exceptions.
|
|
*/
|
|
|
|
/*
|
|
* We can't toggle prompting in user mode, so don't allow it
|
|
* as an option.
|
|
*/
|
|
#ifdef _USERK_
|
|
if (pexi != NULL)
|
|
DbgPrompt("[gbixf?]", szT, sizeof(szT));
|
|
else
|
|
DbgPrompt("[gbwxf?]", szT, sizeof(szT));
|
|
#else
|
|
if (pexi != NULL)
|
|
DbgPrompt("[gbix?]", szT, sizeof(szT));
|
|
else
|
|
DbgPrompt("[gbwx?]", szT, sizeof(szT));
|
|
#endif
|
|
|
|
switch (szT[0] | (char)0x20) {
|
|
case 'g':
|
|
fPrompt = FALSE;
|
|
break;
|
|
|
|
case 'b':
|
|
fBreak = TRUE;
|
|
fPrompt = FALSE;
|
|
break;
|
|
|
|
#ifdef LATER
|
|
case 's':
|
|
// DbgStackBacktrace();
|
|
KdPrint(("Can't do that yet.\n"));
|
|
break;
|
|
#endif
|
|
|
|
case 'x':
|
|
if (pexi != NULL) {
|
|
/*
|
|
* The root-level exception handler will complete the
|
|
* termination of this thread.
|
|
*/
|
|
fPrompt = FALSE;
|
|
break;
|
|
} else {
|
|
|
|
/*
|
|
* Raise an exception, that will kill it real good.
|
|
*/
|
|
KdPrint(("Now raising the exception of death. "
|
|
"Type 'x' again to finish the job.\n"));
|
|
UserRtlRaiseStatus( 0x15551212 );
|
|
}
|
|
break;
|
|
|
|
case 'w':
|
|
if (pexi != NULL)
|
|
break;
|
|
KdPrint(("File: %s, Line: %d\n", pszFile, iLine));
|
|
break;
|
|
|
|
case 'i':
|
|
/*
|
|
* Dump some useful information about this exception, like its
|
|
* address, and the contents of the interesting registers at
|
|
* the time of the exception.
|
|
*/
|
|
if (pexi == NULL)
|
|
break;
|
|
#if defined(i386) // legal
|
|
/*
|
|
* eip = instruction pointer
|
|
* esp = stack pointer
|
|
* ebp = stack frame pointer
|
|
*/
|
|
KdPrint(("eip = %lx\n", pexi->ContextRecord->Eip));
|
|
KdPrint(("esp = %lx\n", pexi->ContextRecord->Esp));
|
|
KdPrint(("ebp = %lx\n", pexi->ContextRecord->Ebp));
|
|
#elif defined(_PPC_)
|
|
/*
|
|
* Iar = instruction register
|
|
* sp = stack pointer
|
|
* ra = return address
|
|
*/
|
|
KdPrint(("Iar = %lx\n", pexi->ContextRecord->Iar));
|
|
KdPrint(("sp = %lx\n", pexi->ContextRecord->Gpr1));
|
|
KdPrint(("ra = %lx\n", pexi->ContextRecord->Lr));
|
|
#else // _MIPS_ || _ALPHA_
|
|
/*
|
|
* fir = instruction register
|
|
* sp = stack pointer
|
|
* ra = return address
|
|
*/
|
|
KdPrint(("fir = %lx\n", pexi->ContextRecord->Fir));
|
|
KdPrint(("sp = %lx\n", pexi->ContextRecord->IntSp));
|
|
KdPrint(("ra = %lx\n", pexi->ContextRecord->IntRa));
|
|
#endif
|
|
break;
|
|
|
|
case '?':
|
|
KdPrint(("g - GO, ignore the error and continue execution\n"));
|
|
if (pexi != NULL) {
|
|
KdPrint(("b - BREAK into the debugger at the location of the exception (part impl.)\n"));
|
|
KdPrint(("i - INFO on instruction pointer and stack pointers\n"));
|
|
KdPrint(("x - execute cleanup code and KILL the thread by returning EXECUTE_HANDLER\n"));
|
|
} else {
|
|
KdPrint(("b - BREAK into the debugger at the location of the error (part impl.)\n"));
|
|
KdPrint(("w - display the source code location WHERE the error occured\n"));
|
|
KdPrint(("x - KILL the offending thread by raising an exception\n"));
|
|
}
|
|
|
|
#ifdef _USERK_
|
|
KdPrint(("f - FLAGS, enter debug flags in format <File/Line><Print><Prompt>\n"));
|
|
KdPrint((" <File/Line> = [0|1]\n"));
|
|
KdPrint((" <Print> = [0-7] Errors = 1, Warnings = 2, Verbose = 4\n"));
|
|
KdPrint((" <Prompt> = [0-7] Errors = 1, Warnings = 2, Verbose = 4\n"));
|
|
KdPrint((" The default is 031\n"));
|
|
#endif
|
|
|
|
#ifdef LATER
|
|
KdPrint(("s - dump a STACK BACKTRACE (unimplemented)\n"));
|
|
KdPrint(("m - Dump the CSR heap\n"));
|
|
#endif
|
|
|
|
break;
|
|
|
|
#ifdef LATER
|
|
case 'm':
|
|
/*
|
|
* LATER
|
|
* Dump everything we know about the shared CSR heap.
|
|
*/
|
|
RtlValidateHeap(RtlProcessHeap(), 0, NULL);
|
|
break;
|
|
#endif
|
|
|
|
#ifdef _USERK_
|
|
case 'f':
|
|
{
|
|
ULONG ulFlags;
|
|
NTSTATUS status;
|
|
int i;
|
|
|
|
szT[ARRAY_SIZE(szT) - 1] = 0; /* don't overflow buffer */
|
|
for (i = 1; i < ARRAY_SIZE(szT); i++) {
|
|
if ('0' <= szT[i] && szT[i] <= '9') {
|
|
status = RtlCharToInteger(&szT[i], 16, &ulFlags);
|
|
if (NT_SUCCESS(status) && !(ulFlags & ~RIPF_VALIDUSERFLAGS)) {
|
|
gpsi->RipFlags = (gpsi->RipFlags & ~RIPF_VALIDUSERFLAGS) | ulFlags;
|
|
}
|
|
break;
|
|
} else if (szT[i] != ' ' && szT[i] != '\t') {
|
|
break;
|
|
}
|
|
}
|
|
|
|
KdPrint(("Flags = %x\n", gpsi->RipFlags & RIPF_VALIDUSERFLAGS));
|
|
KdPrint((" Print File/Line %sabled\n", (TEST_RIP_FLAG(RIPF_PRINTFILELINE)) ? "en" : "dis"));
|
|
KdPrint((" Print on %s\n", szLevels[(gpsi->RipFlags & 0x70) >> 4]));
|
|
KdPrint((" Prompt on %s\n", szLevels[gpsi->RipFlags & 0x07]));
|
|
|
|
break;
|
|
}
|
|
#endif // ifdef _USERK_
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Clear gpsi if we've been faking it.
|
|
*/
|
|
if (fUseDummy)
|
|
gpsi = NULL;
|
|
|
|
return fBreak;
|
|
}
|
|
|
|
#endif
|
|
|
|
VOID UserSetLastError(
|
|
DWORD dwErrCode
|
|
)
|
|
{
|
|
PTEB pteb;
|
|
|
|
UserAssert(
|
|
!(dwErrCode & 0xFFFF0000) &&
|
|
"Error code passed to UserSetLastError is not a valid Win32 error.");
|
|
|
|
pteb = NtCurrentTeb();
|
|
if (pteb)
|
|
pteb->LastErrorValue = (LONG)dwErrCode;
|
|
}
|
|
|
|
VOID SetLastNtError(
|
|
NTSTATUS Status)
|
|
{
|
|
UserSetLastError(RtlNtStatusToDosError(Status));
|
|
}
|
|
|