2010-04-04 62 views
5

我在一个大型应用程序上工作,经常使用WinDbg来诊断基于客户的DMP文件的问题。我已经为WinDbg写了一些小扩展,这些扩展已经证明对于从DMP文件中提取信息位非常有用。在我的扩展代码中,我发现自己以相同的方式反复引用C++类对象,一次又一次地,手动。例如:如何基于WinDbg扩展中的转储文件内存创建对象?

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时髦性?我的代码看起来与此类似:

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

后续:它看起来像从EngExtCpp可能ExtRemoteTyped是我想要的吗?有没有人成功使用过这个?我需要谷歌了一些示例代码,但没有多少运气。

跟帖2:我追求这一调查的两种不同的路线。
1)我期待到ExtRemoteTyped,但现在看来这个类是真的只是为ReadMemory/GetFieldOffset调用一个帮手。是的,这将有助于加快ALOT的速度,但对于从DMP文件重新创建对象并没有什么帮助。尽管文档很渺茫,所以我可能会误解某些东西。 2)我也在考虑尝试使用ReadMemory来使用DMP文件中的数据覆盖在扩展中创建的对象。但是,我并没有像上面那样使用sizeof(* thisClass),而是认为我只会挑选出数据元素,并保持vtable不变。

+0

伟大的问题PJ! – 2010-04-04 21:52:01

回答

1

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

但是,您可能会得到一个“代理”对象来工作,当您调用代理方法时,他们会对ReadMemory()进行适当的调用以获取信息。这听起来是相当不错的工作,而且我认为对于您想要代理的每个类而言,它必须是或多或少的自定义代码集。这可能有更好的办法,但那是我头顶的东西。

+0

是的,我在想同样的事情。我认为指针不能很好地工作,除非我使用ReadMemory跟随指针并复制了这些数据。但是我仍然可以在课堂上调用函数,不是吗?你的意思是关于vtables不能很好地复制? – pj4533 2010-04-04 14:30:22

+0

@ pj4533:你可能可以在类上调用函数,但是如果类包含指针或引用,那么这些函数将会找到一个非常糟糕的对象。你可能会摆脱你在POD类和其他一些简单的类中所做的事情,但它会是非常可怕的东西。当然,您需要确保您构建调试器扩展的方式使对象布局相同。 – 2010-04-04 22:24:31

+0

@ pj4533:通过vtables不能很好地复制,我的意思是任何具有虚函数的类中的vtable指针。它指向的是什么相当于一个函数跳转表,并且该表的位置将在调试对象和调试器进程中的不同地址处。 – 2010-04-04 22:26:27

0

我知道越来越内存转储一向以获取用于诊断的方式,但它的ETW更多的方便,你得到的调用栈,其中包括信息的系统调用和用户代码沿着信息。 MS一直在为包括Windows和VS.NET在内的所有产品做到这一点。

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

+0

我真的没有学到很多关于Windows事件跟踪的知识。它似乎需要大量代码修改(添加对事件跟踪日志API的调用)?对于我们的应用程序的所有不同版本来说,这是不可能的,更不用说我们现在仍然支持的多个旧版本! – pj4533 2010-04-04 20:22:55

0

我对黑客WinDbg的一个GDI泄漏示踪扩展名,当走近类似的东西。我在客户端使用stl容器进行数据存储,并需要一种方法来遍历扩展中的数据。我最终使用ExtRemoteTyped直接在扩展端实现了需要的hash_map部分,这令人满意,但花了我一段时间才弄清楚; o) Here是源代码。

+0

这很有趣,但我认为我的真正目标是能够通过其他实用程序例程将对象从转储文件传回,并将它们作为对象类型进行处理(即:不是“远程”派生类型) 。 – pj4533 2010-04-05 20:53:21

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文件。这种技术允许我将这些新的远程对象传回原始源代码中,以便在各种实用功能中处理,因为它们是从原始类派生的。

确实像我这样的SEEMS应该能够以某种方式进行templatize ......但似乎总有某种原因,每个类的实现方式稍有不同,例如一些更复杂的对象有一对vtables,必须跳过。

相关问题