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 <tlhelp32.h>
      8 #include "Shlwapi.h"
      9 
     10 #pragma comment(lib, "psapi.lib")
     11 #pragma comment(lib, "shlwapi.lib")
     12 
     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 int EvalProcesses(HANDLE hProcess);
     22 time_t ElapsedTime(time_t startTime);
     23 
     24 int __cdecl _tmain (int argc, TCHAR *argv[])
     25 {
     26     HRESULT result = ProcessArgs(argc, argv);
     27     if (FAILED(result))
     28         return result;
     29 
     30     UseImage(QueryContinuously);
     31     return S_OK;
     32 }
     33 
     34 HRESULT ProcessArgs(int argc, TCHAR *argv[])
     35 {
     36     LPTSTR argument;
     37     for( int count = 1; count < argc; count++ ) {
     38         argument = argv[count] ;
     39         if (wcsstr(argument, _T("-h")) || wcsstr(argument, _T("--help")))
     40             return PrintUsage();
     41         else if (wcsstr(argument, _T("--exe"))) {
     42             gCommandLine = argv[++count];
     43         } else if (wcsstr(argument, _T("-i")) ||
     44             wcsstr(argument, _T("--interval"))) {
     45             gQueryInterval = _wtoi(argv[++count]);
     46             if (gQueryInterval < 1) {
     47                 printf("ERROR: invalid interval\n");
     48                 return E_INVALIDARG;
     49             }
     50         } else if (wcsstr(argument, _T("-d")) ||
     51             wcsstr(argument, _T("--duration"))) {
     52             gDuration = _wtoi(argv[++count]);
     53             if (gDuration < 1) {
     54                 printf("ERROR: invalid duration\n");
     55                 return E_INVALIDARG;
     56             }
     57         } else {
     58             _tprintf(_T("ERROR: unrecognized argument \"%s\"\n"), (LPCTSTR)argument);
     59             return PrintUsage();
     60         }
     61     }
     62     if (argc < 2 || !wcslen(gCommandLine) ) {
     63         printf("ERROR: executable path is required\n");
     64         return PrintUsage();
     65     }
     66     return S_OK;
     67 }
     68 
     69 HRESULT PrintUsage()
     70 {
     71     printf("record-memory-win --exe EXE_PATH\n");
     72     printf("    Launch an executable and print the memory usage (in Private Bytes)\n");
     73     printf("    of the process.\n\n");
     74     printf("Usage:\n");
     75     printf("-h [--help]         : Print usage\n");
     76     printf("--exe arg           : Launch specified image.  Required\n");
     77     printf("-i [--interval] arg : Print memory usage every arg seconds.  Default: 5 seconds\n");
     78     printf("-d [--duration] arg : Run for up to arg seconds.  Default: no limit\n\n");
     79     printf("Examples:\n");
     80     printf("    record-memory-win --exe \"C:\\Program Files\\Safari\\Safari.exe /newprocess\"\n");
     81     printf("    record-memory-win --exe \"Safari.exe /newprocess\" -i 10 -d 7200\n");
     82     printf("    NOTE: Close all other browser intances to ensure launching in a new process\n");
     83     printf("          Or, pass the /newprocess (or equivalent) argument to the browser\n");
     84     return E_FAIL;
     85 }
     86 
     87 unsigned int getMemoryInfo(DWORD processID)
     88 {
     89     unsigned int memInfo = 0;
     90     HANDLE hProcess;
     91     PROCESS_MEMORY_COUNTERS_EX pmc;
     92 
     93     hProcess = OpenProcess(  PROCESS_QUERY_INFORMATION |
     94                                     PROCESS_VM_READ,
     95                                     FALSE, processID );
     96     if (NULL == hProcess)
     97         return 0;
     98 
     99     if (GetProcessMemoryInfo( hProcess, (PPROCESS_MEMORY_COUNTERS)&pmc, sizeof(pmc))) {
    100         memInfo = (pmc.PrivateUsage);
    101     }
    102 
    103     CloseHandle( hProcess );
    104     return memInfo;
    105 }
    106 
    107 void printProcessInfo(DWORD processID)
    108 {
    109     TCHAR szProcessName[MAX_PATH] = TEXT("<unknown>");
    110 
    111     // Get a handle to the process.
    112     HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION |
    113                                    PROCESS_VM_READ,
    114                                    FALSE, processID );
    115 
    116     // Get the process name.
    117     if (NULL != hProcess) {
    118         HMODULE hMod;       // An array that receives the list of module handles.
    119         DWORD cbNeeded;     //The number of bytes required to store all module handles in the Module array
    120 
    121         if (EnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded)) {
    122             GetModuleBaseName(hProcess, hMod, szProcessName,
    123                                sizeof(szProcessName)/sizeof(TCHAR));
    124         }
    125     }
    126 
    127     // Print the process name and identifier of matching strings, ignoring case
    128     _tprintf(TEXT("%s  (PID: %u)\n"), szProcessName, processID);
    129 
    130     // Release the handle to the process.
    131     CloseHandle( hProcess );
    132 }
    133 
    134 int evalProcesses(HANDLE hProcess)
    135 {
    136     if (NULL == hProcess)
    137         return 0;
    138 
    139     unsigned int totalMemUsage = 0;
    140     DWORD processID = GetProcessId(hProcess);
    141 
    142     HANDLE hProcessSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    143 
    144     PROCESSENTRY32 processEntry = { 0 };
    145     processEntry.dwSize = sizeof(PROCESSENTRY32);
    146 
    147     // Retrieves information about the first process encountered in a system snapshot
    148     if(Process32First(hProcessSnapshot, &processEntry)) {
    149         do {
    150             // if th32processID = processID, we are the parent process!
    151             // if th32ParentProcessID = processID, we are a child process!
    152             if ((processEntry.th32ProcessID == processID) || (processEntry.th32ParentProcessID == processID)) {
    153                 unsigned int procMemUsage = 0;
    154                 // Record parent process memory
    155                 procMemUsage = getMemoryInfo(processEntry.th32ProcessID);
    156                 totalMemUsage += procMemUsage;
    157             }
    158           // Retrieves information about the next process recorded in a system snapshot.
    159         } while(Process32Next(hProcessSnapshot, &processEntry));
    160     }
    161 
    162     CloseHandle(hProcessSnapshot);
    163     return totalMemUsage;
    164 }
    165 
    166 
    167 void UseImage(void (functionForQueryType(HANDLE)))
    168 {
    169     STARTUPINFO si = {0};
    170     si.cb = sizeof(STARTUPINFO);
    171     PROCESS_INFORMATION pi = {0};
    172 
    173     // Start the child process.
    174     if(!CreateProcess( NULL,   // No module name (use command line)
    175         gCommandLine,        // Command line
    176         NULL,           // Process handle not inheritable
    177         NULL,           // Thread handle not inheritable
    178         FALSE,          // Set handle inheritance to FALSE
    179         0,              // No creation flags
    180         NULL,           // Use parent's environment block
    181         NULL,           // Use parent's starting directory
    182         &si,            // Pointer to STARTUPINFO structure
    183         &pi ))          // Pointer to PROCESS_INFORMATION structure
    184         printf("CreateProcess failed (%d)\n", GetLastError());
    185     else {
    186         printf("Created process with id: %d\n", pi.dwProcessId);
    187         functionForQueryType(pi.hProcess);
    188         // Close process and thread handles.
    189         CloseHandle( pi.hProcess );
    190         CloseHandle( pi.hThread );
    191     }
    192 }
    193 
    194 void QueryContinuously(HANDLE hProcess)
    195 {
    196     Sleep(2000); // give the process some time to launch
    197     bool pastDuration = false;
    198     time_t startTime = time(NULL);
    199     unsigned int memUsage = evalProcesses(hProcess);
    200     while(memUsage && !pastDuration) {
    201         printf( "%u\n", memUsage );
    202         Sleep(gQueryInterval*1000);
    203         memUsage = evalProcesses(hProcess);
    204         pastDuration = gDuration > 0 ? ElapsedTime(startTime) > gDuration : false;
    205     }
    206 }
    207 
    208 // returns elapsed time in seconds
    209 time_t ElapsedTime(time_t startTime)
    210 {
    211     time_t currentTime = time(NULL);
    212     return currentTime - startTime;
    213 }
    214