Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to perform advanced search of all process memory from Visual Studio debugger?

I am a developer on a large commercial program, and I am trying to track down a particular C++ memory leak. I would like to use Visual Studio to search the entire valid address space of my process, but I can not see how to perform this search effectively.

I am aware of the .s command documentation here, but it is not doing what I need. For examle, I am 100% certain that the address 0xfdfd240 contains the value 0x0f0e34a8. I can successfully use the .s command to search near that address like this:

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

But my program has made many small allocations which have left me with many small non-contiguious sections of memory. If I back up a few thousand bytes the search command fails:

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

And it also appears that the search command does not attempt to search forward any more when it finds it's first bad address, even if there are valid addresses beyond it.

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

I'm vaguely aware that there is a way to ask windows what memory ranges are valid for a given process, so I'm considering writing a small throwaway program to harvest that information and then automate a series of search commands to the Immediate Window...but it seems like someone must have dealt with this before, and must have done something smarter.

Also, I can extract a dump file of the running process, so if anyone can recommend third party tools to feed the dump to that has stronger search functionality that should work out as well.

Any suggestions?

Edit: I see this behavior in VS2008SP1 and VS2010SP1.

like image 583
Jaqenn Avatar asked Mar 31 '11 01:03

Jaqenn


2 Answers

Here's a small tool that finds the memory blocks1 in a target process, searches the blocks for a pattern, and prints out the addresses at which it found the pattern. Right now it's fairly primitive -- purely command line, expects you to supply the PID of the target process, only accepts the pattern as a single string on the command line. The search portion should be reasonably robust, so it's mostly a matter of a pretty weak UI -- nonetheless, it works fine as long as the pattern you're looking for is something you can enter as a string on the command line. If you want to include non-printable characters, it would be pretty easy to add in a translator I wrote years ago that would let you use C-style escape sequences on the command line.


1 skipping blocks that hold things like the code of the executable/DLLs for that process.


#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;
}
like image 98
Jerry Coffin Avatar answered Sep 27 '22 17:09

Jerry Coffin


When doing anything beyond simple debugging, I find that often times WinDbg (part of Debugging Tools for Windows) has far better capability.

And you don't even have to do all your debugging from WinDbg. If you are in VS doing your thing and then want to search memory (or do other things which are not easy in VS), go to Debug -> Save Dump As... Make sure to select "Minidump with Heap" file type.

This will create a complete snapshot file of your process. Load that up into WinDbg and now you have oh some many cool commands at your disposal. For easy access to the documentation, you can always type ".hh" in the command window. All Commands are listed under Debuggers -> Debugger Reference -> Debugger Commands.

The one you want is s (Search Memory)

like image 43
DXM Avatar answered Sep 27 '22 18:09

DXM