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.
 
 
 
 
 
 

742 lines
22 KiB

/****************************** Module Header ******************************\
* Module Name: debugc.c
*
* Copyright (c) 1985 - 1999, 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.
\***************************************************************************/
#include "precomp.h"
#if DBG
extern DBGTAG gadbgtag[];
#endif
// common globals
extern CONST ALWAYSZERO gZero;
#if DBG
CONST LPCSTR aszComponents[] = {
"?", // 0x00000000
"USER", // RIP_USER 0x00010000
"WSRV", // RIP_USERSRV 0x00020000
"URTL", // RIP_USERRTL 0x00030000
"GDI", // RIP_GDI 0x00040000
"GDIK", // RIP_GDIKRNL 0x00050000
"GRTL", // RIP_GDIRTL 0x00060000
"KRNL", // RIP_BASE 0x00070000
"BSRV", // RIP_BASESRV 0x00080000
"BRTL", // RIP_BASERTL 0x00090000
"DISP", // RIP_DISPLAYDRV 0x000A0000
"CONS", // RIP_CONSRV 0x000B0000
"USRK", // RIP_USERKRNL 0x000C0000
#ifdef FE_IME
"IMM", // RIP_IMM 0x000D0000
#else
"?", // 0x000D0000
#endif
"?", // 0x000E0000
"?", // 0x000F0000
};
BOOL IsNumChar(
int c,
int base)
{
return ('0' <= c && c <= '9') ||
(base == 16 && (('a' <= c && c <= 'f') || ('A' <= c && c <= 'F')));
}
NTSTATUS GetInteger(
LPSTR psz,
int base,
int *pi,
LPSTR *ppsz)
{
NTSTATUS Status = STATUS_INVALID_PARAMETER;
for (;;) {
if (IsNumChar(*psz, base)) {
Status = RtlCharToInteger(psz, base, pi);
if (ppsz && NT_SUCCESS(Status)) {
while (IsNumChar(*psz++, base)) {
/* do nothing */;
}
*ppsz = psz;
}
break;
}
if (*psz != ' ' && *psz != '\t') {
break;
}
psz++;
}
return Status;
}
/*
* Use a separate debug assertion that doesn't cause recursion
* into this code.
*/
#define DebugAssertion(x) \
do { \
if (!(x)) { \
if (TEST_RIPF(RIPF_PRINTONERROR)) { \
KdPrint(("USER: Debug function assertion failure: %s \nFile %s line %ld\n", #x, __FILE__, __LINE__)); \
} \
if (TEST_RIPF(RIPF_PROMPTONERROR)) { \
DbgBreakPoint(); \
} \
} \
} while (FALSE)
/***************************************************************************\
* PrintAndPrompt
*
* Sets the last error, prints an error message, and prompts for
* debug actions.
*
* History:
* 11-Aug-1996 adams Created.
\***************************************************************************/
BOOL
PrintAndPrompt(
BOOL fPrint,
BOOL fPrompt,
DWORD idErr,
DWORD flags,
LPCSTR pszLevel,
LPCSTR pszFile,
int iLine,
LPCSTR pszFunction,
LPSTR pszErr,
PEXCEPTION_POINTERS pexi)
{
static CONST CHAR *szLevels[8] = {
"<none>",
"Errors",
"Warnings",
"Errors and Warnings",
"Verbose",
"Errors and Verbose",
"Warnings and Verbose",
"Errors, Warnings, and Verbose"
};
#ifdef _USERK_
static CONST CHAR *szSystem = "System";
static CONST CHAR *szUnknown = "???";
CONST CHAR *pszImage;
extern ULONG gSessionId;
#else
static CONST WCHAR *wszUnknown = L"???";
WCHAR *pwszImage;
ULONG ulLenImage;
#endif
DWORD dwT;
DWORD dwP;
DWORD dwSession;
char szT[512];
BOOL fBreak = FALSE;
/*
* Set the last error, but don't clear it!
*/
if (idErr) {
UserSetLastError(idErr);
}
/*
* Print the message.
*/
if (!fPrint && !fPrompt) {
return FALSE;
}
#ifdef _USERK_
{
PETHREAD pet;
PEPROCESS pep;
dwT = HandleToUlong(PsGetCurrentThreadId());
dwP = HandleToUlong(PsGetCurrentProcessId());
pszImage = PsGetCurrentProcessImageFileName();
if (*pszImage == '\0') {
pszImage = szSystem;
}
}
#else
{
PTEB pteb;
PPEB ppeb;
if (pteb = NtCurrentTeb()) {
dwT = HandleToUlong(pteb->ClientId.UniqueThread);
dwP = HandleToUlong(pteb->ClientId.UniqueProcess);
} else {
dwT = dwP = 0;
}
if ((ppeb = NtCurrentPeb()) && ppeb->ProcessParameters != NULL) {
/*
* Get Pointers to the image path
*/
pwszImage = ppeb->ProcessParameters->ImagePathName.Buffer;
ulLenImage = (ppeb->ProcessParameters->ImagePathName.Length) / sizeof(WCHAR);
/*
* If the ProcessParameters haven't been normalized yet, then do it.
*/
if (pwszImage != NULL && !(ppeb->ProcessParameters->Flags & RTL_USER_PROC_PARAMS_NORMALIZED)) {
pwszImage = (PWSTR)((PCHAR)(pwszImage) + (ULONG_PTR)(ppeb->ProcessParameters));
}
/*
* Munge out the path part.
*/
if (pwszImage != NULL && ulLenImage != 0) {
PWSTR pwszT = pwszImage + (ulLenImage - 1);
ULONG ulLenT = 1;
while (ulLenT != ulLenImage && *(pwszT-1) != L'\\') {
pwszT--;
ulLenT++;
}
pwszImage = pwszT;
ulLenImage = ulLenT;
}
} else {
pwszImage = (PWSTR)wszUnknown;
ulLenImage = 3;
}
}
#endif
#ifdef _USERK_
dwSession = gSessionId;
#else
{
PPEB ppeb = NtCurrentPeb();
dwSession = (ppeb != NULL ? ppeb->SessionId : 0);
}
#endif
szT[0] = 'p';
for (;;) {
switch (szT[0] | (char)0x20) {
/* print */
case 'p':
case ' ':
if (!(flags & RIP_NONAME) && (!TEST_RIPF(RIPF_HIDEPID))) {
#ifdef _USERK_
KdPrint((
"(s: %d %#lx.%lx %s) %s-[%s",
dwSession,
dwP,
dwT,
pszImage,
aszComponents[(flags & RIP_COMPBITS) >> RIP_COMPBITSSHIFT],
pszLevel));
#else
KdPrint((
"(s: %d %#lx.%lx %*ws) %s-[%s",
dwSession,
dwP,
dwT,
ulLenImage,
pwszImage,
aszComponents[(flags & RIP_COMPBITS) >> RIP_COMPBITSSHIFT],
pszLevel));
#endif
if (idErr) {
KdPrint(("=%ld] ", idErr));
} else {
KdPrint(("] "));
}
}
KdPrint(("%s", pszErr));
if (!(flags & RIP_NONEWLINE)) {
KdPrint(("\n"));
}
if (flags & RIP_THERESMORE) {
fPrompt = FALSE;
} else if (TEST_RIPF(RIPF_PRINTFILELINE) && (pexi == NULL)) {
KdPrint(("File: %s, Line: %d in function %s\n", pszFile, iLine, pszFunction));
}
break;
/* go */
case 'g':
fPrompt = FALSE;
break;
/* break */
case 'b':
KdPrint(("File: %s, Line: %d in function %s\n", pszFile, iLine, pszFunction));
fBreak = TRUE;
fPrompt = FALSE;
break;
/* display where this originated from */
case 'w':
if (pexi != NULL) {
break;
}
KdPrint(("File: %s, Line: %d in function %s\n", pszFile, iLine, pszFunction));
break;
/* dump information about this exception */
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(_IA64_)
/*
* StIIP = instruction pointer
* IntSp = stack pointer
* RsBSP = Rsestack pointer
*/
KdPrint(("StIIP = %lx\n", pexi->ContextRecord->StIIP));
KdPrint(("IntSp = %lx\n", pexi->ContextRecord->IntSp));
KdPrint(("RsBsp = %lx\n", pexi->ContextRecord->RsBSP));
#elif defined(_AMD64_)
/*
* rip = instruction pointer
* rsp = stack pointer
* rbp = stack frame pointer
*/
KdPrint(("rip = %lx\n", pexi->ContextRecord->Rip));
KdPrint(("rsp = %lx\n", pexi->ContextRecord->Rsp));
KdPrint(("rbp = %lx\n", pexi->ContextRecord->Rbp));
#else
#error "No target architecture"
#endif
break;
/* modify RIP flags */
case 'f':
{
ULONG ulFlags;
NTSTATUS status;
DWORD dwRipFlags = GetRipFlags();
szT[ARRAY_SIZE(szT) - 1] = 0; /* don't overflow buffer */
status = GetInteger(szT + 1, 16, &ulFlags, NULL);
if (NT_SUCCESS(status)) {
SetRipFlags(ulFlags);
}
KdPrint(("Flags = %.3x\n", (dwRipFlags & RIPF_VALIDUSERFLAGS)));
KdPrint((" Print Process/Component %sabled\n", (dwRipFlags & RIPF_HIDEPID) ? "dis" : "en"));
KdPrint((" Print File/Line %sabled\n", (TEST_RIPF(RIPF_PRINTFILELINE)) ? "en" : "dis"));
KdPrint((" Print on %s\n", szLevels[(dwRipFlags & RIPF_PRINT_MASK) >> RIPF_PRINT_SHIFT]));
KdPrint((" Prompt on %s\n", szLevels[(dwRipFlags & RIPF_PROMPT_MASK) >> RIPF_PROMPT_SHIFT]));
break;
}
/* modify tags */
case 't':
{
NTSTATUS status;
int tag;
LPSTR psz;
DWORD dwDBGTAGFlags;
int i;
int iStart, iEnd;
szT[ARRAY_SIZE(szT) - 1] = 0; /* don't overflow buffer */
status = GetInteger(szT + 1, 10, &tag, &psz);
if (!NT_SUCCESS(status) || tag < 0 || DBGTAG_Max - 1 < tag) {
tag = -1;
} else {
status = GetInteger(psz, 16, &dwDBGTAGFlags, NULL);
if (NT_SUCCESS(status)) {
SetDbgTag(tag, dwDBGTAGFlags);
}
}
KdPrint(("%-5s%-7s%-*s%-*s\n",
"Tag",
"Flags",
DBGTAG_NAMELENGTH,
"Name",
DBGTAG_DESCRIPTIONLENGTH,
"Description"));
for (i = 0; i < 12 + DBGTAG_NAMELENGTH + DBGTAG_DESCRIPTIONLENGTH; i++) {
szT[i] = '-';
}
szT[i++] = '\n';
szT[i] = 0;
KdPrint((szT));
if (tag != -1) {
iStart = iEnd = tag;
} else {
iStart = 0;
iEnd = DBGTAG_Max - 1;
}
for (i = iStart; i <= iEnd; i++) {
KdPrint(("%-5d%-7d%-*s%-*s\n",
i,
GetDbgTagFlags(i) & DBGTAG_VALIDUSERFLAGS,
DBGTAG_NAMELENGTH,
gadbgtag[i].achName,
DBGTAG_DESCRIPTIONLENGTH,
gadbgtag[i].achDescription));
}
break;
}
/* display help */
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"));
}
KdPrint(("p - PRINT this message again\n"));
KdPrint(("f - FLAGS, enter debug flags in format <Detail><Print><Prompt>\n"));
KdPrint((" <Detail> = [0-3] Print File/Line = 1, Hide PID/Component = 2\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"));
KdPrint(("t - TAGS, display and modify tag flags\n"));
KdPrint((" no argument displays all tags\n"));
KdPrint((" <tag> displays one tag\n"));
KdPrint((" <tag> <flags> modifies one tag\n"));
KdPrint((" <tag> = 0 - %d\n", DBGTAG_Max - 1));
KdPrint((" <flags> = [0-3] Disabled = 0, Enabled = 1, Print = 2, Prompt = 3\n"));
break;
/* prompt again on bad input */
default:
break;
}
/* Prompt the user */
if (!fPrompt) {
break;
}
if (pexi != NULL) {
DbgPrompt("[gbipft?]", szT, ARRAY_SIZE(szT));
} else {
DbgPrompt("[gbwpft?]", szT, ARRAY_SIZE(szT));
}
}
return fBreak;
}
/***************************************************************************\
* VRipOutput
*
* Formats a variable argument string and calls RipOutput.
*
* History:
* 19-Mar-1996 adams Created.
\***************************************************************************/
ULONG _cdecl VRipOutput(
ULONG idErr,
ULONG flags,
LPSTR pszFile,
int iLine,
LPSTR pszFunction,
LPSTR pszFmt,
...)
{
char szT[512];
va_list arglist;
va_start(arglist, pszFmt);
_vsnprintf(szT, ARRAY_SIZE(szT) - 1, pszFmt, arglist);
szT[ARRAY_SIZE(szT) - 1] = 0; /* ensure null termination */
va_end(arglist);
return RipOutput(idErr, flags, pszFile, iLine, pszFunction, 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.
\***************************************************************************/
ULONG RipOutput(
ULONG idErr,
ULONG flags,
LPSTR pszFile,
int iLine,
LPSTR pszFunction,
LPSTR pszErr,
PEXCEPTION_POINTERS pexi)
{
static CONST struct {
LPSTR szLevel;
DWORD dwPrint;
DWORD dwPrompt;
} aLevel[] = {
"?", 0, 0,
"Err", RIPF_PRINTONERROR, RIPF_PROMPTONERROR,
"Wrn", RIPF_PRINTONWARNING, RIPF_PROMPTONWARNING,
"Vrbs", RIPF_PRINTONVERBOSE, RIPF_PROMPTONVERBOSE,
};
int iLevel;
DebugAssertion(flags & RIP_LEVELBITS);
iLevel = ((flags & RIP_LEVELBITS) >> RIP_LEVELBITSSHIFT);
DebugAssertion(!(flags & RIP_USERTAGBITS));
return PrintAndPrompt(
TEST_RIPF(aLevel[iLevel].dwPrint),
TEST_RIPF(aLevel[iLevel].dwPrompt),
idErr,
flags,
aLevel[iLevel].szLevel,
pszFile,
iLine,
pszFunction,
pszErr,
pexi);
}
BOOL _cdecl VTagOutput(
DWORD flags,
LPSTR pszFile,
int iLine,
LPSTR pszFunction,
LPSTR pszFmt,
...)
{
char szT[512];
va_list arglist;
int tag;
DWORD dwDBGTAGFlags;
tag = (flags & RIP_USERTAGBITS);
DebugAssertion(tag < DBGTAG_Max);
DebugAssertion(!(flags & RIP_LEVELBITS));
dwDBGTAGFlags = GetDbgTagFlags(tag) & DBGTAG_VALIDUSERFLAGS;
if (dwDBGTAGFlags < DBGTAG_PRINT) {
return FALSE;
}
va_start(arglist, pszFmt);
_vsnprintf(szT, ARRAY_SIZE(szT) - 1, pszFmt, arglist);
szT[ARRAY_SIZE(szT) - 1] = 0; /* ensure null termination */
va_end(arglist);
return PrintAndPrompt(
dwDBGTAGFlags >= DBGTAG_PRINT,
dwDBGTAGFlags >= DBGTAG_PROMPT,
0,
flags,
gadbgtag[tag].achName,
pszFile,
iLine,
pszFunction,
szT,
NULL);
}
#endif
VOID UserSetLastError(
DWORD dwErrCode)
{
/*
* Check if NT Error is directly passed to UserSetLastError.
* Raid #320555 note: some Win32 error could be
* 0x4000XXXX, 0x8000XXXX or 0xC000XXXX etc.,
* but they are still valid. E.g) STATUS_SEGMENT_NOTIFICATION,
* STATUS_GUARD_PAGE_VIOLATION, etc.
*
* The mapper returns the equivalent W32 error value as NT error.
* So we assert only if mapper routine returns the different w32 error code.
*/
UserAssert((dwErrCode & 0xffff0000) == 0 || RtlNtStatusToDosError(dwErrCode) == dwErrCode);
try {
NtCurrentTeb()->LastErrorValue = dwErrCode;
} except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
}
}
VOID SetLastNtError(
NTSTATUS Status)
{
UserSetLastError(RtlNtStatusToDosError(Status));
}
#if DBG
VOID ValidateZero(
VOID)
{
static ALWAYSZERO z;
UserAssert(RtlCompareMemory(&z, (void *)&gZero, sizeof(z)) == sizeof(z));
}
#endif
/***************************************************************************\
* W32ExceptionHandler
*
* To be called from except blocks.
*
* History:
* 07-17-98 GerardoB Created.
\***************************************************************************/
ULONG _W32ExceptionHandler(
NTSTATUS ExceptionCode)
{
SetLastNtError(ExceptionCode);
return EXCEPTION_EXECUTE_HANDLER;
}
#if DBG
ULONG DBGW32ExceptionHandler(
PEXCEPTION_POINTERS pexi,
BOOL fSetLastError,
ULONG ulflags)
{
RIPMSG5(ulflags,
"Exception %#x at address %#p. flags:%#x. .exr %#p .cxr %#p",
pexi->ExceptionRecord->ExceptionCode,
CONTEXT_TO_PROGRAM_COUNTER(pexi->ContextRecord),
pexi->ExceptionRecord->ExceptionFlags,
pexi->ExceptionRecord,
pexi->ContextRecord);
if (fSetLastError) {
SetLastNtError(pexi->ExceptionRecord->ExceptionCode);
}
return EXCEPTION_EXECUTE_HANDLER;
}
#endif
#if defined(PRERELEASE) || defined(USER_INSTRUMENTATION)
/*
* UserBreakIfDebugged(): software break point that *may* also be available in FRE.
* Fre: breaks in only if there's a debugger attached.
* Chk: always breaks in.
*/
#if DBG
#define UserBreakIfDebugged DbgBreakPoint
#else
#ifdef _USERK_
#define IS_DEBUGGER_ATTACHED KD_DEBUGGER_ENABLED
#else
#define IS_DEBUGGER_ATTACHED IsDebuggerPresent()
#endif
VOID __inline UserBreakIfDebugged(VOID)
{
if (IS_DEBUGGER_ATTACHED) {
DbgBreakPoint();
}
}
#endif
/*
* Called by FRE_RIPMSGx. This is a partial implementation
* of RIPMSGx. In the future (Blackcomb?), we'll revisit
* this to have the fullest support of RIP.
*/
VOID FreDbgPrint(
ULONG flags,
LPSTR pszFile,
int iLine,
LPSTR pszFunction,
LPSTR pszFmt,
...)
{
static BOOL fSuppressFileLine;
va_list arglist;
if (!fSuppressFileLine) {
DbgPrintEx(-1, 0, "File: %s, Line: %d in function %s\n -- ", pszFile, iLine, pszFunction);
} else {
fSuppressFileLine = FALSE;
}
va_start(arglist, pszFmt);
vDbgPrintEx(-1, 0, pszFmt, arglist);
if ((flags & RIP_NONEWLINE) != 0) {
fSuppressFileLine = TRUE;
} else {
DbgPrintEx(-1, 0, "\n");
}
if ((flags & RIP_ERROR) != 0) {
UserBreakIfDebugged();
}
}
#endif