DoStackSnapshot Tidbit #3: Callback CONTEXT Registers


In my initial post about DoStackSnapshot, I touched on how and when your profiler can “fill in the holes” by walking the unmanaged parts of the stack itself.  Doing this requires that your profiler have access to a register context at the top of the unmanaged block that you can use to begin your walk.  So it’s quite reasonable for you to ask, “What registers will be valid in the context I receive in my StackSnapshotCallback call?”


The quick answer is that nonvolatile (i.e., preserved), integer registers should be valid.  You don’t really need many registers to walk the stack anyway.  Obviously, you want a good stack pointer and instruction pointer.  And hey, a frame pointer is handy when you come across an EBP-based frame in x86 (RBP on x64).  These are all included in the set, of course.  Specifically by architecture, you can trust these fields in your context:


x86: Edi, Esi, Ebx, Ebp, Esp, Eip
x64: Rdi, Rsi, Rbx, Rbp, Rsp, Rip, R12:R15
ia64: IntS0:IntS3, RsBSP, StIFS, RsPFS, IntSp, StIIP, StIPSR


 


Comments (2)

  1. DaveJF says:

    Great series of posts! This is for a different type of application, but speaking of register contents… I’m wondering if there’s a ‘clean’ way of pushing the registers onto the stack with VC++ on x64 w/o having to drop into MASM64?

    For example, on x86:

    __asm pushad;

    MyStackWalker();

    __asm popad;

    I would want the information for the current (main application) thread, and from my understanding of CONTEXT the register information may not be accurate unless the thread is suspended. And I’d actually need the contents all the registers as well (volatile or not).

    Is there currently some elegant way of doing this?

    Thanks…

  2. davbr says:

    I’m not the expert on the x64 C++ compiler, but from all I’ve seen and everyone I’ve spoken with, there is no way to provide inline assembly in x64 C++ like you can with x86.  However, inline assembly is really just syntax, when it comes down to it.  As you mentioned, you can always stick your assembly into a separate file, masm it, and call those routines from inside your C++ code.

    I believe you are correct that if one thread (current thread) wants to grab context information of another thread (target thread), that target thread must be suspended to get an accurate read of the registers.  However, your scenario looks like current thread == target thread.  In other words, one thread wants to read its OWN registers.  That, of course, is perfectly do-able without suspending itself.  Assembly code looks at its registers all the time.  🙂

    So just stick your pushad into a separate asm file routine and call that from C++.  I don’t know if that counts as elegant for you, but it should work.

    And be sure to trace through the disassembly in the debugger to make sure that the compiler isn’t inserting unwanted assembly instructions on either side of your calls to the masm routines.