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
_tmain(int argc,TCHAR * argv[])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
ProcessArgs(int argc,TCHAR * argv[])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
PrintUsage()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
getMemoryInfo(DWORD processID)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
printProcessInfo(DWORD processID)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
evalProcesses(HANDLE hProcess)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
UseImage(void (functionForQueryType (HANDLE)))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
QueryContinuously(HANDLE hProcess)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
ElapsedTime(time_t startTime)209 time_t ElapsedTime(time_t startTime)
210 {
211 time_t currentTime = time(NULL);
212 return currentTime - startTime;
213 }
214