/****************************** 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] = { "", "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 \n")); KdPrint((" = [0-3] Print File/Line = 1, Hide PID/Component = 2\n")); KdPrint((" = [0-7] Errors = 1, Warnings = 2, Verbose = 4\n")); KdPrint((" = [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((" displays one tag\n")); KdPrint((" modifies one tag\n")); KdPrint((" = 0 - %d\n", DBGTAG_Max - 1)); KdPrint((" = [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