C++ 如何基于WinDbg扩展中的转储文件内存创建对象?

C++ 如何基于WinDbg扩展中的转储文件内存创建对象?,c++,object,windbg,minidump,memory-dump,C++,Object,Windbg,Minidump,Memory Dump,我在一个大型应用程序上工作,经常使用WinDbg根据客户提供的DMP文件诊断问题。我已经为WinDbg编写了一些小的扩展,这些扩展对于从DMP文件中提取一些信息非常有用。在我的扩展代码中,我发现自己以同样的方式,用手一遍又一遍地取消C++类对象。例如: Address = GetExpression("somemodule!somesymbol"); ReadMemory(Address, &addressOfPtr, sizeof(addressOfPtr), &cb); /

我在一个大型应用程序上工作,经常使用WinDbg根据客户提供的DMP文件诊断问题。我已经为WinDbg编写了一些小的扩展,这些扩展对于从DMP文件中提取一些信息非常有用。在我的扩展代码中,我发现自己以同样的方式,用手一遍又一遍地取消C++类对象。例如:

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);
这很好,但随着我编写了更多的扩展,具有更强大的功能(以及访问应用程序DMP文件中更复杂的对象),我一直渴望有一个更好的解决方案。当然,我可以访问我们自己应用程序的源代码,因此我认为应该有一种方法可以从DMP文件中复制一个对象,并使用该内存在调试器扩展中创建一个实际的对象,我可以调用该对象上的函数(通过从我们的应用程序链接DLL)。这将省去我用手从DMP中取出东西的麻烦

这可能吗?我尝试了一些显而易见的事情,比如在扩展中创建一个新对象,然后直接从DMP文件用一个大的ReadMemory覆盖它。这似乎将数据放在了正确的字段中,但当我试图调用函数时,它被吓坏了。我想我错过了什么…也许C++拉一些VTI的古怪,我不知道?我的代码与此类似:

SomeClass* thisClass = SomeClass::New();
ReadMemory(addressOfObj, &(*thisClass), sizeof(*thisClass), &cb);
后续:看起来可能是EngExtCpp的极端类型是我想要的?有人成功地使用过这个吗?我需要用谷歌搜索一些示例代码,但运气不太好

后续2:我正在对这一问题进行两种不同的调查。
1) 我正在研究ExtRemoteTyped,但似乎这个类实际上只是ReadMemory/GetFieldOffset调用的助手。是的,这将有助于大大加快速度,但在从DMP文件重新创建对象时,这并没有真正的帮助。虽然文档很薄,所以我可能会误解一些东西。
2) 我还打算尝试使用ReadMemory用DMP文件中的数据覆盖在扩展名中创建的对象。然而,我并没有像上面那样使用sizeof(*thisklass),而是认为我应该只选择数据元素,而保持vtables不变

这是一个有趣的想法,但它有望只处理最简单的对象。例如,如果对象包含指向其他对象(或vtables)的指针或引用,则这些对象不会很好地复制到新的地址空间


但是,您可以让“proxy”对象工作,当您调用proxy方法时,它们会适当地调用
ReadMemory()
以获取信息。这听起来是一项相当艰巨的工作,我认为它或多或少需要为您想要代理的每个类定制一组代码。可能有更好的方法来实现这一点,但这正是我脑海中浮现出来的。

我知道获取内存转储一直是获取诊断信息的方法,但使用ETW更容易,您可以获得包括信息系统调用和用户代码在内的调用堆栈信息。微软一直在为他们的所有产品(包括Windows和VS.NET)这样做


这是一种非侵入式的调试方式。我已经做了很长时间的相同调试,现在使用ETW,我能够解决大多数客户问题,而无需在调试器中花费大量时间。这是我的两分钱。

我在为windbg破解gdi泄漏追踪程序扩展时也做了类似的事情。我在客户机中使用了一个stl容器来存储数据,需要一种方法来遍历扩展中的数据。我最终使用ExtRemoteTyped直接在扩展端实现了我需要的hash_映射的部分,这很令人满意,但我花了一段时间才弄清楚;o)
是源代码。

我只是按照我最初的直觉,将数据从dmp文件复制到一个新对象中。我通过如下方式制作远程包装器对象使这一点变得更好:

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

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

private:

    ULONG                   m_Address;

};
在实施过程中:

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);
}
这是最基本的,但是我会根据需要添加特定的覆盖,以便从dmp文件中获取更多信息。这种技术允许我将这些新的远程对象传递回原始源代码,以便在各种实用程序函数中进行处理,因为它们是从原始类派生的


看起来我真的应该能够以某种方式将其模板化。。。但似乎总是有一些原因导致每个类的实现略有不同,例如,我们的一些更复杂的对象有两个vtable,这两个都必须跳过。

是的,我的想法是相同的。我认为指针不会很好地工作,除非我用ReadMemory跟随指针并复制数据。但是我应该仍然能够调用类上的函数,不是吗?vtables复制不好是什么意思?@pj4533:您可能可以调用类上的函数,但如果类包含指针或引用,这些函数将发现一个非常混乱的对象。你可能可以通过POD类或者其他一些简单的类逃脱惩罚,但是这是非常不确定的事情。当然,您需要确保构建调试器扩展时,对象布局是相同的。@pj4533:所谓vtables没有很好地复制,我指的是任何具有虚拟函数的类中的vtable指针。它指向相当于一个函数跳转表的内容,该表的位置将位于调试对象和调试器进程中的不同地址。是的,我注意到在为我的对象进一步研究“dt”输出之后。我认为问题在于新的“real”对象的vtables被ReadMemory()调用中的vtables覆盖,这阻止了我的对象调用任何函数。我试着只复制数据,不使用vtable指针,但没有成功