【问题标题】:How can I create objects based on dump file memory in a WinDbg extension?如何基于 WinDbg 扩展中的转储文件内存创建对象?
【发布时间】:2010-04-04 01:23:30
【问题描述】:

我在处理一个大型应用程序,并且经常使用 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 不变。

【问题讨论】:

    标签: c++ object windbg minidump memory-dump


    【解决方案1】:

    有趣的想法,但这有可能只适用于最简单的对象。例如,如果对象包含指向其他对象(或 vtables)的指针或引用,则它们不会很好地复制到新的地址空间。

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

    【讨论】:

    • 是的,我也在想同样的事情。我有点认为指针不能很好地工作,除非我用 ReadMemory 跟随指针并复制该数据。但是我应该仍然可以调用类上的函数,不是吗?你说的 vtables 不能很好地复制是什么意思?
    • @pj4533:你也许可以在类上调用函数,但如果类包含指针或引用,这些函数会发现一个非常混乱的对象。您可能能够摆脱使用 POD 类和其他一些简单类所做的事情,但这将是非常不确定的东西。当然,您需要确保以对象布局相同的方式构建调试器扩展。
    • @pj4533:vtables 不能很好地复制,我的意思是任何具有虚函数的类中的 vtable 指针。它指向一个函数跳转表,该表的位置将位于被调试者和调试器进程中的不同地址。
    • 是的,我注意到在调查了我的对象的“dt”输出后,我发现了更多。我认为问题在于新的“真实”对象的 vtable 被 ReadMemory() 调用中的 vtable 覆盖,这会阻止我的对象调用任何函数。我尝试只复制数据,并单独留下 vtable 指针,但我还没有成功(还)。
    • 我成功地调用了这些对象的函数。我通过跳过类中的 vtable 指针来做到这一点,但覆盖了类中的所有其他数据。这确实允许我在与转储文件中的对象相同的对象上正确调用任何函数。但是,正如您所说,任何其他指针都会出现同样的问题。因此,在我遇到其中一个并爆炸之前,我的代码并没有走得太远。
    【解决方案2】:

    我最终只是按照我最初的预感,将 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,两者都有必须跳过。

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

      • 我真的没有学到很多关于 Windows 事件跟踪的知识。似乎需要大量代码修改(添加对事件跟踪日志 api 的调用)?对于我们应用程序的所有不同版本来说,这确实是不可能的,更不用说我们仍然支持的多个旧版本已经在现场了!
      【解决方案4】:

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

      【讨论】:

      • 这很有趣,但我认为我的真正目标是能够通过其他实用程序将转储文件中的对象传回,并将它们完全视为它们的对象类型(即:不是一个“远程”派生类型)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-26
      相关资源
      最近更新 更多