views:

179

answers:

4

I work on a large application, and frequently use WinDbg to diagnose issues based on a DMP file from a customer. I have written a few small extensions for WinDbg that have proved very useful for pulling bits of information out of DMP files. In my extension code I find myself dereferencing c++ class objects in the same way, over and over, by hand. For example:

Address = GetExpression("somemodule!somesymbol");
ReadMemory(Address, &addressOfPtr, sizeof(addressOfPtr), &cb);

// get the actual address
ReadMemory(addressOfObj, &addressOfObj, sizeof(addressOfObj), &cb);

ULONG offset;
ULONG addressOfField;

GetFieldOffset("somemodule!somesymbolclass", "somefield", &offset);
ReadMemory(addressOfObj+offset, &addressOfField, sizeof(addressOfField), &cb);

That works well, but as I have written more extensions, with greater functionality (and accessing more complicated objects in our applications DMP files), I have longed for a better solution. I have access to the source of our own application of course, so I figure there should be a way to copy an object out of a DMP file and use that memory to create an actual object in the debugger extension that I can call functions on (by linking in dlls from our application). This would save me the trouble of pulling things out of the DMP by hand.

Is this even possible? I tried obvious things like creating a new object in the extension, then overwriting it with a big ReadMemory directly from the DMP file. This seemed to put the data in the right fields, but freaked out when I tried to call a function. I figure I am missing something...maybe c++ pulls some vtable funky-ness that I don't know about? My code looks similar to this:

SomeClass* thisClass = SomeClass::New();
ReadMemory(addressOfObj, &(*thisClass), sizeof(*thisClass), &cb);

FOLLOWUP: It looks like POSSIBLY ExtRemoteTyped from EngExtCpp is what I want? Has anyone successfully used this? I need to google up some example code, but am not having much luck.

FOLLOWUP 2: I am pursuing two different routes of investigation on this.
1) I am looking into ExtRemoteTyped, but it appears this class is really just a helper for the ReadMemory/GetFieldOffset calls. Yes, it would help speed things up ALOT, but doesn't really help when it comes to recreating an object from a DMP file. Although documentation is slim, so I might be misunderstanding something. 2) I am also looking into trying to use ReadMemory to overwrite an object created in my extension with data from the DMP file. However, rather than using sizeof(*thisClass) as above, I was thinking I would only pick out the data elements, and leave the vtables untouched.

A: 

Interesting idea, but this would have a hope of working only on the simplest of objects. For example, if the object contains pointers or references to other objects (or vtables), those won't copy very well over to a new address space.

However, you might be able to get a 'proxy' object to work that when you call the proxy methods they make the appropriate calls to ReadMemory() to get the information. This sounds to be a fair bit of work, and I'd think it would have to be more or less a custom set of code for each class you wanted to proxy. There's probably a better way to go about this, but that's what came to me off the top of my head.

Michael Burr
Yeah, I was thinking the same thing. I kind of figured the pointers wouldn't work well, unless I followed the pointer with ReadMemory and copied that data as well. But I should still be able to call functions on the class, no? What did you mean about the vtables not copying well?
pj4533
@pj4533: you might be able to call functions on the class, but if the class contains pointers or references those functions will find a very messed-up object. You'd probably be able to get away with what you're doing with POD classes and maybe some other simple classes, but it would be pretty iffy stuff. Of course, you need to make sure that you're building the debugger extension in such a way that the object layout will be the same.
Michael Burr
@pj4533: by vtables not copying well, I meant the vtable pointer that's in any class with a virtual function. It points to what amounts to a function jump table, and the location of that table will be at different addresses in the debuggee and debugger processes.
Michael Burr
yeah I noticed that after investigating the 'dt' output a bit more for my object. I figure the problem is that the new 'real' object's vtables get over written with the vtables from the ReadMemory() call, and that prevents my object from calling any functions. I tried copying just the data, and leaving vtable pointers alone, but I haven't been successful (yet).
pj4533
I was successfully able to call functions on these objects. I did it by skipping the vtable pointers in the class, but overwritten all the other data in the class. This did allow me to call any function correctly on the object that is identical to the object from the dump file. However, as you say, the same problem arises for any other pointers. So I don't get very far in my code before I hit one of those and it blows up.
pj4533
@pj4533: I think that you'll run into a lot of fragility unless and until you put a fair bit of work into the extension, but like I said - it's a very interesting idea, and I think you might well get a lot of benefit out of it.
Michael Burr
It is a slow process, basically, I am trying to feed my dmp-file based object through one utility function. The object in question is very large, with lots of pointers. Feels kind of like a rat hole, in that I run my extension under the debugger, get an exception on a invalid dereference, fix by reading more data from dmp file, run a bit more...rinse, repeat. Works, but slow.
pj4533
A: 

I know getting memory dumps have always been the way to get information for diagnosing, but with ETW its lot more easy and you get a information along with call stacks which include information system calls and user code. MS has been doing this for all their products including Windows and VS.NET.

It is a non-intrusive way of debugging. I have done same debugging for very long and now with ETW I am able to solve most of customer issues without spending lot of time inside the debugger. These are my two cents.

Naveen
I really haven't learned much of anything about Event Tracing for Windows. It seems that it requires much code modification (addition of calls to the event tracing log api)? That really isn't possible for all the different versions of our application, not to mention the multiple legacy versions we still support that are already in the field!
pj4533
A: 

I approached something similar when hacking a gdi leak tracer extension for windbg. I used an stl container for data storage in the client and needed a way to traverse the data from the extension. I ended up implementing the parts of the hash_map I needed directly on the extension side using ExtRemoteTyped which was satisfactory but took me awhile to figure out ;o) Here is the source code.

That is interesting, but I think my real goal is to be able to pass objects from a dump file back through other utility routines, and have them treated exactly as the object types they are (ie: not a 'remote' derived type).
pj4533
A: 

I ended up just following my initial hunch, and copying over the data from the dmp file into a new object. I made this better by making remote wrapper objects like this:

class SomeClassRemote : public SomeClass
{
protected:
    SomeClassRemote (void);
    SomeClassRemote (ULONG inRemoteAddress);

public:
    static  SomeClassRemote *       New(ULONG inRemoteAddress);
    virtual ~SomeClassRemote (void);

private:

    ULONG                   m_Address;

};

And in the implementation:

SomeClassRemote::SomeClassRemote (ULONG inRemoteAddress)
{
    ULONG cb;

    m_Address = inRemoteAddress;

    // copy in all the data to the new object, skipping the virtual function tables
    ReadMemory(inRemoteAddress + 0x4, (PVOID) ((ULONG)&(*this) +0x4), sizeof(SomeClass) - 4, &cb);
}

SomeClassRemote::SomeClassRemote(void)
{
}

SomeClassRemote::~SomeClassRemote(void)
{
}

SomeClassRemote* SomeClassRemote::New(ULONG inRemoteAddress)
{
    SomeClassRemote*x = new SomeClassRemote(inRemoteAddress);

    return (x);
}

That is the basics, but then I add specific overrides in as necessary to grab more information from the dmp file. This technique allows me to pass these new remote objects back into our original source code for processing in various utility functions, cause they are derived from the original class.

It sure SEEMS like I should be able to templatize this somehow... but there always seems to be SOME reason that each class is implemented SLIGHTLY differently, for example some of our more complicated objects have a couple vtables, both of which have to be skipped.

pj4533