/*++ Copyright (c) 1991 Microsoft Corporation Module Name: vrdebug.c Abstract: Contains diagnostic/debugging routines for Vdm Redir: VrDebugInit VrDiagnosticEntryPoint VrPrint VrDumpRealMode16BitRegisters VrDumpDosMemory (ConvertFlagsToString) (GetFlags) (GrabDosData) probe_parens CategoryToIndex GetRoutineDiagnosticInfo MergeInfo DoEntryDiagnostics GetRegisterOrValue VrDumpDosMemoryStructure VrPauseBreak VrErrorBreak Author: Richard L Firth (rfirth) 13-Feb-1992 Notes: This module allows us to run NTVDM.EXE and breakpoint or dump info based on an environment variable. Set the __VRDEBUG environment variable depending on the level and type of diagnostics required from VdmRedir functions. See vrdebug.h for more info This is a convenient method of dynamically changing diagnostic/debug information at run time without having to poke around in memory or start up NTVDM under NTSD (especially since NTVDM is run by the loader and not by typing NTVDM at the command prompt. Changing diagnostic settings requires that NTVDM be shut down, redefine the __VRDEBUG environment variable(s) and start a dos app. Alternatively, start the dos app in a new command session after setting the new __VRDEBUG environment variable(s) Types of things which can be affected by values in environment string: - Whether a routine breakpoints at entry (and goes into debugger) - What functions or categories to diagnose - What to dump at function entry: x86 registers DOS memory DOS stack DOS structures Note that currently the order of things at function entry are predefined: 1. Display function's name 2. Display x86 registers 3. Dump DOS stack 4. Dump DOS memory 5. Dump DOS structure(s) Currently only 1 structure 6. Break Diagnostic information is set up once at NTVDM initialisation for this session, so the environment information must have been already entered. There can be 11 __VRDEBUG environment variables: __VRDEBUG and __VRDEBUG0 through __VRDEBUG9. Each string can contain several function/group specifications of the form: [()] The group names are predefined (add a new one if a new group of functions is added to VdmRedir) and are currently: MAILSLOT, NAMEPIPE, LANMAN, NETBIOS and DLC Group names are case insensitive. Function names are case sensitive and should be entered as they appear in the C code. However, if a case sensitive search for a function name fails, a secondary case-insensitive search will be made. Diagnostic options are case insensitive and are performed in the order shown above and are the following: BREAK[()] Break into the debugger (DbgBreakPoint) when the function is entered DISPLAYNAME[()] Display the function name on entry DUMPREGS[()] Dump the x86 16-bit registers on entry to the function DUMPSTACK[()] Dump of DOS stack words (or use DEFAULT_DUMP_STACK_LENGTH) on entry to the function. This is just a special case of DUMPMEM, with type set to 'W', and segment:offset set to ss:sp. DUMPMEM[(, , , )] Dump and of DOS memory (or use DEFAULT_DUMP_MEMORY_LENGTH and DEFAULT_DUMP_MEMORY_TYPE) at : DUMPSTRUCT[( )] Dump the structure addressed by : (not necessarily segment and offset registers, can be eg. ax:flags) and descriped by INFO[()] Filter for calls to VrPrint - display INFORMATION, WARNING and ERROR messages WARN[()] Filter for calls to VrPrint - display WARNING and ERROR messages ERROR[()] Filter for calls to VrPrint - display ERROR messages only ERRORBREAK[()] Cause a break into debugger whenever BREAK_ON_ERROR/VrErrorBreak called PAUSEBREAK[()] Cause a break into debuggger whenever BREAK_ON_PAUSE/VrPauseBreak called If an option appears without parentheses then its function is turned OFF for this group or function. Whitespace between an option and following parentheses is tolerated, but should not be present. If an option takes parameters then all required parameters must be present or the option is ignored. Where an option takes parameters, commas are tolerated between parameters but are not necessary. Eg dumpmem(ds si 32 B) and DUMPMEM ( ds, si, 32, b ) and DumpMem (ds,si,32,B) are treated as the same (, is just whitespace in this u-grammar). Where register parameters are required, absolute hex values can be given. Similarly, where a number parameter is required, a 16-bit register (any register) definition can be substituted. Function specifications take precedence over group specifications. So if the same option is given for a group and a function that belongs to that group then the options specified in the function entry are used. Any options specified for the group but not negated by the function are used for the function also. When parsing the environment, if the syntax is violated, the current description is skipped until the next description starts or the end of the string is found. Eg: mailslot(break() DLC(break()) DLC(dumpstruct(bx BBBBPPBBBB)) ^ ^ 1 2 In this example, only DLC(break()) is parsed correctly, because there is a missing right parenthesis at 1 and a missing offset register specification at 2 (assumes bx is segment register, because that's the syntax) Revision History: --*/ #if DBG #include #include #include #include #include #include #include #include // ASSERT, DbgPrint #include #include #include // x86 virtual machine definitions #include #include #include "vrdebug.h" #ifdef VR_DEBUG_FLAGS DWORD VrDebugFlags = VR_DEBUG_FLAGS; #else DWORD VrDebugFlags = 0; #endif #ifdef DBGDBG #define DbgPrint printf #endif #define BYTES_DUMPED_AT_A_TIME 16 // // private prototypes // PRIVATE VOID GetRoutineDiagnosticInfo( IN LPSTR FunctionName, OUT LPDIAGNOSTIC_INFO Info ); PRIVATE DWORD CategoryToIndex( IN DWORD Category ); VOID MergeInfo( OUT LPDIAGNOSTIC_INFO Info, IN LPDIAGNOSTIC_INFO FunctionInfo, IN LPDIAGNOSTIC_INFO CategoryInfo ); VOID DoEntryDiagnostics( IN LPSTR FunctionName, IN LPDIAGNOSTIC_INFO Info ); WORD GetRegisterOrValue( IN REGVAL Union ); PRIVATE BOOL ParseString( IN LPSTR EnvStr ); PRIVATE int probe_parens( LPSTR str ); PRIVATE VOID apply_controls( IN DWORD mask, IN LPFUNCTION_DIAGNOSTIC lpFunc, IN DWORD on_controls, IN DWORD off_controls, IN LPMEMORY_INFO lpMemory, IN LPMEMORY_INFO lpStack, IN LPSTRUCT_INFO lpStructure ); PRIVATE VOID apply_diags( IN LPDIAGNOSTIC_INFO lpDiag, IN DWORD on_controls, IN DWORD off_controls, IN LPMEMORY_INFO lpMemory, IN LPMEMORY_INFO lpStack, IN LPSTRUCT_INFO lpStructure ); PRIVATE LPFUNCTION_DIAGNOSTIC FindFuncDiags( IN LPSTR function_name ); PRIVATE LPFUNCTION_DIAGNOSTIC AllocFuncDiags( IN LPSTR function_name ); TOKEN parse_token(LPTIB pTib); TOKEN peek_token(LPTIB pTib); LPSTR skip_ws(LPSTR str); LPSTR search_delim(LPSTR str); LPSTR extract_token(LPTIB pTib, LPSTR* token_stream); BOOL IsLexKeyword(LPSTR tokstr, TOKEN* pToken); BOOL IsLexRegister(LPSTR tokstr, LPREGVAL lpRegVal); BOOL IsLexNumber(LPSTR tokstr, LPREGVAL lpRegVal); WORD hex(char hexch); BOOL IsKeywordToken(TOKEN token); BOOL IsValidDumpDescriptor(char* str); BOOL IsValidStructDescriptor(char* str); PRIVATE LPSTR ConvertFlagsToString( IN WORD FlagsRegister, OUT LPSTR Buffer ); PRIVATE WORD GetFlags( VOID ); PRIVATE DWORD GrabDosData( IN LPBYTE DosMemoryPointer, IN DWORD DataSize ); // // data // // // VrDiagnosticGroups - array of GROUP_DIAGNOSTIC structures in chronological // order of implementation // GROUP_DIAGNOSTIC VrDiagnosticGroups[NUMBER_OF_VR_GROUPS] = { {ES_MAILSLOT}, // DI_MAILSLOT {ES_NAMEPIPE}, // DI_NAMEPIPE {ES_LANMAN}, // DI_LANMAN {ES_NETBIOS}, // DI_NETBIOS {ES_DLC} // DI_DLC }; REGDEF Registers[] = { "ax", AX, "bx", BX, "cx", CX, "dx", DX, "si", SI, "di", DI, "bp", BP, "sp", SP, "cs", CS, "ds", DS, "es", ES, "ss", SS, "ip", IP, "fl", FLAGS }; CONTROL Controls[] = { DC_BREAK, DM_BREAK, DC_DISPLAYNAME, DM_DISPLAYNAME, DC_DLC, 0, DC_DUMPMEM, DM_DUMPMEM, DC_DUMPREGS, DM_DUMPREGS, DC_DUMPSTACK, DM_DUMPSTACK, DC_DUMPSTRUCT, DM_DUMPSTRUCT, DC_ERROR, DM_ERROR, DC_ERRORBREAK, DM_ERRORBREAK, DC_INFO, DM_INFORMATION, DC_LANMAN, 0, DC_MAILSLOT, 0, DC_NAMEPIPE, 0, DC_NETBIOS, 0, DC_PAUSEBREAK, DM_PAUSEBREAK, DC_WARN, DM_WARNING }; #define NUMBER_OF_CONTROLS (sizeof(Controls)/Sizeof(Controls[0])) // // DiagnosticTokens - alphabetical list of all tokens we can parse from string // excluding registers, parentheses and numbers // DEBUG_TOKEN DiagnosticTokens[] = { DC_BREAK, TBREAK, DC_DISPLAYNAME, TDISPLAYNAME, DC_DLC, TDLC, DC_DUMPMEM, TDUMPMEM, DC_DUMPREGS, TDUMPREGS, DC_DUMPSTACK, TDUMPSTACK, DC_DUMPSTRUCT, TDUMPSTRUCT, DC_ERROR, TERROR, DC_ERRORBREAK, TERRORBREAK, DC_INFO, TINFO, DC_LANMAN, TLANMAN, DC_MAILSLOT, TMAILSLOT, DC_NAMEPIPE, TNAMEPIPE, DC_NETBIOS, TNETBIOS, DC_PAUSEBREAK, TPAUSEBREAK, DC_WARN, TWARN }; #define NUMBER_OF_RECOGNIZABLE_TOKENS (sizeof(DiagnosticTokens)/sizeof(DiagnosticTokens[0])) LPFUNCTION_DIAGNOSTIC FunctionList = NULL; // // routines // VOID VrDebugInit( VOID ) /*++ Routine Description: Sets up the Vdm Redir diagnostic/debugging information based on the presence of the __VRDEBUG environment variable. We actually allow 11 __VRDEBUG environment variables - __VRDEBUG and __VRDEBUG0 through __VRDEBUG9. This is to make it easy to provide a lot of diagnostic information Syntax is __VRDEBUG[0..9]=group|routine(diagnostic controls) * Where: group is MAILSLOT, NAMEPIPE, LANMAN, NETBIOS, DLC routine is name of actual routine diagnostic controls are: break break on entry to routine dumpreg dump 16-bit registers on entry to routine (before break) dumpstack(n) dump n words of DOS stack on entry to routine dumpstruct(seg, off, descriptor) Arguments: None. Return Value: None. --*/ { char envVar[sizeof(ES_VRDEBUG)+1]; // +1 for '0'..'9' LPSTR envString; DWORD i; LPSTR p; static BOOL initialized = FALSE; if (initialized) { return ; } strcpy(envVar, ES_VRDEBUG); if (envString = getenv(envVar)) { ParseString(envString); } p = strchr(envVar, 0); *p = '0'; *(p+1) = 0; for (i=0; i<10; ++i) { if (envString = getenv(envVar)) { ParseString(envString); } ++*p; } initialized = TRUE; } LPDIAGNOSTIC_INFO VrDiagnosticEntryPoint( IN LPSTR FunctionName, IN DWORD FunctionCategory, OUT LPDIAGNOSTIC_INFO Info ) /*++ Routine Description: Performs diagnostic processing on entry to routine based on what was specified in the __VRDEBUG environment variables for this routine. Tries to perform diagnostics specific to this routine. If can't find specific function diagnostic control, checks if anything was specified for this category Arguments: FunctionName - string defining procedure's name (eg VrNetUseAdd) FunctionCategory- which category this function belongs to (eg DG_LANMAN) Info - pointer to LPDIAGNOSTIC_INFO which will be returned Return Value: LPDIAGNOSTIC_INFO - pointer to structure describing diagnostic controls for THIS ROUTINE --*/ { DIAGNOSTIC_INFO function_info; LPDIAGNOSTIC_INFO category_info; RtlZeroMemory(&function_info, sizeof(function_info)); GetRoutineDiagnosticInfo(FunctionName, &function_info); if (FunctionCategory != DG_NONE && FunctionCategory != DG_ALL) { category_info = &VrDiagnosticGroups[CategoryToIndex(FunctionCategory)].Diagnostic; } else { DIAGNOSTIC_INFO null_info; RtlZeroMemory(&null_info, sizeof(null_info)); category_info = &null_info; } MergeInfo(Info, &function_info, category_info); DoEntryDiagnostics(FunctionName, Info); return Info; } PRIVATE VOID GetRoutineDiagnosticInfo( IN LPSTR FunctionName, OUT LPDIAGNOSTIC_INFO Info ) /*++ Routine Description: Tries to find the diagnostic information for this function. If found, returns the info, else a DIAGNOSTIC_INFO structure filled with 0s Arguments: FunctionName - name of function to find info for Info - pointer to place to store returned diagnostic info Return Value: None. --*/ { LPFUNCTION_DIAGNOSTIC pfunc; if (pfunc = FindFuncDiags(FunctionName)) { *Info = pfunc->Diagnostic; } else { RtlZeroMemory(Info, sizeof(DIAGNOSTIC_INFO)); } } PRIVATE DWORD CategoryToIndex( IN DWORD Category ) /*++ Routine Description: description-of-function. Arguments: Category - a DG_ category Return Value: DWORD - offset in VrDiagnosticGroups --*/ { DWORD i, bit; for (i=0, bit=1; bit; bit <<=1, ++i) { if (Category & bit) { break; } } return i; } VOID MergeInfo( OUT LPDIAGNOSTIC_INFO Info, IN LPDIAGNOSTIC_INFO FunctionInfo, IN LPDIAGNOSTIC_INFO CategoryInfo ) /*++ Routine Description: Creates one DIAGNOSTIC_INFO from one function and one category info structure Arguments: Info - place to store merged diagnostic info FunctionInfo - input function info CategoryInfo - input category info Return Value: None. --*/ { RtlZeroMemory(Info, sizeof(DIAGNOSTIC_INFO)); Info->OnControl = (FunctionInfo->OnControl | CategoryInfo->OnControl) & ~(FunctionInfo->OffControl | CategoryInfo->OffControl); Info->OffControl = 0; if (FunctionInfo->OnControl & DM_DUMPSTACK) { Info->StackInfo = FunctionInfo->StackInfo; } else if (CategoryInfo->OnControl & DM_DUMPSTACK) { Info->StackInfo = CategoryInfo->StackInfo; } if (FunctionInfo->OnControl & DM_DUMPMEM) { Info->MemoryInfo = FunctionInfo->MemoryInfo; } else if (CategoryInfo->OnControl & DM_DUMPMEM) { Info->MemoryInfo = CategoryInfo->MemoryInfo; } if (FunctionInfo->OnControl & DM_DUMPSTRUCT) { Info->StructInfo = FunctionInfo->StructInfo; } else if (CategoryInfo->OnControl & DM_DUMPSTRUCT) { Info->StructInfo = CategoryInfo->StructInfo; } } VOID DoEntryDiagnostics( IN LPSTR FunctionName, IN LPDIAGNOSTIC_INFO Info ) /*++ Routine Description: Performs diagnostics at entry to routine as per description in header Arguments: FunctionName - name of function calling VrDiagnosticEntryPoint Info - pointer to DIAGNOSTIC_INFO created in entry point Return Value: --*/ { DWORD control = Info->OnControl; if (control & DM_DISPLAYNAME) { DbgPrint("\n%s\n", FunctionName); } if (control & DM_DUMPREGS|DM_DUMPREGSDBG) { VrDumpRealMode16BitRegisters(control & DM_DUMPREGSDBG); } if (control & DM_DUMPSTACK) { VrDumpDosStack(GetRegisterOrValue(Info->StackInfo.DumpCount)); } if (control & DM_DUMPMEM) { VrDumpDosMemory(Info->MemoryInfo.DumpType, GetRegisterOrValue(Info->MemoryInfo.DumpCount), GetRegisterOrValue(Info->MemoryInfo.Segment), GetRegisterOrValue(Info->MemoryInfo.Offset) ); } if (control & DM_DUMPSTRUCT) { VrDumpDosMemoryStructure(Info->StructInfo.StructureDescriptor, GetRegisterOrValue(Info->StructInfo.Segment), GetRegisterOrValue(Info->StructInfo.Offset) ); } if (control & DM_BREAK) { DbgBreakPoint(); } } WORD GetRegisterOrValue( IN REGVAL Union ) /*++ Routine Description: Given a REGVAL, gets the current register contents described, or the returns the number in the union Arguments: Union - Return Value: WORD --*/ { if (Union.IsRegister) { switch (Union.RegOrVal.Register) { case AX: return getAX(); case BX: return getBX(); case CX: return getCX(); case DX: return getDX(); case SI: return getSI(); case DI: return getDI(); case BP: return getBP(); case SP: return getSP(); case CS: return getCS(); case DS: return getDS(); case ES: return getES(); case SS: return getSS(); case IP: return getIP(); case FLAGS: return GetFlags(); } } return Union.RegOrVal.Value; } VOID VrPauseBreak( LPDIAGNOSTIC_INFO Info ) /*++ Routine Description: Breaks into debugger if PAUSEBREAK was specified for this function/group Arguments: Info - pointer to DIAGNOSTIC_INFO for calling function Return Value: None. --*/ { if (Info) { if (Info->OnControl & DM_PAUSEBREAK) { DbgPrint("VrPauseBreak()\n"); DbgBreakPoint(); } } } VOID VrErrorBreak( LPDIAGNOSTIC_INFO Info ) /*++ Routine Description: Breaks into debugger if ERRORBREAK was specified for this function/group Arguments: Info - pointer to DIAGNOSTIC_INFO for calling function Return Value: None. --*/ { if (Info) { if (Info->OnControl & DM_ERRORBREAK) { DbgPrint("VrErrorBreak()\n"); DbgBreakPoint(); } } } VOID VrPrint( IN DWORD Level, IN LPDIAGNOSTIC_INFO Context, IN LPSTR Format, IN ... ) /*++ Routine Description: Displays diagnostic messages to standard diagnostic output, but filters depending on level of message and required level of output. Only messages that have a Level >= requested level for this routine/category are output Arguments: Level - of message to be displayed (DM_ERROR, DM_WARNING, DM_INFORMATION) Context - pointer to DIAGNOSTIC_INFO structure with info for this call Format - printf-style format string ... - additional value parameters Return Value: None. --*/ { char print_buffer[256]; // will we need more than this? va_list list; if (Context) { if (Level >= (Context->OnControl & DM_DISPLAY_MASK)) { va_start(list, Format); vsprintf(print_buffer, Format, list); va_end(list); DbgPrint(print_buffer); } } } #ifndef DBGDBG VOID VrDumpRealMode16BitRegisters( IN BOOL DebugStyle ) /*++ Routine Description: Displays (to standard diagnostic display (ie com port #)) dump of 16-bit real-mode 80286 registers - gp registers (8), segment registers (4), flags register (1) instruction pointer register (1) Arguments: DebugStyle - determines look of output: DebugStyle == TRUE: ax=1111 bx=2222 cx=3333 dx=4444 sp=5555 bp=6666 si=7777 di=8888 ds=aaaa es=bbbb ss=cccc cs=dddd ip=iiii fl fl fl fl fl fl fl fl DebugStyle == FALSE: cs:ip=cccc:iiii ss:sp=ssss:pppp bp=bbbb ax=1111 bx=2222 cx=3333 dx=4444 ds:si=dddd:ssss es:di=eeee:dddd flags[ODIxSZxAxPxC]=fl fl fl fl fl fl fl fl Return Value: None. --*/ { char flags_string[25]; if (DebugStyle) { DbgPrint( "ax=%04x bx=%04x cx=%04x dx=%04x sp=%04x bp=%04x si=%04x di=%04x\n" "ds=%04x es=%04x ss=%04x cs=%04x ip=%04x %s\n\n", getAX(), getBX(), getCX(), getDX(), getSP(), getBP(), getSI(), getDI(), getDS(), getES(), getSS(), getCS(), getIP(), ConvertFlagsToString(GetFlags(), flags_string) ); } else { DbgPrint( "cs:ip=%04x:%04x ss:sp=%04x:%04x bp=%04x ax=%04x bx=%04x cx=%04x dx=%04x\n" "ds:si=%04x:%04x es:di=%04x:%04x flags[ODITSZxAxPxC]=%s\n\n", getCS(), getIP(), getSS(), getSP(), getBP(), getAX(), getBX(), getCX(), getDX(), getDS(), getSI(), getES(), getDI(), ConvertFlagsToString(GetFlags(), flags_string) ); } } VOID VrDumpDosMemory( BYTE Type, DWORD Iterations, WORD Segment, WORD Offset ) /*++ Routine Description: Dumps DOS memory in one of following formats: Byte 0a Word 0123 Dword 01234567 Pointer 0abc:1234 Byte format also has memory dumped as ASCII, a la debug Examples: Type == 'B': 1234:5678 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f | ................ Type == 'W': 1234:5678 0100 0302 0504 0706 0908 0b0a 0d0c 0f0e Type == 'D': 1234:5678 03020100 07060504 0b0a0908 0f0e0c0d Type == 'P': 1234:5678 0302:0100 0706:0504 0b0a:0908 0f0e:0c0d Arguments: Type - of dump - 'B', 'W', 'D' or 'P' Iterations - number of TYPES of data to dump (NOT number of bytes!) Segment - WORD describing segment in DOS memory Offset - WORD describing offset in DOS segment where dump starts Return Value: None. --*/ { LPSTR dumpStr; LPBYTE pointer; DWORD size; DWORD character; switch (Type) { case 'P': dumpStr = "%04x:%04x "; size = 4; break; case 'D': dumpStr = "%08x "; size = 4; break; case 'W': dumpStr = "%04x "; size = 2; break; case 'B': default: dumpStr = "%02x "; size = 1; } pointer = LPBYTE_FROM_WORDS(Segment, Offset); while (Iterations) { DWORD i; DWORD weight = BYTES_DUMPED_AT_A_TIME / size; DWORD numDumps; DbgPrint("%04x:%04x ", Segment, Offset); numDumps = Iterations > weight ? weight : Iterations; for (i=0; i= 0x20 && character <= 0x7f) ? character : '.' ); } } DbgPrint("\n"); Iterations -= numDumps; pointer += numDumps * size; Offset += BYTES_DUMPED_AT_A_TIME; } } VOID VrDumpDosMemoryStructure( IN LPSTR Descriptor, IN WORD Segment, IN WORD Offset ) /*++ Routine Description: Dumps a structure in Dos memory Arguments: Descriptor - String describing the structure Segment - in Dos memory where structure lives Offset - in Dos memory where structure lives Return Value: None. --*/ { LPBYTE bigPointer = LPBYTE_FROM_WORDS(Segment, Offset); LPBYTE delimptr = strchr(Descriptor, SD_NAMESEP); char namebuf[MAX_ID_LEN+1]; int len; BOOL is_sign = FALSE; char* format; DWORD value; if (delimptr) { // DbgPrint("%.*s\n", (DWORD)delimptr - (DWORD)Descriptor, Descriptor); len = (DWORD)delimptr - (DWORD)Descriptor; if (len < sizeof(namebuf)) { strncpy(namebuf, Descriptor, len); namebuf[len] = 0; DbgPrint("Structure %s:\n", namebuf); Descriptor += len+1; } } while (*Descriptor) { DbgPrint("%04x:%04x ", Segment, Offset); delimptr = strchr(Descriptor, SD_DELIM); if (delimptr) { len = (DWORD)delimptr - (DWORD)Descriptor; if (len < sizeof(namebuf)) { strncpy(namebuf, Descriptor, len); namebuf[len] = 0; DbgPrint("%s ", namebuf); } Descriptor += len+1; } switch (*Descriptor) { case SD_BYTE : DbgPrint("%#02x\n", (DWORD)*bigPointer & 0xff); ++bigPointer; ++Offset; break; case SD_WORD : DbgPrint("%#04x\n", (DWORD)*(LPWORD)bigPointer & 0xffff); ++((LPWORD)bigPointer); Offset += sizeof(WORD); break; case SD_DWORD : DbgPrint("%#08x\n", (DWORD)*(LPDWORD)bigPointer); ++((LPDWORD)bigPointer); Offset += sizeof(DWORD); break; case SD_POINTER: DbgPrint("%04x:%04x\n", (DWORD)*(((LPWORD)bigPointer)+1), (DWORD)*(LPWORD)bigPointer ); ++((LPDWORD)bigPointer); Offset += 2 * sizeof(WORD); break; case SD_ASCIZ : DbgPrint("\"%s\"\n", LPSTR_FROM_WORDS(*(((LPWORD)bigPointer)+1), *(LPWORD)bigPointer) ); ++((LPDWORD)bigPointer); Offset += 2 * sizeof(WORD); break; case SD_ASCII : DbgPrint("%04x:%04x \"%s\"\n", (DWORD)*(((LPWORD)bigPointer)+1), (DWORD)*(LPWORD)bigPointer, LPSTR_FROM_WORDS(*(((LPWORD)bigPointer)+1), *(LPWORD)bigPointer) ); ++((LPDWORD)bigPointer); Offset += 2 * sizeof(WORD); break; case SD_CHAR : DbgPrint("'%c'\n", (DWORD)*bigPointer & 0xff); ++bigPointer; ++Offset; break; case SD_NUM : format = is_sign ? "%02d\n" : "%02u\n"; value = is_sign ? (long)*bigPointer : (unsigned long)*bigPointer; DbgPrint(format, value); ++bigPointer; ++Offset; is_sign = FALSE; break; case SD_INT : format = is_sign ? "%04d\n" : "%04u\n"; value = is_sign ? (long)*(LPWORD)bigPointer : (unsigned long)*(LPWORD)bigPointer; DbgPrint(format, value); ++((LPWORD)bigPointer); Offset += sizeof(WORD); is_sign = FALSE; break; case SD_LONG : format = is_sign ? "%08d\n" : "%08u\n"; value = is_sign ? (long)*(LPDWORD)bigPointer : (unsigned long)*(LPDWORD)bigPointer; DbgPrint(format, value); ++((LPDWORD)bigPointer); Offset += sizeof(DWORD); is_sign = FALSE; break; case SD_SIGNED : is_sign = TRUE; break; default: // // if we somehow got a messed up descriptor, display an error and // abort the dump // DbgPrint("VrDumpDosMemoryStructure: Invalid descriptor: '%s'\n", Descriptor); return ; } ++Descriptor; while (*Descriptor == SD_FIELDSEP) { ++Descriptor; } } } #endif // // private routines // PRIVATE BOOL ParseString( IN LPSTR EnvStr ) /*++ Routine Description: Given one of the __VRDEBUG environment strings, parse it using BFI algorithm and collect the debug/diagnostic info. The string will look something like one of the following: MAILSLOT(DisplayName, BREAK dumpregs dumpstack(32) dumpstruct(ds si "WWB4PAa")) Which tells us that for all the mailslot category Vr routines that we run, on entry, we should display the function's name, dump the 16-bit registers, dump 32 words of DOS stack, dump a structure addressed by ds:si and having the following fields: WORD WORD BYTE BYTE BYTE BYTE POINTER ASCII string POINTER+ASCII string VrNetUseEnum(BREAK INFO) Which tells us to break into the debugger whenever we run VrNetUseEnum and that any calls to VrPrint should not filter out any levels of diagnostic (as opposed to WARN which doesn't display INFO levels or ERROR which doesn't display WARN or INFO levels of diagnostic messages) Note: group category names are case INSENSITIVE but specific function names are case SENSITIVE (because C is). However, if a case-sensitive search for a function fails to find the required name, a case-insensitive search is made, just in case the name was incorrectly entered in the environment string Note also that diagnostic option keywords are case insensitive Note also also (?) that an empty function specification effectively switches off all options for that particular function. An empty group specification similarly cancels all options for that group Arguments: EnvStr - string returned from getenv. Assumed to be non-NULL Return Value: TRUE if EnvironmentString parsed OK, else FALSE. If FALSE, string may have been partially parsed --*/ { // // Syntax is: (*)* // // diagnostic options are cumulative, so if we found // // __VRDEBUG0=NAMEPIPE(DISPLAYNAME()) NAMEPIPE(BREAK()) // __VRDEBUG3=NAMEPIPE(DUMPMEM(ds si 32 B)) // // then we would have DISPLAYNAME, BREAK and DUMPMEM ds, si, 32, 'B' in the // NAMEPIPE group category entry // // If we also have // // __VRDEBUG8=VrGetNamedPipeInfo(dumpreg) // // then when VrDiagnosticEntryPoint was called for VrGetNamedPipeInfo, // we would first search for VrGetNamedPipeInfo in the FUNCTION_DIAGNOSTIC // list then search for an entry for NAMEPIPE in the GROUP_DIAGNOSTIC array. // The info is merged and diagnostics run. Note that if we have something // like // // __VRDEBUG5=VrGetNamedPipeInfo(dumpmem(ds si 32 B)) // __VRDEBUG6=NAMEPIPE(dumpmem(ds dx 80 w)) // // then dumpmem(ds si 32 B) takes precedence because we assume that the // requirement is to override the NAMEPIPE values for this particular // function // TIB tib; TOKEN current_token = TUNKNOWN, next_token; int parenthesis_depth = 0; int n; DWORD expecting = EXPECTING_NOTHING; #define EXPECTING( x ) (expecting & EXPECTING_##x) DWORD off_controls = 0; DWORD on_controls = 0; DWORD application_mask = DG_MAILSLOT | DG_NAMEPIPE | DG_LANMAN | DG_NETBIOS | DG_DLC; LPDIAGNOSTIC_INFO pInfo = NULL; LPFUNCTION_DIAGNOSTIC pFunc = NULL; MEMORY_INFO memory, stack; STRUCT_INFO structure; int parts_to_collect = 0; BOOL already_had_function = FALSE; #if DBG DbgPrint("\nParseString(\"%s\")\n", EnvStr); #endif if (n = probe_parens(EnvStr)) { DbgPrint("ParseString: Ignoring string due to unbalanced " "parentheses (%d level(s))\n", n); return FALSE; } tib.TokenStream = EnvStr; RtlZeroMemory(&memory, sizeof(memory)); RtlZeroMemory(&stack, sizeof(stack)); RtlZeroMemory(&structure, sizeof(structure)); while (1) { switch (parse_token(&tib)) { case TLEFTPAREN: #ifdef DBGDBG printf("token = TLEFTPAREN\n"); #endif if (!(expecting & EXPECTING_LEFTPAREN)) { DbgPrint("ParseString: not expecting left parenthesis\n"); return FALSE; } ++parenthesis_depth; expecting &= ~EXPECTING_LEFTPAREN; break; case TRIGHTPAREN: #ifdef DBGDBG printf("token = TRIGHTPAREN\n"); #endif if (parenthesis_depth < 0) { DbgPrint("ParseString: parenthesis level Error in %s\n", EnvStr); return FALSE; } if (!(expecting & EXPECTING_RIGHTPAREN)) { DbgPrint("ParseString: not expecting right parenthesis\n"); return FALSE; } if (parts_to_collect) { if (current_token != TDUMPSTACK) { DbgPrint("ParseString: expecting register, value or descriptor\n"); return FALSE; } else { stack.DumpCount.IsRegister = FALSE; stack.DumpCount.RegOrVal.Value = DEFAULT_STACK_DUMP; } } expecting &= EXPECTING_RIGHTPAREN; --parenthesis_depth; if (!parenthesis_depth) { apply_controls(application_mask, pFunc, on_controls, off_controls, &memory, &stack, &structure ); on_controls = 0; off_controls = 0; application_mask = DG_MAILSLOT | DG_NAMEPIPE | DG_LANMAN | DG_NETBIOS | DG_DLC; if (pFunc) { LPFUNCTION_DIAGNOSTIC listptr; if (!FunctionList) { FunctionList = pFunc; } else if (!already_had_function) { for (listptr = FunctionList; listptr->Next; listptr = listptr->Next); listptr->Next = pFunc; } pFunc = NULL; } expecting = EXPECTING_NOTHING; parts_to_collect = 0; current_token = TUNKNOWN; } break; case TREGISTER: case TNUMBER: #ifdef DBGDBG printf("token = TREGISTER/TNUMBER\n"); #endif if (!(expecting & EXPECTING_REGVAL)) { DbgPrint("ParseString: Not expecting register or value" " at this time. Got %s\n", tib.RegValOrId.Id); return FALSE; } if (current_token == TDUMPSTRUCT) { if (parts_to_collect == 3) { structure.Segment = tib.RegValOrId.RegVal; } else if (parts_to_collect == 2) { structure.Offset = tib.RegValOrId.RegVal; } if (--parts_to_collect == 1) { expecting &= ~EXPECTING_REGVAL; } } else if (current_token == TDUMPMEM) { if (parts_to_collect == 4) { memory.Segment = tib.RegValOrId.RegVal; } else if (parts_to_collect == 3) { memory.Offset = tib.RegValOrId.RegVal; } else if (parts_to_collect == 2) { memory.DumpCount = tib.RegValOrId.RegVal; } if (--parts_to_collect == 1) { expecting &= ~EXPECTING_REGVAL; } } else if (current_token == TDUMPREGS) { // // REGS(0) => debug-style // REGS(!0) => vrdebug-style // if (tib.RegValOrId.RegVal.RegOrVal.Value) { on_controls &= ~DM_DUMPREGSDBG; } else { on_controls &= ~DM_DUMPREGS; } expecting &= ~EXPECTING_REGVAL; } else { stack.DumpCount = tib.RegValOrId.RegVal; if (!--parts_to_collect) { expecting &= ~EXPECTING_REGVAL; } } break; case TEOS: #ifdef DBGDBG printf("token = TEOS\n"); #endif if (expecting == EXPECTING_NOTHING) { apply_controls(application_mask, pFunc, on_controls, off_controls, &memory, &stack, &structure ); if (pFunc) { LPFUNCTION_DIAGNOSTIC listptr; if (!FunctionList) { FunctionList = pFunc; } else if (!already_had_function) { for (listptr = FunctionList; listptr->Next; listptr = listptr->Next); listptr->Next = pFunc; } pFunc = NULL; } } else { DbgPrint("ParseString: early End-Of-String\n"); } return !expecting && !parenthesis_depth; case TUNKNOWN: #ifdef DBGDBG printf("token = TUNKNOWN\n"); #endif // // can be: MEMDESC, STRUCTDESC or function id // if (current_token == TDUMPMEM) { if (parts_to_collect != 1) { DbgPrint("ParseString: syntax error\n"); return FALSE; } if (!IsValidDumpDescriptor(_strupr(tib.RegValOrId.Id))) { DbgPrint("ParseString: Invalid memory dump descriptor:'%s'\n", tib.RegValOrId.Id); return FALSE; } memory.DumpType = (BYTE)toupper(*tib.RegValOrId.Id); --parts_to_collect; } else if (current_token == TDUMPSTRUCT) { if (parts_to_collect != 1) { DbgPrint("ParseString: syntax error\n"); return FALSE; } if (!IsValidStructDescriptor(_strupr(tib.RegValOrId.Id))) { DbgPrint("ParseString: Invalid structure dump descriptor:'%s'\n", tib.RegValOrId.Id); return FALSE; } strcpy(structure.StructureDescriptor, tib.RegValOrId.Id); _strupr(structure.StructureDescriptor); --parts_to_collect; } else { if (!(pFunc = FindFuncDiags(tib.RegValOrId.Id))) { pFunc = AllocFuncDiags(tib.RegValOrId.Id); already_had_function = FALSE; } else { already_had_function = TRUE; } if (!pFunc) { DbgPrint("ParseString: out of memory getting struct " "for %s. Aborting!\n", tib.RegValOrId.Id); return FALSE; } application_mask = 0; // no groups expecting |= EXPECTING_LEFTPAREN; } break; case TBREAK: #ifdef DBGDBG printf("token = TBREAK\n"); #endif if ((expecting != EXPECTING_NOTHING) && !(expecting & EXPECTING_RIGHTPAREN)) { DbgPrint("ParseString: syntax error\n"); return FALSE; } next_token = peek_token(&tib); if (next_token == TLEFTPAREN) { on_controls |= DM_BREAK; expecting = EXPECTING_LEFTPAREN | EXPECTING_RIGHTPAREN; } else if (next_token == TRIGHTPAREN || next_token == TEOS) { off_controls |= DM_BREAK; } else if (IsKeywordToken(next_token)) { off_controls |= DM_BREAK; expecting &= EXPECTING_RIGHTPAREN; } else { DbgPrint("ParseString: bad syntax for BREAK\n"); return FALSE; } break; case TDISPLAYNAME: #ifdef DBGDBG printf("token = TDISPLAYMEM\n"); #endif if ((expecting != EXPECTING_NOTHING) && !(expecting & EXPECTING_RIGHTPAREN)) { DbgPrint("ParseString: syntax error\n"); return FALSE; } next_token = peek_token(&tib); if (peek_token(&tib) == TLEFTPAREN) { on_controls |= DM_DISPLAYNAME; expecting = EXPECTING_LEFTPAREN | EXPECTING_RIGHTPAREN; } else if (IsKeywordToken(next_token)) { off_controls |= DM_DISPLAYNAME; expecting &= EXPECTING_RIGHTPAREN; } else { DbgPrint("ParseString: bad syntax for DISPLAYNAME\n"); return FALSE; } break; case TDLC: #ifdef DBGDBG printf("token = TDLC\n"); #endif if (expecting != EXPECTING_NOTHING) { DbgPrint("ParseString: syntax error\n"); return FALSE; } application_mask = DG_DLC; pInfo = &VrDiagnosticGroups[DI_DLC].Diagnostic; on_controls = off_controls = 0; RtlZeroMemory(&memory, sizeof(memory)); RtlZeroMemory(&stack, sizeof(stack)); RtlZeroMemory(&structure, sizeof(structure)); pFunc = NULL; if (peek_token(&tib) == TLEFTPAREN) { expecting = EXPECTING_LEFTPAREN | EXPECTING_RIGHTPAREN; } else { expecting = EXPECTING_NOTHING; } break; case TDUMPMEM: #ifdef DBGDBG printf("token = TDUMPMEM\n"); #endif if ((expecting != EXPECTING_NOTHING) && !(expecting & EXPECTING_RIGHTPAREN)) { DbgPrint("ParseString: syntax error\n"); return FALSE; } current_token = TDUMPMEM; next_token = peek_token(&tib); if (peek_token(&tib) == TLEFTPAREN) { on_controls |= DM_DUMPMEM; expecting = EXPECTING_LEFTPAREN | EXPECTING_RIGHTPAREN | EXPECTING_REGVAL; parts_to_collect = 4; } else if (IsKeywordToken(next_token)) { off_controls |= DM_DUMPMEM; expecting &= EXPECTING_RIGHTPAREN; } else { DbgPrint("ParseString: bad syntax for DUMPMEM\n"); return FALSE; } break; case TDUMPREGS: #ifdef DBGDBG printf("token = TDUMPREGS\n"); #endif if ((expecting != EXPECTING_NOTHING) && !(expecting & EXPECTING_RIGHTPAREN)) { DbgPrint("ParseString: syntax error\n"); return FALSE; } current_token = TDUMPREGS; next_token = peek_token(&tib); if (next_token == TLEFTPAREN) { on_controls |= DM_DUMPREGS|DM_DUMPREGSDBG; expecting = EXPECTING_LEFTPAREN | EXPECTING_RIGHTPAREN | EXPECTING_REGVAL; } else if (IsKeywordToken(next_token)) { off_controls |= DM_DUMPREGS; expecting &= EXPECTING_RIGHTPAREN; } else { DbgPrint("ParseString: bad syntax for DUMPREGS\n"); return FALSE; } break; case TDUMPSTACK: #ifdef DBGDBG printf("token = TDUMPSTACK\n"); #endif if ((expecting != EXPECTING_NOTHING) && !(expecting & EXPECTING_RIGHTPAREN)) { DbgPrint("ParseString: syntax error\n"); return FALSE; } current_token = TDUMPSTACK; next_token = peek_token(&tib); if (peek_token(&tib) == TLEFTPAREN) { on_controls |= DM_DUMPSTACK; expecting |= EXPECTING_LEFTPAREN | EXPECTING_RIGHTPAREN | EXPECTING_REGVAL; parts_to_collect = 1; } else if (IsKeywordToken(next_token)) { off_controls |= DM_DUMPSTACK; expecting &= EXPECTING_RIGHTPAREN; } else { DbgPrint("ParseString: bad syntax for DUMPSTACK\n"); return FALSE; } break; case TDUMPSTRUCT: #ifdef DBGDBG printf("token = TDUMPSTRUCT\n"); #endif if ((expecting != EXPECTING_NOTHING) && !(expecting & EXPECTING_RIGHTPAREN)) { DbgPrint("ParseString: syntax error\n"); return FALSE; } current_token = TDUMPSTRUCT; next_token = peek_token(&tib); if (peek_token(&tib) == TLEFTPAREN) { on_controls |= DM_DUMPSTRUCT; expecting = EXPECTING_LEFTPAREN | EXPECTING_RIGHTPAREN | EXPECTING_REGVAL; parts_to_collect = 3; } else if (IsKeywordToken(next_token)) { off_controls |= DM_DUMPSTRUCT; expecting &= EXPECTING_RIGHTPAREN; } else { DbgPrint("ParseString: bad syntax for DUMPSTRUCT\n"); return FALSE; } break; case TERROR: //aaiegh! #ifdef DBGDBG printf("token = TERROR\n"); #endif if ((expecting != EXPECTING_NOTHING) && !(expecting & EXPECTING_RIGHTPAREN)) { DbgPrint("ParseString: syntax error\n"); return FALSE; } next_token = peek_token(&tib); if (peek_token(&tib) == TLEFTPAREN) { on_controls |= DM_ERROR; expecting = EXPECTING_LEFTPAREN | EXPECTING_RIGHTPAREN; } else if (IsKeywordToken(next_token)) { off_controls |= DM_ERROR; expecting &= EXPECTING_RIGHTPAREN; } else { DbgPrint("ParseString: bad syntax for ERROR\n"); return FALSE; } break; case TERRORBREAK: //phew! But I'd rather have a TBREAK #ifdef DBGDBG printf("token = TERRORBREAK\n"); #endif if ((expecting != EXPECTING_NOTHING) && !(expecting & EXPECTING_RIGHTPAREN)) { DbgPrint("ParseString: syntax error\n"); return FALSE; } next_token = peek_token(&tib); if (peek_token(&tib) == TLEFTPAREN) { on_controls |= DM_ERRORBREAK; expecting = EXPECTING_LEFTPAREN | EXPECTING_RIGHTPAREN; } else if (IsKeywordToken(next_token)) { off_controls |= DM_ERRORBREAK; expecting &= EXPECTING_RIGHTPAREN; } else { DbgPrint("ParseString: bad syntax for ERRORBREAK\n"); return FALSE; } break; case TINFO: #ifdef DBGDBG printf("token = TINFO\n"); #endif if ((expecting != EXPECTING_NOTHING) && !(expecting & EXPECTING_RIGHTPAREN)) { DbgPrint("ParseString: syntax error\n"); return FALSE; } next_token = peek_token(&tib); if (peek_token(&tib) == TLEFTPAREN) { on_controls |= DM_INFORMATION; expecting = EXPECTING_LEFTPAREN | EXPECTING_RIGHTPAREN; } else if (IsKeywordToken(next_token)) { off_controls |= DM_INFORMATION; expecting &= EXPECTING_RIGHTPAREN; } else { DbgPrint("ParseString: bad syntax for INFO\n"); return FALSE; } break; case TLANMAN: #ifdef DBGDBG printf("token = TLANMAN\n"); #endif if (expecting != EXPECTING_NOTHING) { DbgPrint("ParseString: syntax error\n"); return FALSE; } application_mask = DG_LANMAN; pInfo = &VrDiagnosticGroups[DI_LANMAN].Diagnostic; on_controls = off_controls = 0; RtlZeroMemory(&memory, sizeof(memory)); RtlZeroMemory(&stack, sizeof(stack)); RtlZeroMemory(&structure, sizeof(structure)); pFunc = NULL; if (peek_token(&tib) == TLEFTPAREN) { expecting = EXPECTING_LEFTPAREN | EXPECTING_RIGHTPAREN; } else { expecting = EXPECTING_NOTHING; } break; case TMAILSLOT: #ifdef DBGDBG printf("token = TMAILSLOT\n"); #endif if (expecting != EXPECTING_NOTHING) { DbgPrint("ParseString: syntax error\n"); return FALSE; } application_mask = DG_MAILSLOT; pInfo = &VrDiagnosticGroups[DI_MAILSLOT].Diagnostic; on_controls = off_controls = 0; RtlZeroMemory(&memory, sizeof(memory)); RtlZeroMemory(&stack, sizeof(stack)); RtlZeroMemory(&structure, sizeof(structure)); pFunc = NULL; if (peek_token(&tib) == TLEFTPAREN) { expecting = EXPECTING_LEFTPAREN | EXPECTING_RIGHTPAREN; } else { expecting = EXPECTING_NOTHING; } break; case TNAMEPIPE: #ifdef DBGDBG printf("token = TNAMEPIPE\n"); #endif if (expecting != EXPECTING_NOTHING) { DbgPrint("ParseString: syntax error\n"); return FALSE; } application_mask = DG_NAMEPIPE; pInfo = &VrDiagnosticGroups[DI_NAMEPIPE].Diagnostic; on_controls = off_controls = 0; RtlZeroMemory(&memory, sizeof(memory)); RtlZeroMemory(&stack, sizeof(stack)); RtlZeroMemory(&structure, sizeof(structure)); pFunc = NULL; if (peek_token(&tib) == TLEFTPAREN) { expecting = EXPECTING_LEFTPAREN | EXPECTING_RIGHTPAREN; } else { expecting = EXPECTING_NOTHING; } break; case TNETBIOS: #ifdef DBGDBG printf("token = TNETBIOS\n"); #endif if (expecting != EXPECTING_NOTHING) { DbgPrint("ParseString: syntax error\n"); return FALSE; } application_mask = DG_NETBIOS; pInfo = &VrDiagnosticGroups[DI_NETBIOS].Diagnostic; on_controls = off_controls = 0; RtlZeroMemory(&memory, sizeof(memory)); RtlZeroMemory(&stack, sizeof(stack)); RtlZeroMemory(&structure, sizeof(structure)); pFunc = NULL; if (peek_token(&tib) == TLEFTPAREN) { expecting = EXPECTING_LEFTPAREN | EXPECTING_RIGHTPAREN; } else { expecting = EXPECTING_NOTHING; } break; case TPAUSEBREAK: #ifdef DBGDBG printf("token = TPAUSEBREAK\n"); #endif if ((expecting != EXPECTING_NOTHING) && !(expecting & EXPECTING_RIGHTPAREN)) { DbgPrint("ParseString: syntax error\n"); return FALSE; } next_token = peek_token(&tib); if (peek_token(&tib) == TLEFTPAREN) { on_controls |= DM_PAUSEBREAK; expecting = EXPECTING_LEFTPAREN | EXPECTING_RIGHTPAREN; } else if (IsKeywordToken(next_token)) { off_controls |= DM_PAUSEBREAK; expecting &= EXPECTING_RIGHTPAREN; } else { DbgPrint("ParseString: bad syntax for PAUSEBREAK\n"); return FALSE; } break; case TWARN: #ifdef DBGDBG printf("token = TWARN\n"); #endif if ((expecting != EXPECTING_NOTHING) && !(expecting & EXPECTING_RIGHTPAREN)) { DbgPrint("ParseString: syntax error\n"); return FALSE; } next_token = peek_token(&tib); if (peek_token(&tib) == TLEFTPAREN) { on_controls |= DM_WARNING; expecting = EXPECTING_LEFTPAREN | EXPECTING_RIGHTPAREN; } else if (IsKeywordToken(next_token)) { off_controls |= DM_WARNING; expecting &= EXPECTING_RIGHTPAREN; } else { DbgPrint("ParseString: bad syntax for WARN\n"); return FALSE; } break; } } } PRIVATE int probe_parens( LPSTR str ) /*++ Routine Description: Probes env string and returns balance of parentheses. Number of parentheses may balance, but string could still break syntax. First line of defence Arguments: str - pointer to string containing parentheses to check balance for Return Value: int number of levels by which parentheses don't balance, or 0 if they do. -ve number means more right parens than left --*/ { int balance = 0; while (*str) { if (*str == '(') { ++balance; } else if (*str == ')') { --balance; } ++str; } return balance; } PRIVATE VOID apply_controls( IN DWORD mask, IN LPFUNCTION_DIAGNOSTIC lpFunc, IN DWORD on_controls, IN DWORD off_controls, IN LPMEMORY_INFO lpMemory, IN LPMEMORY_INFO lpStack, IN LPSTRUCT_INFO lpStructure ) { DWORD bit = 1; DWORD index = 0; if (on_controls & DM_DUMPSTACK) { lpStack->Segment.IsRegister = TRUE; lpStack->Segment.RegOrVal.Register = SS; lpStack->Offset.IsRegister = TRUE; lpStack->Offset.RegOrVal.Register = SP; lpStack->DumpType = SD_WORD; } if (lpFunc) { apply_diags(&lpFunc->Diagnostic, on_controls, off_controls, lpMemory, lpStack, lpStructure ); } else { while (bit) { if (mask & bit) { apply_diags(&VrDiagnosticGroups[index].Diagnostic, on_controls, off_controls, lpMemory, lpStack, lpStructure ); } bit <<= 1; ++index; } } } PRIVATE VOID apply_diags( IN LPDIAGNOSTIC_INFO lpDiags, IN DWORD on_controls, IN DWORD off_controls, IN LPMEMORY_INFO lpMemory, IN LPMEMORY_INFO lpStack, IN LPSTRUCT_INFO lpStructure ) { lpDiags->OnControl |= on_controls; lpDiags->OffControl |= off_controls; if (on_controls & DM_DUMPMEM) { *(&(lpDiags->MemoryInfo)) = *lpMemory; } if (on_controls & DM_DUMPSTACK) { *(&(lpDiags->StackInfo)) = *lpStack; } if (on_controls & DM_DUMPSTRUCT) { *(&lpDiags->StructInfo) = *lpStructure; } } PRIVATE LPFUNCTION_DIAGNOSTIC FindFuncDiags( IN LPSTR function_name ) { LPFUNCTION_DIAGNOSTIC ptr; // // as promised: search w/ case, then w/o case // for (ptr = FunctionList; ptr; ptr = ptr->Next) { if (!strcmp(function_name, ptr->FunctionName)) { return ptr; } } for (ptr = FunctionList; ptr; ptr = ptr->Next) { if (!_stricmp(function_name, ptr->FunctionName)) { return ptr; } } return NULL; } PRIVATE LPFUNCTION_DIAGNOSTIC AllocFuncDiags( IN LPSTR function_name ) { LPFUNCTION_DIAGNOSTIC result; result = calloc(1, sizeof(FUNCTION_DIAGNOSTIC)); if (result) { strcpy(result->FunctionName, function_name); } return result; } TOKEN parse_token(LPTIB pTib) { LPSTR ptr = pTib->TokenStream; TOKEN token; ptr = skip_ws(ptr); if (!*ptr) { token = TEOS; } else if (*ptr == '(' ) { token = TLEFTPAREN; ++ptr; } else if (*ptr == ')') { token = TRIGHTPAREN; ++ptr; } else { char* tokstr; REGVAL regval; // // got some other type of token. This bit leaves ptr pointing at ws or EOS // tokstr = extract_token(pTib, &ptr); if (IsLexRegister(tokstr, ®val)) { token = TREGISTER; pTib->RegValOrId.RegVal = regval; } else if (IsLexNumber(tokstr, ®val)) { token = TNUMBER; pTib->RegValOrId.RegVal = regval; } else if (!IsLexKeyword(tokstr, &token)){ token = TUNKNOWN; } #ifdef DBGDBG printf("parse_token: token = %s\n", tokstr); #endif } pTib->TokenStream = ptr; // pointer to next token (if any) pTib->Token = token; return token; } TOKEN peek_token(LPTIB pTib) { LPSTR ptr; TOKEN token; // // gets next token type, but doesn't update TIB // ptr = skip_ws(pTib->TokenStream); if (!*ptr) { return TEOS; } else if (*ptr == '(' ) { return TLEFTPAREN; } else if (*ptr == ')') { return TRIGHTPAREN; } else { char* tokstr; REGVAL regval; tokstr = extract_token(pTib, &ptr); if (IsLexRegister(tokstr, ®val)) { return TREGISTER; } if (IsLexNumber(tokstr, ®val)) { return TNUMBER; } if (IsLexKeyword(tokstr, &token)) { return token; } } return TUNKNOWN; // don't require anything else } LPSTR skip_ws(LPSTR str) { while (*str && (*str == ' '|| *str == '\t' || *str == ',')) { ++str; } return str; } LPSTR search_delim(LPSTR str) { // strpbrk(str, " \t,()"); while (*str && !(*str == ' ' || *str == '\t' || *str == ',' || *str == '(' || *str == ')' ) ) { ++str; } return str; } LPSTR extract_token(LPTIB pTib, LPSTR* token_stream) { LPSTR ptr; DWORD len; ptr = search_delim(*token_stream); len = (DWORD)ptr - (DWORD)*token_stream; if (!len) { return NULL; } strncpy(pTib->RegValOrId.Id, *token_stream, len); pTib->RegValOrId.Id[len] = 0; *token_stream = ptr; return pTib->RegValOrId.Id; } BOOL IsLexKeyword(LPSTR tokstr, TOKEN* pToken) { int i; for (i = 0; i < NUMBER_OF_RECOGNIZABLE_TOKENS; ++i) { if (!_stricmp(tokstr, DiagnosticTokens[i].TokenString)) { *pToken = DiagnosticTokens[i].Token; return TRUE; } } return FALSE; } BOOL IsLexRegister(LPSTR tokstr, LPREGVAL lpRegVal) { int i; if (strlen(tokstr) == 2) { for (i = 0; i < NUMBER_OF_CPU_REGISTERS; ++i) { if (!_stricmp(tokstr, Registers[i].RegisterName)) { lpRegVal->IsRegister = TRUE; lpRegVal->RegOrVal.Register = Registers[i].Register; return TRUE; } } } return FALSE; } BOOL IsLexNumber(LPSTR tokstr, LPREGVAL lpRegVal) { WORD number = 0; BOOL yes = FALSE; // // go round this loop until no more hex digits. Too bad if we entered 5 // digits - number will be overflowed and results unpredictable, but its // only debug code // if (!_strnicmp(tokstr, "0x", 2)) { tokstr += 2; yes = isxdigit(*tokstr); while (isxdigit((int)*tokstr)) { number = number * (WORD)16 + hex((char)*tokstr); ++tokstr; } } else if (yes = isdigit(*tokstr)) { number = (WORD)atoi(tokstr); } if (yes) { lpRegVal->IsRegister = FALSE; lpRegVal->RegOrVal.Value = number; } return yes; } WORD hex(char hexch) { return hexch <= '9' ? (WORD)(hexch - '0') : (WORD)(toupper(hexch) - ('0' + ('A' - ('9' + 1)))); } BOOL IsKeywordToken(TOKEN token) { return token >= TBREAK && token <= TWARN; } BOOL IsValidDumpDescriptor(char* str) { static char md_chars[] = MD_CHARS; if (strlen(str) > 1) { return FALSE; } return strchr(md_chars, *str) != NULL; } BOOL IsValidStructDescriptor(char* str) { static char sd_chars[] = SD_CHARS; unsigned len = strlen(str); // return (len <= MAX_DESC_LEN) ? (strspn(str, sd_chars) == len) : FALSE; return (len <= MAX_DESC_LEN); } PRIVATE LPSTR ConvertFlagsToString( IN WORD FlagsRegister, OUT LPSTR Buffer ) /*++ Routine Description: Given a 16-bit word, interpret bit positions as for x86 Flags register and produce descriptive string of flags state (as per debug) eg: NV UP DI PL NZ NA PO NC ODItSZxAxPxC = 000000000000b OV DN EI NG ZR AC PE CY ODItSZxAxPxC = 111111111111b Trap Flag (t) is not dumped since this has no interest for programs which are not debuggers or don't examine program execution (ie virtually none) Arguments: FlagsRegister - 16-bit flags Buffer - place to store string. Requires 25 bytes inc \0 Return Value: Address of --*/ { static char* flags_states[16][2] = { //0 1 "NC", "CY", // CF (0x0001) - Carry "", "", // x (0x0002) "PO", "PE", // PF (0x0004) - Parity "", "", // x (0x0008) "NA", "AC", // AF (0x0010) - Aux (half) carry "", "", // x (0x0020) "NZ", "ZR", // ZF (0x0040) - Zero "PL", "NG", // SF (0x0080) - Sign "", "", // TF (0x0100) - Trap (not dumped) "DI", "EI", // IF (0x0200) - Interrupt "UP", "DN", // DF (0x0400) - Direction "NV", "OV", // OF (0x0800) - Overflow "", "", // x (0x1000) - (I/O Privilege Level) (not dumped) "", "", // x (0x2000) - (I/O Privilege Level) (not dumped) "", "", // x (0x4000) - (Nested Task) (not dumped) "", "" // x (0x8000) }; int i; WORD bit; BOOL on; *Buffer = 0; for (bit=0x0800, i=11; bit; bit >>= 1, --i) { on = (BOOL)((FlagsRegister & bit) == bit); if (flags_states[i][on][0]) { strcat(Buffer, flags_states[i][on]); strcat(Buffer, " "); } } return Buffer; } #ifndef DBGDBG PRIVATE WORD GetFlags( VOID ) /*++ Routine Description: Supplies the missing softpc function Arguments: None. Return Value: Conglomerates softpc flags into x86 flags word --*/ { WORD flags; flags = (WORD)getCF(); flags |= (WORD)getPF() << 2; flags |= (WORD)getAF() << 4; flags |= (WORD)getZF() << 6; flags |= (WORD)getSF() << 7; flags |= (WORD)getIF() << 9; flags |= (WORD)getDF() << 10; flags |= (WORD)getOF() << 11; return flags; } #endif PRIVATE DWORD GrabDosData( IN LPBYTE DosMemoryPointer, IN DWORD DataSize ) /*++ Routine Description: Reads one basic data element from DOS memory in a certain format (BYTE, WORD or DWORD) Arguments: DosMemoryPointer - Flat 32-bit pointer to place in DOS memory from where to read data DataSize - size (in bytes) of data to read - 1, 2 or 4 Return Value: DWORD - value read from DOS memory --*/ { switch (DataSize) { case 1: return (DWORD)*DosMemoryPointer; case 2: return (DWORD)*((LPWORD)DosMemoryPointer); case 4: return (DWORD)*((LPDWORD)DosMemoryPointer); } return 0; } #endif // DBG