void OBJECTHANDLE_EnumMemoryRegions(OBJECTHANDLE handle) { SUPPORTS_DAC; PTR_TADDR ref = PTR_TADDR(handle); if (ref.IsValid()) { ref.EnumMem(); PTR_Object obj = PTR_Object(*ref); if (obj.IsValid()) { obj->EnumMemoryRegions(); } } }
// // DacEnumCodeForStackwalk // This is a helper function to enumerate the instructions around a call site to aid heuristics // used by debugger stack walkers. // // Arguments: // taCallEnd - target address of the instruction just after the call instruction for the stack // frame we want to examine(i.e. the return address for the next frame). // // Note that this is shared by our two stackwalks during minidump generation, // code:Thread::EnumMemoryRegionsWorker and code:ClrDataAccess::EnumMemWalkStackHelper. Ideally // we'd only have one stackwalk, but we currently have two different APIs for stackwalking // (CLR StackFrameIterator and IXCLRDataStackWalk), and we must ensure that the memory needed // for either is captured in a minidump. Eventually, all clients should get moved over to the // arrowhead debugging architecture, at which time we can rip out all the IXCLRData APIs, and // so this logic could just be private to the EnumMem code for Thread. // void DacEnumCodeForStackwalk(TADDR taCallEnd) { if (taCallEnd == 0) return; // // x86 stack walkers often end up having to guess // about what's a return address on the stack. // Doing so involves looking at the code at the // possible call site and seeing if it could // reach the callee. Save enough code and around // the call site to allow this with a dump. // // For whatever reason 64-bit platforms require us to save // the instructions around the call sites on the stack as well. // Otherwise we cannnot show the stack in a minidump. // // Note that everything we do here is a heuristic that won't always work in general. // Eg., part of the 2xMAX_INSTRUCTION_LENGTH range might not be mapped (we could be // right on a page boundary). More seriously, X86 is not necessarily parsable in reverse // (eg. there could be a segment-override prefix in front of the call instruction that // we miss). So we'll dump what we can and ignore any failures. Ideally we'd better // quantify exactly what debuggers need and why, and try and avoid these ugly heuristics. // It seems like these heuristics are too tightly coupled to the implementation details // of some specific debugger stackwalking algorithm. // DacEnumMemoryRegion(taCallEnd - MAX_INSTRUCTION_LENGTH, MAX_INSTRUCTION_LENGTH * 2, false); #if defined(_TARGET_X86_) // If it was an indirect call we also need to save the data indirected through. // Note that this only handles absolute indirect calls (ModR/M byte of 0x15), all the other forms of // indirect calls are register-relative, and so we'd have to do a much more complicated decoding based // on the register context. Regardless, it seems like this is fundamentally error-prone because it's // aways possible that the call instruction was not 6 bytes long, and we could have some other instructions // that happen to match the pattern we're looking for. PTR_BYTE callCode = PTR_BYTE(taCallEnd - 6); PTR_BYTE callMrm = PTR_BYTE(taCallEnd - 5); PTR_TADDR callInd = PTR_TADDR(taCallEnd - 4); if (callCode.IsValid() && (*callCode == 0xff) && callMrm.IsValid() && (*callMrm == 0x15) && callInd.IsValid()) { DacEnumMemoryRegion(*callInd, sizeof(TADDR), false); } #endif // #ifdef _TARGET_X86_ }