/******************************Module*Header*******************************\ * Module Name: dumpers.cxx * * Copyright (c) 2000 Microsoft Corporation * \**************************************************************************/ #include "precomp.hxx" // A resonable count limit for scans and walls #define COUNT_LIMIT 0x07ffffff /**************************************************************************\ * * class ScanDumper * \**************************************************************************/ const FIELD_INFO ScanFieldsInit[SCAN_FIELDS_LENGTH] = { { DbgStr("cWalls"), NULL, 0, DBG_DUMP_FIELD_FULL_NAME, 0, NULL }, { DbgStr("yTop"), NULL, 0, DBG_DUMP_FIELD_FULL_NAME, 0, NULL }, { DbgStr("yBottom"), NULL, 0, DBG_DUMP_FIELD_FULL_NAME, 0, NULL }, { DbgStr("ai_x[0]"), NULL, 0, DBG_DUMP_FIELD_FULL_NAME | DBG_DUMP_FIELD_RETURN_ADDRESS, 0, NULL }, }; const SYM_DUMP_PARAM ScanSymInit = { sizeof (SYM_DUMP_PARAM), DbgStr(GDIType(SCAN)), DBG_DUMP_NO_PRINT, 0, NULL, NULL, NULL, SCAN_FIELDS_LENGTH, NULL }; const FIELD_INFO WallFieldsInit[1] = { { DbgStr("x"), NULL, 0, DBG_DUMP_FIELD_FULL_NAME, 0, LeftWallCallback }, }; const SYM_DUMP_PARAM WallSymInit = { sizeof (SYM_DUMP_PARAM), DbgStr(GDIType(_INDEX_LONG)), DBG_DUMP_NO_PRINT | DBG_DUMP_ARRAY, 0, NULL, NULL, NULL, 1, NULL }; const SYM_DUMP_PARAM cWalls2SymInit = { sizeof (SYM_DUMP_PARAM), DbgStr("ULONG"), DBG_DUMP_NO_PRINT | DBG_DUMP_ADDRESS_AT_END | DBG_DUMP_COPY_TYPE_DATA, 0, NULL, NULL, NULL, 0, NULL }; /**************************************************************************\ * * ScanDumper::ScanDumper * \**************************************************************************/ ScanDumper::ScanDumper( ULONG64 HeadScanAddr, ULONG64 TailScanAddr, ULONG ScanCount, ULONG64 AllocationBase, ULONG64 AllocationLimit, ULONG Flags ) { block = 0; Top = POS_INFINITY; Bottom = NEG_INFINITY; FirstScanAddr = HeadScanAddr; LastScanAddr = TailScanAddr; ScanLimit = ScanCount ? ScanCount : COUNT_LIMIT; AddressBase = AllocationBase; AddressLimit = AllocationLimit; PrintBlocks = (Flags & SCAN_DUMPER_NO_PRINT) == 0; ForceDump = (Flags & SCAN_DUMPER_FORCE) != 0; Reverse = (Flags & SCAN_DUMPER_FROM_TAIL) != 0; // Enable to print a . after each right wall is // processed when block printing is disabled. PrintProgress = FALSE; ProgressCount = 0; RtlCopyMemory(ScanFields, ScanFieldsInit, sizeof(ScanFields)); RtlCopyMemory(&ScanSym, &ScanSymInit, sizeof(ScanSym)); ScanSym.Fields = ScanFields; ListWallSize.fOptions = 0; ListWallSize.fieldCallBack = WallArrayEntryCallback; RtlCopyMemory(WallFields, WallFieldsInit, sizeof(WallFields)); RtlCopyMemory(&WallSym, &WallSymInit, sizeof(WallSym)); WallSym.listLink = &ListWallSize; WallSym.Fields = WallFields; RtlCopyMemory(&cWalls2Sym, &cWalls2SymInit, sizeof(cWalls2Sym)); Valid = TRUE; // Get basic scan structure information ScanSize = GetTypeSize(GDIType(SCAN)); if (1) { cWallsSize = GetTypeSize("ULONG"); IXSize = GetTypeSize(GDIType(_INDEX_LONG)); } else { ULONG error; error = Ioctl(IG_DUMP_SYMBOL_INFO, &ScanSym, ScanSym.size); if (error) { dprintf(" SCAN type info Ioctl returned %s\n", pszWinDbgError(error)); } cWallsSize = ScanFields[SCAN_CWALLS].size; IXSize = ScanFields[SCAN_AI_X_ADDR].size; } if (Reverse && cWallsSize != GetTypeSize("ULONG")) { dprintf(" * Error: sizeof(SCAN::cWalls) != sizeof(ULONG)\n" " => From tail scan dumping won't work.\n"); Reverse = FALSE; } if (Reverse) { scan = ScanCount; NegativeScanCount = (ScanCount == 0); ScanAddr = TailScanAddr; } else { scan = -1; NegativeScanCount = FALSE; ScanAddr = HeadScanAddr; } // Will DumpScans work? CanDump = (ScanSize != 0) && (cWallsSize != 0) && (IXSize != 0); } /**************************************************************************\ * * ScanDumper::DumpScans * \**************************************************************************/ BOOL ScanDumper::DumpScans( ULONG Count ) { ULONG64 NextScanAddr; ULONG cWalls1, cWalls2; ULONG error; if (!CanDump) { dprintf(" An error occured in this extension preventing DumpScans from working.\n"); return FALSE; } if (! Count && PrintBlocks) { dprintf("\tNo scans to dump.\n"); } if (Count > 100) { PrintProgress = TRUE; ProgressCount = 0; } while (Count-- && !CheckControlC()) { cWalls1 = cWalls2 = 0; if (Reverse) { if (!NT_SUCCESS(ValidateAddress(ScanAddr, "End of ScanAddr", SCAN_VALID_AT_END))) { if (!ForceDump) break; } cWalls2Sym.addr = ScanAddr; cWalls2Sym.Context = &cWalls2; error = Ioctl(IG_DUMP_SYMBOL_INFO, &cWalls2Sym, cWalls2Sym.size); if (error) { dprintf(" Ioctl returned %s\n", pszWinDbgError(error)); break; } ScanAddr -= cWalls2 * IXSize + ScanSize; } if (gbVerbose) { dprintf(" Examining scan %d @ %#p\n", scan+(Reverse?-1:+1), ScanAddr); } if (!NT_SUCCESS(ValidateAddress(ScanAddr, "ScanAddr", SCAN_VALID_AT_START))) { if (!ForceDump) break; } ScanSym.addr = ScanAddr; error = Ioctl(IG_DUMP_SYMBOL_INFO, &ScanSym, ScanSym.size); if (error) { dprintf(" Ioctl returned %s\n", pszWinDbgError(error)); break; } cWalls1 = (ULONG)ScanFields[SCAN_CWALLS].address; // Update Top and Bottom information if (!ScanAdvance((LONG)ScanFields[SCAN_YTOP].address, (LONG)ScanFields[SCAN_YBOTTOM].address, Reverse ? cWalls2 : cWalls1)) { if (!ForceDump) break; } // Read cWalls2 if we don't know it. if (!Reverse) { NextScanAddr = ScanAddr + ScanSize + cWalls1 * IXSize; if (!NT_SUCCESS(ValidateAddress(NextScanAddr, "End of Wall array", SCAN_VALID_AT_END | SCAN_VALID_NO_ERROR_PRINT))) { dprintf(" * End of Wall array @ %#p, length %u, at scan %d lies outside valid scan range\n", NextScanAddr, cWalls1, scan); if (!ForceDump) break; } cWalls2Sym.addr = NextScanAddr; cWalls2Sym.Context = &cWalls2; error = Ioctl(IG_DUMP_SYMBOL_INFO, &cWalls2Sym, cWalls2Sym.size); if (error) { dprintf(" Ioctl returned %s\n", pszWinDbgError(error)); break; } } // Check cWalls against cWalls2 if (cWalls1 != cWalls2) { dprintf(" * cWalls (%u) != cWalls2 (%u) at scan %d\n", cWalls1, cWalls2, scan); Valid = FALSE; if (!ForceDump) break; } // cWalls is set by ScanAdvance if (cWalls != 0) { WallSym.Context = (PVOID)this; WallSym.addr = ScanFields[SCAN_AI_X_ADDR].address; ListWallSize.size = cWalls; WallFields[0].fieldCallBack = LeftWallCallback; error = Ioctl(IG_DUMP_SYMBOL_INFO, &WallSym, WallSym.size); if (error) { dprintf(" Ioctl returned %s\n", pszWinDbgError(error)); break; } if (!Valid && !ForceDump) break; } if (!Reverse) { if (!NT_SUCCESS(ValidateAddress(NextScanAddr, "Next ScanAddr", SCAN_VALID_AT_END))) { if (!ForceDump) break; } ScanAddr = NextScanAddr; } } return Valid; } /**************************************************************************\ * * ScanDumper::NextScan * \**************************************************************************/ BOOL ScanDumper::NextScan( LONG NextTop, LONG NextBottom, ULONG NumWalls ) { LONG PrevBottom = Bottom; BOOL bRet = TRUE; scan++; if (scan < 0 || (ULONG)scan >= ScanLimit) { dprintf(" * Scan count %d is not in range 1 to %u\n", scan+1, ScanLimit); Valid = FALSE; bRet = FALSE; } wall = 0; PrevRight = NEG_INFINITY; cWalls = NumWalls; Top = NextTop; Bottom = NextBottom; if ((bRet || ForceDump) && Top < PrevBottom) { dprintf(" * Scan %d top (%d) < prev bottom (%d)\n", scan, Top, PrevBottom); Valid = FALSE; bRet = FALSE; } if (gbVerbose) { dprintf(" * Scan %d covers from %d to %d and has %u walls.\n", scan, Top, Bottom, cWalls); } if ((bRet || ForceDump) && Bottom < Top) { dprintf(" * Scan %d bottom (%d) < top (%d)\n", scan, Bottom, Top); Valid = FALSE; bRet = FALSE; } if ((bRet || ForceDump) && cWalls > COUNT_LIMIT) { dprintf(" * cWalls (%u) is suspiciously long at scan %d\n", cWalls, scan); Valid = FALSE; bRet = FALSE; } if ((bRet || ForceDump) && cWalls == 0 && PrintBlocks) { dprintf("\tNULL Scans from %d to %d.\n", Top, Bottom); } return bRet; } /**************************************************************************\ * * ScanDumper::PrevScan * \**************************************************************************/ BOOL ScanDumper::PrevScan( LONG PrevTop, LONG PrevBottom, ULONG NumWalls ) { LONG NextTop = Top; BOOL bRet = TRUE; scan--; if (NegativeScanCount) { if (-scan < 1 || (ULONG)-scan > ScanLimit) { dprintf(" * Scan count %u is not in range 1 to %u\n", -scan, ScanLimit); Valid = FALSE; bRet = FALSE; } } else { if (scan < 0 || (ULONG)scan >= ScanLimit) { dprintf(" * Scan count %u is not in range 1 to %u\n", scan+1, ScanLimit); Valid = FALSE; bRet = FALSE; } } wall = 0; PrevRight = NEG_INFINITY; cWalls = NumWalls; Top = PrevTop; Bottom = PrevBottom; if ((bRet || ForceDump) && Bottom > NextTop) { dprintf(" * Scan %d bottom (%d) > next top (%d)\n", scan, Bottom, NextTop); Valid = FALSE; bRet = FALSE; } if (gbVerbose) { dprintf(" * Scan %d covers from %d to %d and has %u walls.\n", scan, Top, Bottom, cWalls); } if ((bRet || ForceDump) && Bottom < Top) { dprintf(" * Scan %d bottom (%d) < top (%d)\n", scan, Bottom, Top); Valid = FALSE; bRet = FALSE; } if ((bRet || ForceDump) && cWalls > COUNT_LIMIT) { dprintf(" * cWalls (%u) is suspiciously long at scan %d\n", cWalls, scan); Valid = FALSE; bRet = FALSE; } if ((bRet || ForceDump) && cWalls == 0 && PrintBlocks) { dprintf("\tNULL Scans from %d to %d.\n", Top, Bottom); } return bRet; } /**************************************************************************\ * * ScanDumper::NextLeftWall * \**************************************************************************/ ULONG ScanDumper::NextLeftWall( LONG NextLeft ) { ULONG Status = STATUS_SUCCESS; if (wall > cWalls) { dprintf(" * More walls (%u) than expected (%u).\n", wall, cWalls); Valid = FALSE; return STATUS_UNSUCCESSFUL; } Left = NextLeft; if (Left <= PrevRight) { dprintf(" * Left wall %d (%d) <= previous right (%d) @ scan %d\n", wall, Left, PrevRight, scan); Valid = FALSE; Status = STATUS_UNSUCCESSFUL; } wall++; return Status; } /**************************************************************************\ * * ScanDumper::NextRightWall * \**************************************************************************/ ULONG ScanDumper::NextRightWall( LONG Right ) { ULONG Status = STATUS_SUCCESS; if (wall > cWalls) { dprintf(" * More walls (%u) than expected (%u).\n", wall, cWalls); Valid = FALSE; return STATUS_UNSUCCESSFUL; } if (Right <= Left) { dprintf(" * Right wall %d (%d) <= left (%d) @ scan %d\n", wall, Right, Left, scan); Valid = FALSE; Status = STATUS_UNSUCCESSFUL; } if (PrintBlocks) { dprintf("\tRectangle #%d (%d,%d) - (%d,%d)\n", block, Left, Top, Right, Bottom); } else if (PrintProgress) { dprintf("."); if ((ProgressCount++ % 60) == 0) { dprintf("\n"); } } wall++; block++; PrevRight = Right; return Status; } /**************************************************************************\ * * ScanDumper::ValidateAddress * \**************************************************************************/ ULONG ScanDumper::ValidateAddress( ULONG64 Address, const char *pszAddrName, ULONG Flags ) { if (pszAddrName == NULL) { pszAddrName = "address"; } if (gbVerbose) { dprintf(" Validating %s %#p\n", pszAddrName, Address); } // Check allocation limits if (Address < AddressBase) { if ((Flags & SCAN_VALID_NO_ERROR_PRINT) == 0) dprintf(" * %s %#p is before address base %#p\n", pszAddrName, Address, AddressBase); Valid = FALSE; return STATUS_UNSUCCESSFUL; } if (Address > AddressLimit) { if ((Flags & SCAN_VALID_NO_ERROR_PRINT) == 0) dprintf(" * %s %#p is beyond address limit %#p\n", pszAddrName, Address, AddressLimit); Valid = FALSE; return STATUS_UNSUCCESSFUL; } // Check head and tail limits if (Address < FirstScanAddr) { if ((Flags & SCAN_VALID_NO_ERROR_PRINT) == 0) dprintf(" * %s %#p is before head address %#p\n", pszAddrName, Address, FirstScanAddr); Valid = FALSE; return STATUS_UNSUCCESSFUL; } if (Address > LastScanAddr) { if ((Flags & SCAN_VALID_NO_ERROR_PRINT) == 0) dprintf(" * %s %#p beyond tail address %#p\n", pszAddrName, Address, LastScanAddr); Valid = FALSE; return STATUS_UNSUCCESSFUL; } // Check end edge limits if (! (Flags & SCAN_VALID_AT_END)) { if (Address == AddressLimit) { if ((Flags & SCAN_VALID_NO_ERROR_PRINT) == 0) dprintf(" * %s %#p is at address limit %#p\n", pszAddrName, Address, AddressLimit); Valid = FALSE; return STATUS_UNSUCCESSFUL; } if (Address == LastScanAddr) { if ((Flags & SCAN_VALID_NO_ERROR_PRINT) == 0) dprintf(" * %s %#p is at tail address %#p\n", pszAddrName, Address, LastScanAddr); Valid = FALSE; return STATUS_UNSUCCESSFUL; } } // Check start edge limits if (! (Flags & SCAN_VALID_AT_START)) { if (Address == AddressBase) { if ((Flags & SCAN_VALID_NO_ERROR_PRINT) == 0) dprintf(" * %s %#p is at address base %#p\n", pszAddrName, Address, AddressBase); Valid = FALSE; return STATUS_UNSUCCESSFUL; } if (Address == FirstScanAddr) { if ((Flags & SCAN_VALID_NO_ERROR_PRINT) == 0) dprintf(" * %s %#p is at head address %#p\n", pszAddrName, Address, FirstScanAddr); Valid = FALSE; return STATUS_UNSUCCESSFUL; } } return STATUS_SUCCESS; } /**************************************************************************\ * * ScanDumper callback interfaces * \**************************************************************************/ ULONG WallArrayEntryCallback( PFIELD_INFO pField, ScanDumper *pScanDumper ) { if (pScanDumper == NULL || pField == NULL) { return STATUS_UNSUCCESSFUL; } return pScanDumper->ValidateAddress(pField->address, "Wall @", SCAN_VALID_EXCLUSIVE); } ULONG LeftWallCallback( PFIELD_INFO pField, ScanDumper *pScanDumper ) { if (pField == NULL) { dprintf(" * Error: LeftWallCallback was given NULL pField.\n"); return STATUS_UNSUCCESSFUL; } pField->fieldCallBack = RightWallCallback; if (pScanDumper == NULL) { dprintf(" * Error: LeftWallCallback was given NULL pScanDumper.\n"); return STATUS_UNSUCCESSFUL; } return pScanDumper->NextLeftWall((LONG)pField->address); } ULONG RightWallCallback( PFIELD_INFO pField, ScanDumper *pScanDumper ) { if (pField == NULL) { dprintf(" * Error: RightWallCallback was given NULL pField.\n"); return STATUS_UNSUCCESSFUL; } pField->fieldCallBack = LeftWallCallback; if (pScanDumper == NULL) { dprintf(" * Error: RightWallCallback was given NULL pScanDumper.\n"); return STATUS_UNSUCCESSFUL; } return pScanDumper->NextRightWall((LONG)pField->address); } template PrintfTypeFormat::PrintfTypeFormat() : PrintfFormat() { ComposeFormat(); } template void PrintfTypeFormat::ComposeFormat() { int pos = 0; Format[pos++] = '%'; if (Width != -1) { int stored = _snprintf(&Format[pos], sizeof(Format)-5-pos, "%d", Width); if (stored > 0) { pos += stored; } } if (Spec & (PRINT_FORMAT_CHARACTER | PRINT_FORMAT_STRING)) { if (Spec & PRINT_FORMAT_1BYTE) { Format[pos++] = 'h'; } else if (Spec & PRINT_FORMAT_2BYTES) { Format[pos++] = 'l'; } else { ExtWarn("Warning: Unknown character print size specification.\n"); } if (Spec & PRINT_FORMAT_CHARACTER) { Format[pos++] = 'c'; } else { Format[pos++] = 's'; } } else if (Spec & PRINT_FORMAT_POINTER) { Format[pos++] = 'p'; } else { if (Spec & PRINT_FORMAT_1BYTE) { } else if (Spec & PRINT_FORMAT_2BYTES) { Format[pos++] = 'h'; } else if (Spec & PRINT_FORMAT_4BYTES) { Format[pos++] = 'l'; } else if (Spec & PRINT_FORMAT_8BYTES) { Format[pos++] = 'I'; Format[pos++] = '6'; Format[pos++] = '4'; } else { ExtWarn("Warning: Unknown print format size specification.\n"); } if (Spec & PRINT_FORMAT_HEX) { Format[pos++] = 'x'; } else if (Spec & PRINT_FORMAT_SIGNED) { Format[pos++] = 'd'; } else if (Spec & PRINT_FORMAT_UNSIGNED) { Format[pos++] = 'u'; } else { ExtErr("Error: Unknown print format specification.\n"); Format[0] = 0; return; } } Format[pos] = 0; IsDirty = FALSE; } template BOOL ArrayDumper::ReadArray( const char * SymbolName ) { DEBUG_VALUE Addr; ULONG error; ULONG ArraySize; ULONG ArrayLength; ULONG EntrySize; DbgPrint("ReadArray called for %s\n", SymbolName); Length = 0; if (!GetArrayDimensions(Client, SymbolName, NULL, &ArraySize, &ArrayLength, &EntrySize) || !(ArraySize > 0 && ArrayLength > 0 && EntrySize > 0) ) { ExtErr("GetArrayDimensions failed or returned a zero value dimension for\n\t%s.\n", SymbolName); ExtVerb("ArraySize: %u ArrayLength: %u EntrySize: %u.\n", ArraySize, ArrayLength, EntrySize); return FALSE; } if (EntrySize != sizeof(T)) { ExtErr("Error: %s has entries of size %u not %u as expected.\n", SymbolName, EntrySize, sizeof(T)); return FALSE; } if (S_OK == g_pExtControl->Evaluate(SymbolName, DEBUG_VALUE_INT64, &Addr, NULL)) { if (Addr.I64 != 0) { HANDLE hHeap = GetProcessHeap(); if (ArrayBuffer == NULL || ArrayLength > HeapSize(hHeap, 0, ArrayBuffer)) { T *NewBuffer; NewBuffer = (T *) ((ArrayBuffer == NULL) ? HeapAlloc(hHeap, 0, ArraySize): HeapReAlloc(hHeap, 0, ArrayBuffer, ArraySize)); if (NewBuffer == NULL) { ExtErr("Buffer alloc failed.\n"); return FALSE; } ArrayBuffer = NewBuffer; } if (S_OK == g_pExtData->ReadVirtual(Addr.I64, ArrayBuffer, ArraySize, NULL)) { Length = ArrayLength; return TRUE; } ExtErr("ReadMemory at %p for %u bytes failed.\n", Addr.I64, ArrayLength); } else { ExtWarn("Symbol %s evaluated to zero.\n", SymbolName); } } else { ExtErr("Couldn't evalutate: %s\n", SymbolName); } return FALSE; } template class PrintfTypeFormat; template class PrintfTypeFormat; template class PrintfTypeFormat; template class PrintfTypeFormat; template class PrintfTypeFormat; template class PrintfTypeFormat; template class PrintfTypeFormat; template class PrintfTypeFormat; template class PrintfTypeFormat; template class PrintfTypeFormat; template class PrintfTypeFormat; template class PrintfTypeFormat; template class PrintfTypeFormat; template class PrintfTypeFormat; template class PrintfTypeFormat; template class ArrayDumper;