Как выполнить расширенный поиск всей памяти процесса из отладчика Visual Studio? - PullRequest
3 голосов
/ 31 марта 2011

Я разработчик крупной коммерческой программы, и я пытаюсь отследить конкретную утечку памяти в C ++. Я хотел бы использовать Visual Studio для поиска по всему допустимому адресному пространству моего процесса, но я не вижу, как эффективно выполнить этот поиск.

Мне известна документация команды .s здесь , но она не выполняет то, что мне нужно. Например, я на 100% уверен, что адрес 0xfdfd240 содержит значение 0x0f0e34a8. Я могу успешно использовать команду .s для поиска рядом с этим адресом, как это:

.s -d 0x0fdfd200 L256000000 0x0f0e34a8
Found match at
0xfdfd240

Но моя программа сделала много небольших выделений, которые оставили мне много маленьких несмежных участков памяти. Если я резервирую несколько тысяч байт, команда поиска завершается неудачно:

.s -d 0x0fd00000 L256000000 0x0f0e34a8
Memory location could not be read. Please specify a valid memory location.

Кроме того, похоже, что команда поиска больше не пытается искать вперед, когда находит свой первый неверный адрес, , даже если за ним есть действительные адреса .

.s -d 0x0f000000 L256000000 0x0f0e34a8
No match was found

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

Кроме того, я могу извлечь файл дампа запущенного процесса, так что если кто-нибудь может порекомендовать сторонние инструменты для подачи дампа в него, который обладает более мощной функцией поиска, которая также должна сработать.

Есть предложения?

Редактировать. Такое поведение наблюдается в VS2008SP1 и VS2010SP1.

Ответы [ 2 ]

4 голосов
/ 31 марта 2011

Вот небольшой инструмент, который находит блоки памяти 1 в целевом процессе, ищет блоки для шаблона и распечатывает адреса, по которым он нашел шаблон.Прямо сейчас это довольно примитивно - чисто командная строка, ожидает, что вы предоставите PID целевого процесса, принимает шаблон только как одну строку в командной строке.Часть поиска должна быть достаточно надежной, так что это в основном дело в довольно слабом пользовательском интерфейсе - тем не менее, она работает нормально, пока искомый шаблон можно вводить в виде строки в командной строке.Если вы хотите включить непечатаемые символы, было бы довольно легко добавить переводчик , который я написал несколько лет назад, который позволил бы вам использовать escape-последовательности в стиле C в командной строке.


1 пропуск блоков, которые содержат такие вещи, как код исполняемого файла / DLL для этого процесса.


#include <iostream>
#include <vector>
#include <string>
#include <windows.h>
#include <algorithm>
#include <iterator>

template <class outIter>
void find_locs(HANDLE process, std::string const &pattern, outIter output) {

    unsigned char *p = NULL;
    MEMORY_BASIC_INFORMATION info;

    // VirtualQueryEx does the hard part, telling use about each
    // block in the target process.

    for ( p = NULL;
        VirtualQueryEx(process, p, &info, sizeof(info)) == sizeof(info);
        p += info.RegionSize ) 
    {
        // buffer to hold copies of memory blocks from the target
        std::vector<char> buffer;
        std::vector<char>::iterator pos;

        // We only want committed memory that's mapped or private -- 
        // screens out things like the code in the target process.
        // 
        if (info.State == MEM_COMMIT && 
            (info.Type == MEM_MAPPED || info.Type == MEM_PRIVATE)) 
        {             
            DWORD bytes_read;

            // copy block from target to our buffer, search for pattern:
            buffer.resize(info.RegionSize);
            ReadProcessMemory(process, p, &buffer[0], info.RegionSize, &bytes_read);
            buffer.resize(bytes_read);

            // find all occurrences of pattern in buffer:
            for ( pos = buffer.begin();
                buffer.end()!=(pos=std::search(pos, buffer.end(), pattern.begin(), pattern.end()));
                ++pos)
            {
                // record each address in target where pattern was found:
                *output++ = p+(pos-buffer.begin());
            }
        }
    }
}

int main(int argc, char **argv) {
    if (argc != 3) {
        fprintf(stderr, "Usage: %s <process ID> <pattern>", argv[0]);
        return 1;
    }

    // Read the target PID and search pattern:
    int pid;
    sscanf(argv[1], "%i", &pid);   
    std::string pattern(argv[2]);

    // Open the target process with rights to read its information and memory:
    HANDLE process = OpenProcess( 
        PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, 
        false,
        pid);

    // Find the locations, and write them to standard output, one per line:
    find_locs(process, pattern, 
        std::ostream_iterator<void *>(std::cout, "\n"));

    return 0;
}
1 голос
/ 31 марта 2011

При выполнении чего-либо, кроме простой отладки, я обнаружил, что часто WinDbg (часть средств отладки для Windows) обладает гораздо лучшими возможностями.

И вам даже не нужно выполнять всю отладку из WinDbg.Если вы в VS делаете свое дело, а затем хотите выполнить поиск в памяти (или делаете что-то другое, что не так просто в VS), перейдите в Debug -> Save Dump As ... Убедитесь, что выбран тип файла «Minidump with Heap».

Это создаст полный файл снимка вашего процесса.Загрузите это в WinDbg, и теперь у вас есть несколько интересных команд.Для быстрого доступа к документации вы всегда можете ввести «.hh» в командном окне.Все команды перечислены в разделе «Отладчики» -> «Справочник по отладчику» -> «Команды отладчика».

Вам нужна команда s (Search Memory)

...