#include <windows.h> #include <assert.h> #include <psapi.h> #include <stdio.h> #include <tchar.h> #include <time.h> #include <tlhelp32.h> #include "Shlwapi.h" #pragma comment(lib, "psapi.lib") #pragma comment(lib, "shlwapi.lib") int gQueryInterval = 5; // seconds time_t gDuration = 0; // seconds LPTSTR gCommandLine; HRESULT ProcessArgs(int argc, TCHAR *argv[]); HRESULT PrintUsage(); void UseImage(void (functionForQueryType(HANDLE))); void QueryContinuously(HANDLE hProcess); int EvalProcesses(HANDLE hProcess); time_t ElapsedTime(time_t startTime); int __cdecl _tmain (int argc, TCHAR *argv[]) { HRESULT result = ProcessArgs(argc, argv); if (FAILED(result)) return result; UseImage(QueryContinuously); return S_OK; } HRESULT ProcessArgs(int argc, TCHAR *argv[]) { LPTSTR argument; for( int count = 1; count < argc; count++ ) { argument = argv[count] ; if (wcsstr(argument, _T("-h")) || wcsstr(argument, _T("--help"))) return PrintUsage(); else if (wcsstr(argument, _T("--exe"))) { gCommandLine = argv[++count]; } else if (wcsstr(argument, _T("-i")) || wcsstr(argument, _T("--interval"))) { gQueryInterval = _wtoi(argv[++count]); if (gQueryInterval < 1) { printf("ERROR: invalid interval\n"); return E_INVALIDARG; } } else if (wcsstr(argument, _T("-d")) || wcsstr(argument, _T("--duration"))) { gDuration = _wtoi(argv[++count]); if (gDuration < 1) { printf("ERROR: invalid duration\n"); return E_INVALIDARG; } } else { _tprintf(_T("ERROR: unrecognized argument \"%s\"\n"), (LPCTSTR)argument); return PrintUsage(); } } if (argc < 2 || !wcslen(gCommandLine) ) { printf("ERROR: executable path is required\n"); return PrintUsage(); } return S_OK; } HRESULT PrintUsage() { printf("record-memory-win --exe EXE_PATH\n"); printf(" Launch an executable and print the memory usage (in Private Bytes)\n"); printf(" of the process.\n\n"); printf("Usage:\n"); printf("-h [--help] : Print usage\n"); printf("--exe arg : Launch specified image. Required\n"); printf("-i [--interval] arg : Print memory usage every arg seconds. Default: 5 seconds\n"); printf("-d [--duration] arg : Run for up to arg seconds. Default: no limit\n\n"); printf("Examples:\n"); printf(" record-memory-win --exe \"C:\\Program Files\\Safari\\Safari.exe /newprocess\"\n"); printf(" record-memory-win --exe \"Safari.exe /newprocess\" -i 10 -d 7200\n"); printf(" NOTE: Close all other browser intances to ensure launching in a new process\n"); printf(" Or, pass the /newprocess (or equivalent) argument to the browser\n"); return E_FAIL; } unsigned int getMemoryInfo(DWORD processID) { unsigned int memInfo = 0; HANDLE hProcess; PROCESS_MEMORY_COUNTERS_EX pmc; hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID ); if (NULL == hProcess) return 0; if (GetProcessMemoryInfo( hProcess, (PPROCESS_MEMORY_COUNTERS)&pmc, sizeof(pmc))) { memInfo = (pmc.PrivateUsage); } CloseHandle( hProcess ); return memInfo; } void printProcessInfo(DWORD processID) { TCHAR szProcessName[MAX_PATH] = TEXT("<unknown>"); // Get a handle to the process. HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processID ); // Get the process name. if (NULL != hProcess) { HMODULE hMod; // An array that receives the list of module handles. DWORD cbNeeded; //The number of bytes required to store all module handles in the Module array if (EnumProcessModules(hProcess, &hMod, sizeof(hMod), &cbNeeded)) { GetModuleBaseName(hProcess, hMod, szProcessName, sizeof(szProcessName)/sizeof(TCHAR)); } } // Print the process name and identifier of matching strings, ignoring case _tprintf(TEXT("%s (PID: %u)\n"), szProcessName, processID); // Release the handle to the process. CloseHandle( hProcess ); } int evalProcesses(HANDLE hProcess) { if (NULL == hProcess) return 0; unsigned int totalMemUsage = 0; DWORD processID = GetProcessId(hProcess); HANDLE hProcessSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); PROCESSENTRY32 processEntry = { 0 }; processEntry.dwSize = sizeof(PROCESSENTRY32); // Retrieves information about the first process encountered in a system snapshot if(Process32First(hProcessSnapshot, &processEntry)) { do { // if th32processID = processID, we are the parent process! // if th32ParentProcessID = processID, we are a child process! if ((processEntry.th32ProcessID == processID) || (processEntry.th32ParentProcessID == processID)) { unsigned int procMemUsage = 0; // Record parent process memory procMemUsage = getMemoryInfo(processEntry.th32ProcessID); totalMemUsage += procMemUsage; } // Retrieves information about the next process recorded in a system snapshot. } while(Process32Next(hProcessSnapshot, &processEntry)); } CloseHandle(hProcessSnapshot); return totalMemUsage; } void UseImage(void (functionForQueryType(HANDLE))) { STARTUPINFO si = {0}; si.cb = sizeof(STARTUPINFO); PROCESS_INFORMATION pi = {0}; // Start the child process. if(!CreateProcess( NULL, // No module name (use command line) gCommandLine, // Command line NULL, // Process handle not inheritable NULL, // Thread handle not inheritable FALSE, // Set handle inheritance to FALSE 0, // No creation flags NULL, // Use parent's environment block NULL, // Use parent's starting directory &si, // Pointer to STARTUPINFO structure &pi )) // Pointer to PROCESS_INFORMATION structure printf("CreateProcess failed (%d)\n", GetLastError()); else { printf("Created process with id: %d\n", pi.dwProcessId); functionForQueryType(pi.hProcess); // Close process and thread handles. CloseHandle( pi.hProcess ); CloseHandle( pi.hThread ); } } void QueryContinuously(HANDLE hProcess) { Sleep(2000); // give the process some time to launch bool pastDuration = false; time_t startTime = time(NULL); unsigned int memUsage = evalProcesses(hProcess); while(memUsage && !pastDuration) { printf( "%u\n", memUsage ); Sleep(gQueryInterval*1000); memUsage = evalProcesses(hProcess); pastDuration = gDuration > 0 ? ElapsedTime(startTime) > gDuration : false; } } // returns elapsed time in seconds time_t ElapsedTime(time_t startTime) { time_t currentTime = time(NULL); return currentTime - startTime; }