Как я могу создать объекты, основанные на памяти файла дампа в расширении WinDbg? - PullRequest
5 голосов
/ 04 апреля 2010

Я работаю с большим приложением и часто использую 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 вручную.

Это вообще возможно? Я попробовал очевидные вещи, такие как создание нового объекта в расширении, а затем перезаписать его большой ReadMemory прямо из файла DMP. Это, казалось, поместило данные в правильные поля, но взбесилось, когда я попытался вызвать функцию. Я полагаю, что я что-то упускаю ... может быть, C ++ тянет некоторые vtable фанки, о которых я не знаю? Мой код выглядит примерно так:

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

FOLLOWUP: похоже, что возможно ExtRemoteTyped из EngExtCpp - это то, что я хочу? Кто-нибудь успешно использовал это? Мне нужно набрать пример кода, но мне не везет.

СЛЕДУЮЩАЯ 2: Я преследую два разных пути расследования этого вопроса.
1) Я смотрю в ExtRemoteTyped, но, похоже, этот класс действительно просто помощник для вызовов ReadMemory / GetFieldOffset. Да, это помогло бы ускорить процесс ALOT, но на самом деле не помогает, когда дело доходит до воссоздания объекта из файла DMP. Хотя документация скудная, я могу что-то неправильно понять. 2) Я также пытаюсь использовать ReadMemory для перезаписи объекта, созданного в моем расширении, данными из файла DMP. Однако вместо того, чтобы использовать sizeof (* thisClass), как описано выше, я подумал, что я бы выбрал только элементы данных и оставил vtables без изменений.

Ответы [ 4 ]

1 голос
/ 07 апреля 2010

В итоге я просто следовал своему первоначальному предположению и копировал данные из файла 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, оба из которых должны быть пропущены.

1 голос
/ 04 апреля 2010

Интересная идея, но есть надежда работать только над простейшими объектами.Например, если объект содержит указатели или ссылки на другие объекты (или vtables), они не будут очень хорошо копироваться в новое адресное пространство.

Однако вы можете получить «прокси»объект для работы, что когда вы вызываете прокси-методы, они делают соответствующие вызовы ReadMemory(), чтобы получить информацию.Это звучит довольно трудоемко, и я думаю, это должен быть более или менее индивидуальный набор кода для каждого класса, который вы хотите прокси.Вероятно, есть лучший способ сделать это, но это то, что пришло мне в голову.

0 голосов
/ 05 апреля 2010

Я подошел к чему-то похожему, когда взламывал расширение трассировки gdi для windbg. Я использовал контейнер stl для хранения данных в клиенте и мне нужен был способ перебрать данные из расширения. В итоге я реализовал необходимые части hash_map непосредственно на стороне расширения, используя ExtRemoteTyped, который был удовлетворительным, но мне потребовалось некоторое время, чтобы разобраться; o) Здесь - исходный код.

0 голосов
/ 04 апреля 2010

Я знаю, что получение дампов памяти всегда было способом получения информации для диагностики, но с ETW это намного проще, и вы получаете информацию вместе со стеками вызовов, которые включают вызовы информационной системы и код пользователя. MS делает это для всех своих продуктов, включая Windows и VS.NET.

Это ненавязчивый способ отладки. Я выполнял одну и ту же отладку очень долго, и теперь с ETW я могу решить большинство проблем клиентов, не проводя много времени внутри отладчика. Это мои два цента.

...