|
1 /* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 /* |
|
8 * This program tracks a process's working memory usage using the |
|
9 * ``performance'' entries in the Win32 registry. It borrows from |
|
10 * the ``pviewer'' source code in the MS SDK. |
|
11 */ |
|
12 |
|
13 #include <assert.h> |
|
14 #include <windows.h> |
|
15 #include <winperf.h> |
|
16 #include <stdio.h> |
|
17 #include <stdlib.h> |
|
18 |
|
19 #define PN_PROCESS 1 |
|
20 #define PN_PROCESS_CPU 2 |
|
21 #define PN_PROCESS_PRIV 3 |
|
22 #define PN_PROCESS_USER 4 |
|
23 #define PN_PROCESS_WORKING_SET 5 |
|
24 #define PN_PROCESS_PEAK_WS 6 |
|
25 #define PN_PROCESS_PRIO 7 |
|
26 #define PN_PROCESS_ELAPSE 8 |
|
27 #define PN_PROCESS_ID 9 |
|
28 #define PN_PROCESS_PRIVATE_PAGE 10 |
|
29 #define PN_PROCESS_VIRTUAL_SIZE 11 |
|
30 #define PN_PROCESS_PEAK_VS 12 |
|
31 #define PN_PROCESS_FAULT_COUNT 13 |
|
32 #define PN_THREAD 14 |
|
33 #define PN_THREAD_CPU 15 |
|
34 #define PN_THREAD_PRIV 16 |
|
35 #define PN_THREAD_USER 17 |
|
36 #define PN_THREAD_START 18 |
|
37 #define PN_THREAD_SWITCHES 19 |
|
38 #define PN_THREAD_PRIO 20 |
|
39 #define PN_THREAD_BASE_PRIO 21 |
|
40 #define PN_THREAD_ELAPSE 22 |
|
41 #define PN_THREAD_DETAILS 23 |
|
42 #define PN_THREAD_PC 24 |
|
43 #define PN_IMAGE 25 |
|
44 #define PN_IMAGE_NOACCESS 26 |
|
45 #define PN_IMAGE_READONLY 27 |
|
46 #define PN_IMAGE_READWRITE 28 |
|
47 #define PN_IMAGE_WRITECOPY 29 |
|
48 #define PN_IMAGE_EXECUTABLE 30 |
|
49 #define PN_IMAGE_EXE_READONLY 31 |
|
50 #define PN_IMAGE_EXE_READWRITE 32 |
|
51 #define PN_IMAGE_EXE_WRITECOPY 33 |
|
52 #define PN_PROCESS_ADDRESS_SPACE 34 |
|
53 #define PN_PROCESS_PRIVATE_NOACCESS 35 |
|
54 #define PN_PROCESS_PRIVATE_READONLY 36 |
|
55 #define PN_PROCESS_PRIVATE_READWRITE 37 |
|
56 #define PN_PROCESS_PRIVATE_WRITECOPY 38 |
|
57 #define PN_PROCESS_PRIVATE_EXECUTABLE 39 |
|
58 #define PN_PROCESS_PRIVATE_EXE_READONLY 40 |
|
59 #define PN_PROCESS_PRIVATE_EXE_READWRITE 41 |
|
60 #define PN_PROCESS_PRIVATE_EXE_WRITECOPY 42 |
|
61 #define PN_PROCESS_MAPPED_NOACCESS 43 |
|
62 #define PN_PROCESS_MAPPED_READONLY 44 |
|
63 #define PN_PROCESS_MAPPED_READWRITE 45 |
|
64 #define PN_PROCESS_MAPPED_WRITECOPY 46 |
|
65 #define PN_PROCESS_MAPPED_EXECUTABLE 47 |
|
66 #define PN_PROCESS_MAPPED_EXE_READONLY 48 |
|
67 #define PN_PROCESS_MAPPED_EXE_READWRITE 49 |
|
68 #define PN_PROCESS_MAPPED_EXE_WRITECOPY 50 |
|
69 #define PN_PROCESS_IMAGE_NOACCESS 51 |
|
70 #define PN_PROCESS_IMAGE_READONLY 52 |
|
71 #define PN_PROCESS_IMAGE_READWRITE 53 |
|
72 #define PN_PROCESS_IMAGE_WRITECOPY 54 |
|
73 #define PN_PROCESS_IMAGE_EXECUTABLE 55 |
|
74 #define PN_PROCESS_IMAGE_EXE_READONLY 56 |
|
75 #define PN_PROCESS_IMAGE_EXE_READWRITE 57 |
|
76 #define PN_PROCESS_IMAGE_EXE_WRITECOPY 58 |
|
77 |
|
78 struct entry_t { |
|
79 int e_key; |
|
80 int e_index; |
|
81 char* e_title; |
|
82 }; |
|
83 |
|
84 entry_t entries[] = { |
|
85 { PN_PROCESS, 0, TEXT("Process") }, |
|
86 { PN_PROCESS_CPU, 0, TEXT("% Processor Time") }, |
|
87 { PN_PROCESS_PRIV, 0, TEXT("% Privileged Time") }, |
|
88 { PN_PROCESS_USER, 0, TEXT("% User Time") }, |
|
89 { PN_PROCESS_WORKING_SET, 0, TEXT("Working Set") }, |
|
90 { PN_PROCESS_PEAK_WS, 0, TEXT("Working Set Peak") }, |
|
91 { PN_PROCESS_PRIO, 0, TEXT("Priority Base") }, |
|
92 { PN_PROCESS_ELAPSE, 0, TEXT("Elapsed Time") }, |
|
93 { PN_PROCESS_ID, 0, TEXT("ID Process") }, |
|
94 { PN_PROCESS_PRIVATE_PAGE, 0, TEXT("Private Bytes") }, |
|
95 { PN_PROCESS_VIRTUAL_SIZE, 0, TEXT("Virtual Bytes") }, |
|
96 { PN_PROCESS_PEAK_VS, 0, TEXT("Virtual Bytes Peak") }, |
|
97 { PN_PROCESS_FAULT_COUNT, 0, TEXT("Page Faults/sec") }, |
|
98 { PN_THREAD, 0, TEXT("Thread") }, |
|
99 { PN_THREAD_CPU, 0, TEXT("% Processor Time") }, |
|
100 { PN_THREAD_PRIV, 0, TEXT("% Privileged Time") }, |
|
101 { PN_THREAD_USER, 0, TEXT("% User Time") }, |
|
102 { PN_THREAD_START, 0, TEXT("Start Address") }, |
|
103 { PN_THREAD_SWITCHES, 0, TEXT("Con0, TEXT Switches/sec") }, |
|
104 { PN_THREAD_PRIO, 0, TEXT("Priority Current") }, |
|
105 { PN_THREAD_BASE_PRIO, 0, TEXT("Priority Base") }, |
|
106 { PN_THREAD_ELAPSE, 0, TEXT("Elapsed Time") }, |
|
107 { PN_THREAD_DETAILS, 0, TEXT("Thread Details") }, |
|
108 { PN_THREAD_PC, 0, TEXT("User PC") }, |
|
109 { PN_IMAGE, 0, TEXT("Image") }, |
|
110 { PN_IMAGE_NOACCESS, 0, TEXT("No Access") }, |
|
111 { PN_IMAGE_READONLY, 0, TEXT("Read Only") }, |
|
112 { PN_IMAGE_READWRITE, 0, TEXT("Read/Write") }, |
|
113 { PN_IMAGE_WRITECOPY, 0, TEXT("Write Copy") }, |
|
114 { PN_IMAGE_EXECUTABLE, 0, TEXT("Executable") }, |
|
115 { PN_IMAGE_EXE_READONLY, 0, TEXT("Exec Read Only") }, |
|
116 { PN_IMAGE_EXE_READWRITE, 0, TEXT("Exec Read/Write") }, |
|
117 { PN_IMAGE_EXE_WRITECOPY, 0, TEXT("Exec Write Copy") }, |
|
118 { PN_PROCESS_ADDRESS_SPACE, 0, TEXT("Process Address Space") }, |
|
119 { PN_PROCESS_PRIVATE_NOACCESS, 0, TEXT("Reserved Space No Access") }, |
|
120 { PN_PROCESS_PRIVATE_READONLY, 0, TEXT("Reserved Space Read Only") }, |
|
121 { PN_PROCESS_PRIVATE_READWRITE, 0, TEXT("Reserved Space Read/Write") }, |
|
122 { PN_PROCESS_PRIVATE_WRITECOPY, 0, TEXT("Reserved Space Write Copy") }, |
|
123 { PN_PROCESS_PRIVATE_EXECUTABLE, 0, TEXT("Reserved Space Executable") }, |
|
124 { PN_PROCESS_PRIVATE_EXE_READONLY, 0, TEXT("Reserved Space Exec Read Only") }, |
|
125 { PN_PROCESS_PRIVATE_EXE_READWRITE, 0, TEXT("Reserved Space Exec Read/Write") }, |
|
126 { PN_PROCESS_PRIVATE_EXE_WRITECOPY, 0, TEXT("Reserved Space Exec Write Copy") }, |
|
127 { PN_PROCESS_MAPPED_NOACCESS, 0, TEXT("Mapped Space No Access") }, |
|
128 { PN_PROCESS_MAPPED_READONLY, 0, TEXT("Mapped Space Read Only") }, |
|
129 { PN_PROCESS_MAPPED_READWRITE, 0, TEXT("Mapped Space Read/Write") }, |
|
130 { PN_PROCESS_MAPPED_WRITECOPY, 0, TEXT("Mapped Space Write Copy") }, |
|
131 { PN_PROCESS_MAPPED_EXECUTABLE, 0, TEXT("Mapped Space Executable") }, |
|
132 { PN_PROCESS_MAPPED_EXE_READONLY, 0, TEXT("Mapped Space Exec Read Only") }, |
|
133 { PN_PROCESS_MAPPED_EXE_READWRITE, 0, TEXT("Mapped Space Exec Read/Write") }, |
|
134 { PN_PROCESS_MAPPED_EXE_WRITECOPY, 0, TEXT("Mapped Space Exec Write Copy") }, |
|
135 { PN_PROCESS_IMAGE_NOACCESS, 0, TEXT("Image Space No Access") }, |
|
136 { PN_PROCESS_IMAGE_READONLY, 0, TEXT("Image Space Read Only") }, |
|
137 { PN_PROCESS_IMAGE_READWRITE, 0, TEXT("Image Space Read/Write") }, |
|
138 { PN_PROCESS_IMAGE_WRITECOPY, 0, TEXT("Image Space Write Copy") }, |
|
139 { PN_PROCESS_IMAGE_EXECUTABLE, 0, TEXT("Image Space Executable") }, |
|
140 { PN_PROCESS_IMAGE_EXE_READONLY, 0, TEXT("Image Space Exec Read Only") }, |
|
141 { PN_PROCESS_IMAGE_EXE_READWRITE, 0, TEXT("Image Space Exec Read/Write") }, |
|
142 { PN_PROCESS_IMAGE_EXE_WRITECOPY, 0, TEXT("Image Space Exec Write Copy") }, |
|
143 { 0, 0, 0 }, |
|
144 }; |
|
145 |
|
146 #define NENTRIES ((sizeof(entries) / sizeof(entry_t)) - 1) |
|
147 |
|
148 static int |
|
149 key_for_index(int key) |
|
150 { |
|
151 entry_t* entry = entries + NENTRIES / 2; |
|
152 unsigned int step = 64 / 4; // XXX |
|
153 |
|
154 while (step) { |
|
155 if (key < entry->e_key) |
|
156 entry -= step; |
|
157 else if (key > entry->e_key) |
|
158 entry += step; |
|
159 |
|
160 if (key == entry->e_key) |
|
161 return entry->e_index; |
|
162 |
|
163 step >>= 1; |
|
164 } |
|
165 |
|
166 assert(false); |
|
167 return 0; |
|
168 } |
|
169 |
|
170 |
|
171 class auto_hkey { |
|
172 protected: |
|
173 HKEY hkey; |
|
174 |
|
175 HKEY* begin_assignment() { |
|
176 if (hkey) { |
|
177 ::RegCloseKey(hkey); |
|
178 hkey = 0; |
|
179 } |
|
180 return &hkey; |
|
181 } |
|
182 |
|
183 public: |
|
184 auto_hkey() : hkey(0) {} |
|
185 ~auto_hkey() { ::RegCloseKey(hkey); } |
|
186 |
|
187 HKEY get() const { return hkey; } |
|
188 operator HKEY() const { return get(); } |
|
189 |
|
190 friend HKEY* |
|
191 getter_Acquires(auto_hkey& hkey); |
|
192 }; |
|
193 |
|
194 static HKEY* |
|
195 getter_Acquires(auto_hkey& hkey) |
|
196 { |
|
197 return hkey.begin_assignment(); |
|
198 } |
|
199 |
|
200 |
|
201 static int |
|
202 get_perf_titles(char*& buffer, char**& titles, int& last_title_index) |
|
203 { |
|
204 DWORD result; |
|
205 |
|
206 // Open the perflib key to find out the last counter's index and |
|
207 // system version. |
|
208 auto_hkey perflib_hkey; |
|
209 result = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, |
|
210 TEXT("software\\microsoft\\windows nt\\currentversion\\perflib"), |
|
211 0, |
|
212 KEY_READ, |
|
213 getter_Acquires(perflib_hkey)); |
|
214 |
|
215 if (result != ERROR_SUCCESS) |
|
216 return result; |
|
217 |
|
218 // Get the last counter's index so we know how much memory to |
|
219 // allocate for titles |
|
220 DWORD data_size = sizeof(DWORD); |
|
221 DWORD type; |
|
222 result = ::RegQueryValueEx(perflib_hkey, |
|
223 TEXT("Last Counter"), |
|
224 0, |
|
225 &type, |
|
226 reinterpret_cast<BYTE*>(&last_title_index), |
|
227 &data_size); |
|
228 |
|
229 if (result != ERROR_SUCCESS) |
|
230 return result; |
|
231 |
|
232 // Find system version, for system earlier than 1.0a, there's no |
|
233 // version value. |
|
234 int version; |
|
235 result = ::RegQueryValueEx(perflib_hkey, |
|
236 TEXT("Version"), |
|
237 0, |
|
238 &type, |
|
239 reinterpret_cast<BYTE*>(&version), |
|
240 &data_size); |
|
241 |
|
242 bool is_nt_10 = (result == ERROR_SUCCESS); |
|
243 |
|
244 // Now, get ready for the counter names and indexes. |
|
245 char* counter_value_name; |
|
246 auto_hkey counter_autohkey; |
|
247 HKEY counter_hkey; |
|
248 if (is_nt_10) { |
|
249 // NT 1.0, so make hKey2 point to ...\perflib\009 and get |
|
250 // the counters from value "Counters" |
|
251 counter_value_name = TEXT("Counters"); |
|
252 result = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, |
|
253 TEXT("software\\microsoft\\windows nt\\currentversion\\perflib\\009"), |
|
254 0, |
|
255 KEY_READ, |
|
256 getter_Acquires(counter_autohkey)); |
|
257 |
|
258 if (result != ERROR_SUCCESS) |
|
259 return result; |
|
260 |
|
261 counter_hkey = counter_autohkey; |
|
262 } |
|
263 else { |
|
264 // NT 1.0a or later. Get the counters in key HKEY_PERFORMANCE_KEY |
|
265 // and from value "Counter 009" |
|
266 counter_value_name = TEXT("Counter 009"); |
|
267 counter_hkey = HKEY_PERFORMANCE_DATA; |
|
268 } |
|
269 |
|
270 // Find out the size of the data. |
|
271 result = ::RegQueryValueEx(counter_hkey, |
|
272 counter_value_name, |
|
273 0, |
|
274 &type, |
|
275 0, |
|
276 &data_size); |
|
277 |
|
278 if (result != ERROR_SUCCESS) |
|
279 return result; |
|
280 |
|
281 // Allocate memory |
|
282 buffer = new char[data_size]; |
|
283 titles = new char*[last_title_index + 1]; |
|
284 for (int i = 0; i <= last_title_index; ++i) |
|
285 titles[i] = 0; |
|
286 |
|
287 // Query the data |
|
288 result = ::RegQueryValueEx(counter_hkey, |
|
289 counter_value_name, |
|
290 0, |
|
291 &type, |
|
292 reinterpret_cast<BYTE*>(buffer), |
|
293 &data_size); |
|
294 if (result != ERROR_SUCCESS) |
|
295 return result; |
|
296 |
|
297 // Setup the titles array of pointers to point to beginning of |
|
298 // each title string. |
|
299 char* title = buffer; |
|
300 int len; |
|
301 |
|
302 while (len = lstrlen(title)) { |
|
303 int index = atoi(title); |
|
304 title += len + 1; |
|
305 |
|
306 if (index <= last_title_index) |
|
307 titles[index] = title; |
|
308 |
|
309 #ifdef DEBUG |
|
310 printf("%d=%s\n", index, title); |
|
311 #endif |
|
312 |
|
313 title += lstrlen(title) + 1; |
|
314 } |
|
315 |
|
316 return ERROR_SUCCESS; |
|
317 } |
|
318 |
|
319 static void |
|
320 init_entries() |
|
321 { |
|
322 char* buffer; |
|
323 char** titles; |
|
324 int last = 0; |
|
325 |
|
326 DWORD result = get_perf_titles(buffer, titles, last); |
|
327 |
|
328 assert(result == ERROR_SUCCESS); |
|
329 |
|
330 for (entry_t* entry = entries; entry->e_key != 0; ++entry) { |
|
331 for (int index = 0; index <= last; ++index) { |
|
332 if (titles[index] && 0 == lstrcmpi(titles[index], entry->e_title)) { |
|
333 entry->e_index = index; |
|
334 break; |
|
335 } |
|
336 } |
|
337 |
|
338 if (entry->e_index == 0) { |
|
339 fprintf(stderr, "warning: unable to find index for ``%s''\n", entry->e_title); |
|
340 } |
|
341 } |
|
342 |
|
343 delete[] buffer; |
|
344 delete[] titles; |
|
345 } |
|
346 |
|
347 |
|
348 |
|
349 static DWORD |
|
350 get_perf_data(HKEY perf_hkey, char* object_index, PERF_DATA_BLOCK** data, DWORD* size) |
|
351 { |
|
352 if (! *data) |
|
353 *data = reinterpret_cast<PERF_DATA_BLOCK*>(new char[*size]); |
|
354 |
|
355 DWORD result; |
|
356 |
|
357 while (1) { |
|
358 DWORD type; |
|
359 DWORD real_size = *size; |
|
360 |
|
361 result = ::RegQueryValueEx(perf_hkey, |
|
362 object_index, |
|
363 0, |
|
364 &type, |
|
365 reinterpret_cast<BYTE*>(*data), |
|
366 &real_size); |
|
367 |
|
368 if (result != ERROR_MORE_DATA) |
|
369 break; |
|
370 |
|
371 delete[] *data; |
|
372 *size += 1024; |
|
373 *data = reinterpret_cast<PERF_DATA_BLOCK*>(new char[*size]); |
|
374 |
|
375 if (! *data) |
|
376 return ERROR_NOT_ENOUGH_MEMORY; |
|
377 } |
|
378 |
|
379 return result; |
|
380 } |
|
381 |
|
382 |
|
383 static const PERF_OBJECT_TYPE* |
|
384 first_object(const PERF_DATA_BLOCK* data) |
|
385 { |
|
386 return data |
|
387 ? reinterpret_cast<const PERF_OBJECT_TYPE*>(reinterpret_cast<const char*>(data) + data->HeaderLength) |
|
388 : 0; |
|
389 } |
|
390 |
|
391 static const PERF_OBJECT_TYPE* |
|
392 next_object(const PERF_OBJECT_TYPE* object) |
|
393 { |
|
394 return object |
|
395 ? reinterpret_cast<const PERF_OBJECT_TYPE*>(reinterpret_cast<const char*>(object) + object->TotalByteLength) |
|
396 : 0; |
|
397 } |
|
398 |
|
399 const PERF_OBJECT_TYPE* |
|
400 find_object(const PERF_DATA_BLOCK* data, DWORD index) |
|
401 { |
|
402 const PERF_OBJECT_TYPE* object = first_object(data); |
|
403 if (! object) |
|
404 return 0; |
|
405 |
|
406 for (int i = 0; i < data->NumObjectTypes; ++i) { |
|
407 if (object->ObjectNameTitleIndex == index) |
|
408 return object; |
|
409 |
|
410 object = next_object(object); |
|
411 } |
|
412 |
|
413 return 0; |
|
414 } |
|
415 |
|
416 |
|
417 static const PERF_COUNTER_DEFINITION* |
|
418 first_counter(const PERF_OBJECT_TYPE* object) |
|
419 { |
|
420 return object |
|
421 ? reinterpret_cast<const PERF_COUNTER_DEFINITION*>(reinterpret_cast<const char*>(object) + object->HeaderLength) |
|
422 : 0; |
|
423 } |
|
424 |
|
425 static const PERF_COUNTER_DEFINITION* |
|
426 next_counter(const PERF_COUNTER_DEFINITION* counter) |
|
427 { |
|
428 return counter ? |
|
429 reinterpret_cast<const PERF_COUNTER_DEFINITION*>(reinterpret_cast<const char*>(counter) + counter->ByteLength) |
|
430 : 0; |
|
431 } |
|
432 |
|
433 |
|
434 static const PERF_COUNTER_DEFINITION* |
|
435 find_counter(const PERF_OBJECT_TYPE* object, int index) |
|
436 { |
|
437 const PERF_COUNTER_DEFINITION* counter = |
|
438 first_counter(object); |
|
439 |
|
440 if (! counter) |
|
441 return 0; |
|
442 |
|
443 for (int i; i < object->NumCounters; ++i) { |
|
444 if (counter->CounterNameTitleIndex == index) |
|
445 return counter; |
|
446 |
|
447 counter = next_counter(counter); |
|
448 } |
|
449 |
|
450 return 0; |
|
451 } |
|
452 |
|
453 |
|
454 static const PERF_INSTANCE_DEFINITION* |
|
455 first_instance(const PERF_OBJECT_TYPE* object) |
|
456 { |
|
457 return object |
|
458 ? reinterpret_cast<const PERF_INSTANCE_DEFINITION*>(reinterpret_cast<const char*>(object) + object->DefinitionLength) |
|
459 : 0; |
|
460 } |
|
461 |
|
462 |
|
463 static const PERF_INSTANCE_DEFINITION* |
|
464 next_instance(const PERF_INSTANCE_DEFINITION* instance) |
|
465 { |
|
466 if (instance) { |
|
467 const PERF_COUNTER_BLOCK* counter_block = |
|
468 reinterpret_cast<const PERF_COUNTER_BLOCK*>(reinterpret_cast<const char*>(instance) + instance->ByteLength); |
|
469 |
|
470 return reinterpret_cast<const PERF_INSTANCE_DEFINITION*>(reinterpret_cast<const char*>(counter_block) + counter_block->ByteLength); |
|
471 } |
|
472 else { |
|
473 return 0; |
|
474 } |
|
475 } |
|
476 |
|
477 |
|
478 static const wchar_t* |
|
479 instance_name(const PERF_INSTANCE_DEFINITION* instance) |
|
480 { |
|
481 return instance |
|
482 ? reinterpret_cast<const wchar_t*>(reinterpret_cast<const char*>(instance) + instance->NameOffset) |
|
483 : 0; |
|
484 } |
|
485 |
|
486 |
|
487 static const void* |
|
488 counter_data(const PERF_INSTANCE_DEFINITION* instance, |
|
489 const PERF_COUNTER_DEFINITION* counter) |
|
490 { |
|
491 if (counter && instance) { |
|
492 const PERF_COUNTER_BLOCK* counter_block; |
|
493 counter_block = reinterpret_cast<const PERF_COUNTER_BLOCK*>(reinterpret_cast<const char*>(instance) + instance->ByteLength); |
|
494 return reinterpret_cast<const char*>(counter_block) + counter->CounterOffset; |
|
495 } |
|
496 else { |
|
497 return 0; |
|
498 } |
|
499 } |
|
500 |
|
501 |
|
502 static bool |
|
503 list_process(PERF_DATA_BLOCK* perf_data, wchar_t* process_name) |
|
504 { |
|
505 const PERF_OBJECT_TYPE* process = find_object(perf_data, key_for_index(PN_PROCESS)); |
|
506 const PERF_COUNTER_DEFINITION* working_set = find_counter(process, key_for_index(PN_PROCESS_WORKING_SET)); |
|
507 const PERF_COUNTER_DEFINITION* peak_working_set = find_counter(process, key_for_index(PN_PROCESS_PEAK_WS)); |
|
508 const PERF_COUNTER_DEFINITION* private_page = find_counter(process, key_for_index(PN_PROCESS_PRIVATE_PAGE)); |
|
509 const PERF_COUNTER_DEFINITION* virtual_size = find_counter(process, key_for_index(PN_PROCESS_VIRTUAL_SIZE)); |
|
510 |
|
511 const PERF_INSTANCE_DEFINITION* instance = first_instance(process); |
|
512 int index = 0; |
|
513 |
|
514 bool found = false; |
|
515 |
|
516 while (instance && index < process->NumInstances) { |
|
517 const wchar_t* name = instance_name(instance); |
|
518 if (lstrcmpW(process_name, name) == 0) { |
|
519 printf("%d %d %d %d\n", |
|
520 *(static_cast<const int*>(counter_data(instance, working_set))), |
|
521 *(static_cast<const int*>(counter_data(instance, peak_working_set))), |
|
522 *(static_cast<const int*>(counter_data(instance, private_page))), |
|
523 *(static_cast<const int*>(counter_data(instance, virtual_size)))); |
|
524 |
|
525 found = true; |
|
526 } |
|
527 |
|
528 instance = next_instance(instance); |
|
529 ++index; |
|
530 } |
|
531 |
|
532 if (found) { |
|
533 #if 0 |
|
534 // Dig up address space data. |
|
535 PERF_OBJECT_TYPE* address_space = FindObject(costly_data, PX_PROCESS_ADDRESS_SPACE); |
|
536 PERF_COUNTER_DEFINITION* image_executable = FindCounter(process, PX_PROCESS_IMAGE_EXECUTABLE); |
|
537 PERF_COUNTER_DEFINITION* image_exe_readonly = FindCounter(process, PX_PROCESS_IMAGE_EXE_READONLY); |
|
538 PERF_COUNTER_DEFINITION* image_exe_readwrite = FindCounter(process, PX_PROCESS_IMAGE_EXE_READWRITE); |
|
539 PERF_COUNTER_DEFINITION* image_exe_writecopy = FindCounter(process, PX_PROCESS_IMAGE_EXE_WRITECOPY); |
|
540 #endif |
|
541 } |
|
542 |
|
543 return found; |
|
544 } |
|
545 |
|
546 |
|
547 int |
|
548 main(int argc, char* argv[]) |
|
549 { |
|
550 wchar_t process_name[32]; |
|
551 |
|
552 int interval = 10000; // msec |
|
553 |
|
554 int i = 0; |
|
555 while (++i < argc) { |
|
556 if (argv[i][0] != '-') |
|
557 break; |
|
558 |
|
559 switch (argv[i][1]) { |
|
560 case 'i': |
|
561 interval = atoi(argv[++i]) * 1000; |
|
562 break; |
|
563 |
|
564 default: |
|
565 fprintf(stderr, "unknown option `%c'\n", argv[i][1]); |
|
566 exit(1); |
|
567 } |
|
568 } |
|
569 |
|
570 if (argv[i]) { |
|
571 char* p = argv[i]; |
|
572 wchar_t* q = process_name; |
|
573 while (*q++ = wchar_t(*p++)) |
|
574 continue; |
|
575 } |
|
576 else { |
|
577 fprintf(stderr, "no image name specified\n"); |
|
578 exit(1); |
|
579 } |
|
580 |
|
581 init_entries(); |
|
582 |
|
583 PERF_DATA_BLOCK* perf_data = 0; |
|
584 PERF_DATA_BLOCK* costly_data = 0; |
|
585 DWORD perf_data_size = 50 * 1024; |
|
586 DWORD costly_data_size = 100 * 1024; |
|
587 |
|
588 do { |
|
589 char buf[64]; |
|
590 sprintf(buf, "%ld %ld", |
|
591 key_for_index(PN_PROCESS), |
|
592 key_for_index(PN_THREAD)); |
|
593 |
|
594 get_perf_data(HKEY_PERFORMANCE_DATA, buf, &perf_data, &perf_data_size); |
|
595 |
|
596 #if 0 |
|
597 sprintf(buf, "%ld %ld %ld", |
|
598 key_for_index(PN_PROCESS_ADDRESS_SPACE), |
|
599 key_for_index(PN_IMAGE), |
|
600 key_for_index(PN_THREAD_DETAILS)); |
|
601 |
|
602 get_perf_data(HKEY_PERFORMANCE_DATA, buf, &costly_data, &costly_data_size); |
|
603 #endif |
|
604 |
|
605 if (! list_process(perf_data, process_name)) |
|
606 break; |
|
607 |
|
608 _sleep(interval); |
|
609 } while (1); |
|
610 |
|
611 return 0; |
|
612 } |
|
613 |