5

我在一个大型应用程序上工作,并且经常使用 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 ++会带来一些我不知道的vtable funky-ness?我的代码与此类似:

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

跟进:看起来可能来自 EngExtCpp 的 ExtRemoteTyped 是我想要的?有没有人成功使用过这个?我需要谷歌搜索一些示例代码,但运气不佳。

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

4

4 回答 4

1

我最终只是按照我最初的预感,将 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 文件中获取更多信息。这种技术允许我将这些新的远程对象传回我们的原始源代码中,以便在各种实用函数中进行处理,因为它们是从原始类派生的。

它确实看起来像我应该能够以某种方式对其进行模板化......但似乎总是有一些原因每个类的实现略有不同,例如我们的一些更复杂的对象有几个 vtables,两者都必须是跳过。

于 2010-04-06T20:50:11.860 回答
1

有趣的想法,但这将有希望只在最简单的对象上工作。例如,如果对象包含指向其他对象(或 vtable)的指针或引用,则这些对象不会很好地复制到新的地址空间。

但是,您可能能够让“代理”对象工作,当您调用代理方法时,它们会进行适当的调用以ReadMemory()获取信息。这听起来是一项相当大的工作,我认为它或多或少必须是您想要代理的每个类的自定义代码集。可能有更好的方法来解决这个问题,但这就是我想到的。

于 2010-04-04T02:18:34.030 回答
0

在为 windbg 破解 gdi 泄漏跟踪器扩展时,我遇到了类似的问题。我在客户端使用了一个 stl 容器来存储数据,并且需要一种方法来遍历扩展中的数据。我最终使用 ExtRemoteTyped 在扩展端直接实现了我需要的 hash_map 部分,这很令人满意,但我花了一段时间才弄清楚;o) 是源代码。

于 2010-04-04T22:27:01.917 回答
0

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

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

于 2010-04-04T14:42:11.787 回答