• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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