Home | History | Annotate | Download | only in record-memory-win
      1 #include <windows.h>
      2 #include <assert.h>
      3 #include <psapi.h>
      4 #include <stdio.h>
      5 #include <tchar.h>
      6 #include <time.h>
      7 #include "Shlwapi.h"
      8 
      9 #pragma comment(lib, "psapi.lib")
     10 #pragma comment(lib, "shlwapi.lib")
     11 
     12 bool gSingleProcess = true;
     13 int gQueryInterval = 5; // seconds
     14 time_t gDuration = 0;   // seconds
     15 LPTSTR gCommandLine;
     16 
     17 HRESULT ProcessArgs(int argc, TCHAR *argv[]);
     18 HRESULT PrintUsage();
     19 void UseImage(void (functionForQueryType(HANDLE)));
     20 void QueryContinuously(HANDLE hProcess);
     21 time_t ElapsedTime(time_t startTime);
     22 unsigned int OneQuery(HANDLE hProcess);
     23 unsigned int OneQueryMP(HANDLE hProcess);
     24 
     25 int __cdecl _tmain (int argc, TCHAR *argv[])
     26 {
     27     HRESULT result = ProcessArgs(argc, argv);
     28     if (FAILED(result))
     29         return result;
     30 
     31     UseImage(QueryContinuously);
     32     return S_OK;
     33 }
     34 
     35 HRESULT ProcessArgs(int argc, TCHAR *argv[])
     36 {
     37     LPTSTR argument;
     38     for( int count = 1; count < argc; count++ ) {
     39         argument = argv[count] ;
     40         if (wcsstr(argument, _T("-h")) ||
     41             wcsstr(argument, _T("--help")))
     42             return PrintUsage();
     43         else if (wcsstr(argument, _T("--exe"))) {
     44             gCommandLine = argv[++count];
     45             if (wcsstr(gCommandLine, _T("chrome.exe")))
     46                 gSingleProcess = false;
     47         } else if (wcsstr(argument, _T("-i")) ||
     48             wcsstr(argument, _T("--interval"))) {
     49             gQueryInterval = _wtoi(argv[++count]);
     50             if (gQueryInterval < 1) {
     51                 printf("ERROR: invalid interval\n");
     52                 return E_INVALIDARG;
     53             }
     54         } else if (wcsstr(argument, _T("-d")) ||
     55             wcsstr(argument, _T("--duration"))) {
     56             gDuration = _wtoi(argv[++count]);
     57             if (gDuration < 1) {
     58                 printf("ERROR: invalid duration\n");
     59                 return E_INVALIDARG;
     60             }
     61         } else {
     62             _tprintf(_T("ERROR: unrecognized argument \"%s\"\n"), (LPCTSTR)argument);
     63             return PrintUsage();
     64         }
     65     }
     66     if (argc < 2 || !wcslen(gCommandLine) ) {
     67         printf("ERROR: executable path is required\n");
     68         return PrintUsage();
     69     }
     70     return S_OK;
     71 }
     72 
     73 HRESULT PrintUsage()
     74 {
     75     printf("record-memory-win --exe EXE_PATH\n");
     76     printf("    Launch an executable and print the memory usage (in Private Bytes)\n");
     77     printf("    of the process.\n\n");
     78     printf("Usage:\n");
     79     printf("-h [--help]         : Print usage\n");
     80     printf("--exe arg           : Launch specified image.  Required\n");
     81     printf("-i [--interval] arg : Print memory usage every arg seconds.  Default: 5 seconds\n");
     82     printf("-d [--duration] arg : Run for up to arg seconds.  Default: no limit\n\n");
     83     printf("Examples:\n");
     84     printf("    record-memory-win --exe \"C:\\Program Files\\Safari\\Safari.exe\"\n");
     85     printf("    record-memory-win --exe Safari.exe -i 10 -d 7200\n");
     86     return E_FAIL;
     87 }
     88 
     89 void UseImage(void (functionForQueryType(HANDLE)))
     90 {
     91     STARTUPINFO si = {0};
     92     si.cb = sizeof(STARTUPINFO);
     93     PROCESS_INFORMATION pi = {0};
     94 
     95     // Start the child process.
     96     if(!CreateProcess( NULL,   // No module name (use command line)
     97         gCommandLine,        // Command line
     98         NULL,           // Process handle not inheritable
     99         NULL,           // Thread handle not inheritable
    100         FALSE,          // Set handle inheritance to FALSE
    101         0,              // No creation flags
    102         NULL,           // Use parent's environment block
    103         NULL,           // Use parent's starting directory
    104         &si,            // Pointer to STARTUPINFO structure
    105         &pi ))          // Pointer to PROCESS_INFORMATION structure
    106         printf("CreateProcess failed (%d)\n", GetLastError());
    107     else {
    108         printf("Created process\n");
    109         functionForQueryType(pi.hProcess);
    110         // Close process and thread handles.
    111         CloseHandle( pi.hProcess );
    112         CloseHandle( pi.hThread );
    113     }
    114 }
    115 
    116 void QueryContinuously(HANDLE hProcess)
    117 {
    118     Sleep(2000); // give the process some time to launch
    119     bool pastDuration = false;
    120     time_t startTime = time(NULL);
    121     unsigned int memUsage = gSingleProcess ? OneQuery(hProcess) : OneQueryMP(hProcess);
    122     while(memUsage && !pastDuration) {
    123         printf( "%u\n", memUsage );
    124         Sleep(gQueryInterval*1000);
    125         memUsage = gSingleProcess ? OneQuery(hProcess) : OneQueryMP(hProcess);
    126         pastDuration = gDuration > 0 ? ElapsedTime(startTime) > gDuration : false;
    127     }
    128 }
    129 
    130 // returns elapsed time in seconds
    131 time_t ElapsedTime(time_t startTime)
    132 {
    133     time_t currentTime = time(NULL);
    134     return currentTime - startTime;
    135 }
    136 
    137 // returns Commit Size (Private Bytes) in bytes
    138 unsigned int OneQuery(HANDLE hProcess)
    139 {
    140     PROCESS_MEMORY_COUNTERS_EX pmc;
    141     if (NULL == hProcess)
    142         return 0;
    143     if (GetProcessMemoryInfo(hProcess, (PPROCESS_MEMORY_COUNTERS)&pmc, sizeof(pmc)))
    144         return (unsigned)pmc.PrivateUsage;
    145     return 0;
    146 }
    147 
    148 // returns Commit Size (Private Bytes) in bytes for multi-process executables
    149 unsigned int OneQueryMP(HANDLE hProcess)
    150 {
    151     unsigned int memUsage = 0;
    152     TCHAR monitoredProcessName[MAX_PATH];
    153     GetProcessImageFileName(hProcess, monitoredProcessName, sizeof(monitoredProcessName)/sizeof(TCHAR));
    154     LPTSTR shortProcessName = PathFindFileName(monitoredProcessName);
    155     DWORD aProcesses[1024], cbNeeded, cProcesses;
    156     HANDLE hFoundProcess;
    157     if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded))
    158         return 0;
    159 
    160     // Calculate how many process identifiers were returned.
    161     cProcesses = cbNeeded / sizeof(DWORD);
    162     // find existing process
    163     for (unsigned int i = 0; i < cProcesses; i++)
    164         if (aProcesses[i] != 0) {
    165             DWORD retVal = 0;
    166             TCHAR foundProcessName[MAX_PATH];
    167 
    168             // Get a handle to the process.
    169             hFoundProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
    170                                    PROCESS_VM_READ,
    171                                    FALSE, aProcesses[i]);
    172 
    173             // Get the process name.
    174             if (NULL != hFoundProcess) {
    175                 HMODULE hMod;
    176                 DWORD cbNeeded;
    177 
    178                 if (EnumProcessModules(hFoundProcess, &hMod, sizeof(hMod), &cbNeeded)) {
    179                     GetModuleBaseName(hFoundProcess, hMod, foundProcessName, sizeof(foundProcessName)/sizeof(TCHAR));
    180                     if (wcsstr(foundProcessName, shortProcessName))
    181                         memUsage += OneQuery(hFoundProcess);
    182                 }
    183             }
    184             CloseHandle(hFoundProcess);
    185         }
    186     return memUsage;
    187 }
    188