代码之家  ›  专栏  ›  技术社区  ›  pj4533

如何基于windbg扩展名中的转储文件内存创建对象?

  •  5
  • pj4533  · 技术社区  · 15 年前

    我在一个大型应用程序上工作,经常使用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++会吸引一些我不知道的VTHT?我的代码如下所示:

    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 回复  |  直到 15 年前
        1
  •  1
  •   Michael Burr    15 年前

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

    但是,您可以让一个“proxy”对象工作,当您调用代理方法时,它们会对 ReadMemory() 去获取信息。这听起来是一个相当不错的工作,我认为它应该或多或少是一个自定义的代码集,用于您想要代理的每个类。也许有更好的办法来解决这个问题,但这正是我突然想到的。

        2
  •  1
  •   pj4533    15 年前

    我最终只是按照我最初的直觉,将数据从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,必须跳过这两个vtable。

        3
  •  0
  •   Naveen    15 年前

    我知道获取内存转储一直是获取诊断信息的方法,但使用etw则容易得多,您可以获得包括信息系统调用和用户代码在内的调用堆栈信息。微软一直在为他们的所有产品做这些,包括windows和vs.net。

    这是一种非侵入式的调试方法。我做同样的调试很长时间了,现在有了ETW,我可以解决大多数客户问题,而不用在调试器中花费大量时间。这是我的两分钱。

        4
  •  0
  •   deemok    15 年前

    我在入侵windbg的gdi泄漏跟踪器扩展时也遇到了类似的问题。我在客户端使用了一个stl容器来存储数据,需要一种方法来遍历扩展中的数据。最后,我使用extremotetyped直接在扩展端实现了所需的散列映射部分,这是令人满意的,但我花了一段时间才弄明白;o) Here 是源代码。