jeudi 31 janvier 2013

Debugging Tips: Exporting a bunch of memory without instrumenting thecode!

Context and problem:

In the last 3 weeks, I worked on aligning a C/C++ code with its Matlab equivalent. During that task I came across the following problem.... I had to compare the data and the program state from C/C++ and Matlab code during 2 parallel debugging session,. So how can we debug and save at any time a C++ vector as binary file without adding noisy instrumentation code?

1
2
3
4
std::vector<double> vec(10);
std::ofstream output("out.bin", std::ios::binary|std::ios::out);
output.write(reinterpret_cast<char*>(&vec[0]), vec.size()*sizeof(vec[0]));
output.close();


 And when the file is ready, write in Matlab (for example):
vec_from_c_code = read(open('out.bin'), 'double');
sum(abs(vec_from_c_code - vec_in_matlab))

It could be a debugger feature:

That's why as usual, I first googled it, but I didn't find something ready to use.....

I used Visual Studio 2008 IDE to develop and debug, and it's a really good tools, but it doesn't contain that feature. Using the "memory windows" developer can just see the program memory in hexadecimal and eventually copy a part of it as Text.

With WinDbg, you should have access to a ".writemem" function, and it looks like some extension for VS'2010 to support that syntax from the "Immediate console".... So if its your case, search on http://visualstudiogallery.msdn.microsoft.com/

In another hand Matlab come with an API to directly feed in realtime data from C/C++ (and maybe others languages) in a Matlab session for vizualisation, etc.... But it's something I would found in a production code.

So I build it by myself based on some informations found in the previous page, my own command line.

A solution:

The code below is a simple usage of the 2 function win32 API, OpenProcess and ReadProcessMemory (more details on MSDN). I call this tool a ProcessMemDumper....  evolutions of this code, using a Write function can be made to replace the memory of the program with data coming from file during a debugging session.
Using your debugger, your are going step-by-step through your program, you found for example a "std::vector vec;" that you want to see in another tools (Matlab in my case). You need 3 things, pickup from a simple debugger watch:
  • the offset in the process memory , &vec[0],
  • and the number of bytes, vec size * 8(size of double)
Also get the process PID, and  call from a command line:
  • processmemdumper.exe PID offset nbbytes  out.bin
 Really more efficient than adding export source code everywhere....

Source code:

1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
// ProcessMemDumper.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

#include <windows.h>

#include <string>
#include <iostream>
#include <fstream>
#include <vector>
#include <stdexcept>
#include <sstream>

#include "boost/shared_ptr.hpp"

void delete_HANDLE(HANDLE p) 
{ 
  std::cout << "Releasing Handle ...." << std::endl;
  CloseHandle(p);  
};


int _tmain(int argc, _TCHAR* argv[])
{
 try
 {
  unsigned int procPID = atoi(argv[1]);
  std::string procAddressHexa = std::string(argv[2]);
  std::stringstream hexToInt;
  hexToInt << std::hex << procAddressHexa;
  unsigned int procAddress = 0;
  hexToInt >> procAddress;
  unsigned int nbbytetoRead = atoi(argv[3]);
  std::string targetFileName = std::string(argv[4]);

  boost::shared_ptr<void> procHandle(OpenProcess(PROCESS_VM_READ, false, DWORD(procPID)), &::delete_HANDLE);
  if(procHandle)
  {
   std::vector<char> memCopyofProcMem(nbbytetoRead);

   SIZE_T NumberOfBytesRead = 0;
   if(ReadProcessMemory(procHandle.get(), LPCVOID(procAddress), LPVOID(&memCopyofProcMem[0]), SIZE_T(nbbytetoRead), &NumberOfBytesRead) && NumberOfBytesRead == nbbytetoRead)
   {
    std::ofstream targetFile(targetFileName, std::ios::binary|std::ios::out);
    targetFile.write(&memCopyofProcMem[0], nbbytetoRead);
    targetFile.close();
   }
   else
   {
    std::cout << "ReadProcessMemory return false, or NumberOfBytesRead != of the requested number of byte." << std::endl;
    return -1;
   }
  } 
 }
 catch (const std::exception& e)
 {
  std::cout << "std::exception: " << e.what() << std::endl;
  return -1;
 }
 catch (...)
 {
  std::cout << "unknown exception: " << std::endl;
  return -1;
 }
 return 0;
}