#include <windows.h> | |
#include <assert.h> | |
#include <psapi.h> | |
#include <stdio.h> | |
#include <tchar.h> | |
#include <time.h> | |
#include "Shlwapi.h" | |
#pragma comment(lib, "psapi.lib") | |
#pragma comment(lib, "shlwapi.lib") | |
bool gSingleProcess = true; | |
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); | |
time_t ElapsedTime(time_t startTime); | |
unsigned int OneQuery(HANDLE hProcess); | |
unsigned int OneQueryMP(HANDLE hProcess); | |
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]; | |
if (wcsstr(gCommandLine, _T("chrome.exe"))) | |
gSingleProcess = false; | |
} 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\"\n"); | |
printf(" record-memory-win --exe Safari.exe -i 10 -d 7200\n"); | |
return E_FAIL; | |
} | |
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\n"); | |
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 = gSingleProcess ? OneQuery(hProcess) : OneQueryMP(hProcess); | |
while(memUsage && !pastDuration) { | |
printf( "%u\n", memUsage ); | |
Sleep(gQueryInterval*1000); | |
memUsage = gSingleProcess ? OneQuery(hProcess) : OneQueryMP(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; | |
} | |
// returns Commit Size (Private Bytes) in bytes | |
unsigned int OneQuery(HANDLE hProcess) | |
{ | |
PROCESS_MEMORY_COUNTERS_EX pmc; | |
if (NULL == hProcess) | |
return 0; | |
if (GetProcessMemoryInfo(hProcess, (PPROCESS_MEMORY_COUNTERS)&pmc, sizeof(pmc))) | |
return (unsigned)pmc.PrivateUsage; | |
return 0; | |
} | |
// returns Commit Size (Private Bytes) in bytes for multi-process executables | |
unsigned int OneQueryMP(HANDLE hProcess) | |
{ | |
unsigned int memUsage = 0; | |
TCHAR monitoredProcessName[MAX_PATH]; | |
GetProcessImageFileName(hProcess, monitoredProcessName, sizeof(monitoredProcessName)/sizeof(TCHAR)); | |
LPTSTR shortProcessName = PathFindFileName(monitoredProcessName); | |
DWORD aProcesses[1024], cbNeeded, cProcesses; | |
HANDLE hFoundProcess; | |
if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded)) | |
return 0; | |
// Calculate how many process identifiers were returned. | |
cProcesses = cbNeeded / sizeof(DWORD); | |
// find existing process | |
for (unsigned int i = 0; i < cProcesses; i++) | |
if (aProcesses[i] != 0) { | |
DWORD retVal = 0; | |
TCHAR foundProcessName[MAX_PATH]; | |
// Get a handle to the process. | |
hFoundProcess = OpenProcess(PROCESS_QUERY_INFORMATION | | |
PROCESS_VM_READ, | |
FALSE, aProcesses[i]); | |
// Get the process name. | |
if (NULL != hFoundProcess) { | |
HMODULE hMod; | |
DWORD cbNeeded; | |
if (EnumProcessModules(hFoundProcess, &hMod, sizeof(hMod), &cbNeeded)) { | |
GetModuleBaseName(hFoundProcess, hMod, foundProcessName, sizeof(foundProcessName)/sizeof(TCHAR)); | |
if (wcsstr(foundProcessName, shortProcessName)) | |
memUsage += OneQuery(hFoundProcess); | |
} | |
} | |
CloseHandle(hFoundProcess); | |
} | |
return memUsage; | |
} |