Bumping manifests a=b2g-bump
[gecko.git] / tools / footprint / wm.cpp
blob48161e9595470b0f8f87407ccf4821e4085bd721
1 /* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*-
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/. */
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.
13 #include <assert.h>
14 #include <windows.h>
15 #include <winperf.h>
16 #include <stdio.h>
17 #include <stdlib.h>
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
78 struct entry_t {
79 int e_key;
80 int e_index;
81 char* e_title;
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 },
146 #define NENTRIES ((sizeof(entries) / sizeof(entry_t)) - 1)
148 static int
149 key_for_index(int key)
151 entry_t* entry = entries + NENTRIES / 2;
152 unsigned int step = 64 / 4; // XXX
154 while (step) {
155 if (key < entry->e_key)
156 entry -= step;
157 else if (key > entry->e_key)
158 entry += step;
160 if (key == entry->e_key)
161 return entry->e_index;
163 step >>= 1;
166 assert(false);
167 return 0;
171 class auto_hkey {
172 protected:
173 HKEY hkey;
175 HKEY* begin_assignment() {
176 if (hkey) {
177 ::RegCloseKey(hkey);
178 hkey = 0;
180 return &hkey;
183 public:
184 auto_hkey() : hkey(0) {}
185 ~auto_hkey() { ::RegCloseKey(hkey); }
187 HKEY get() const { return hkey; }
188 operator HKEY() const { return get(); }
190 friend HKEY*
191 getter_Acquires(auto_hkey& hkey);
194 static HKEY*
195 getter_Acquires(auto_hkey& hkey)
197 return hkey.begin_assignment();
201 static int
202 get_perf_titles(char*& buffer, char**& titles, int& last_title_index)
204 DWORD result;
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"),
212 KEY_READ,
213 getter_Acquires(perflib_hkey));
215 if (result != ERROR_SUCCESS)
216 return result;
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"),
225 &type,
226 reinterpret_cast<BYTE*>(&last_title_index),
227 &data_size);
229 if (result != ERROR_SUCCESS)
230 return result;
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"),
238 &type,
239 reinterpret_cast<BYTE*>(&version),
240 &data_size);
242 bool is_nt_10 = (result == ERROR_SUCCESS);
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"),
255 KEY_READ,
256 getter_Acquires(counter_autohkey));
258 if (result != ERROR_SUCCESS)
259 return result;
261 counter_hkey = counter_autohkey;
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;
270 // Find out the size of the data.
271 result = ::RegQueryValueEx(counter_hkey,
272 counter_value_name,
274 &type,
276 &data_size);
278 if (result != ERROR_SUCCESS)
279 return result;
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;
287 // Query the data
288 result = ::RegQueryValueEx(counter_hkey,
289 counter_value_name,
291 &type,
292 reinterpret_cast<BYTE*>(buffer),
293 &data_size);
294 if (result != ERROR_SUCCESS)
295 return result;
297 // Setup the titles array of pointers to point to beginning of
298 // each title string.
299 char* title = buffer;
300 int len;
302 while (len = lstrlen(title)) {
303 int index = atoi(title);
304 title += len + 1;
306 if (index <= last_title_index)
307 titles[index] = title;
309 #ifdef DEBUG
310 printf("%d=%s\n", index, title);
311 #endif
313 title += lstrlen(title) + 1;
316 return ERROR_SUCCESS;
319 static void
320 init_entries()
322 char* buffer;
323 char** titles;
324 int last = 0;
326 DWORD result = get_perf_titles(buffer, titles, last);
328 assert(result == ERROR_SUCCESS);
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;
338 if (entry->e_index == 0) {
339 fprintf(stderr, "warning: unable to find index for ``%s''\n", entry->e_title);
343 delete[] buffer;
344 delete[] titles;
349 static DWORD
350 get_perf_data(HKEY perf_hkey, char* object_index, PERF_DATA_BLOCK** data, DWORD* size)
352 if (! *data)
353 *data = reinterpret_cast<PERF_DATA_BLOCK*>(new char[*size]);
355 DWORD result;
357 while (1) {
358 DWORD type;
359 DWORD real_size = *size;
361 result = ::RegQueryValueEx(perf_hkey,
362 object_index,
364 &type,
365 reinterpret_cast<BYTE*>(*data),
366 &real_size);
368 if (result != ERROR_MORE_DATA)
369 break;
371 delete[] *data;
372 *size += 1024;
373 *data = reinterpret_cast<PERF_DATA_BLOCK*>(new char[*size]);
375 if (! *data)
376 return ERROR_NOT_ENOUGH_MEMORY;
379 return result;
383 static const PERF_OBJECT_TYPE*
384 first_object(const PERF_DATA_BLOCK* data)
386 return data
387 ? reinterpret_cast<const PERF_OBJECT_TYPE*>(reinterpret_cast<const char*>(data) + data->HeaderLength)
388 : 0;
391 static const PERF_OBJECT_TYPE*
392 next_object(const PERF_OBJECT_TYPE* object)
394 return object
395 ? reinterpret_cast<const PERF_OBJECT_TYPE*>(reinterpret_cast<const char*>(object) + object->TotalByteLength)
396 : 0;
399 const PERF_OBJECT_TYPE*
400 find_object(const PERF_DATA_BLOCK* data, DWORD index)
402 const PERF_OBJECT_TYPE* object = first_object(data);
403 if (! object)
404 return 0;
406 for (int i = 0; i < data->NumObjectTypes; ++i) {
407 if (object->ObjectNameTitleIndex == index)
408 return object;
410 object = next_object(object);
413 return 0;
417 static const PERF_COUNTER_DEFINITION*
418 first_counter(const PERF_OBJECT_TYPE* object)
420 return object
421 ? reinterpret_cast<const PERF_COUNTER_DEFINITION*>(reinterpret_cast<const char*>(object) + object->HeaderLength)
422 : 0;
425 static const PERF_COUNTER_DEFINITION*
426 next_counter(const PERF_COUNTER_DEFINITION* counter)
428 return counter ?
429 reinterpret_cast<const PERF_COUNTER_DEFINITION*>(reinterpret_cast<const char*>(counter) + counter->ByteLength)
430 : 0;
434 static const PERF_COUNTER_DEFINITION*
435 find_counter(const PERF_OBJECT_TYPE* object, int index)
437 const PERF_COUNTER_DEFINITION* counter =
438 first_counter(object);
440 if (! counter)
441 return 0;
443 for (int i; i < object->NumCounters; ++i) {
444 if (counter->CounterNameTitleIndex == index)
445 return counter;
447 counter = next_counter(counter);
450 return 0;
454 static const PERF_INSTANCE_DEFINITION*
455 first_instance(const PERF_OBJECT_TYPE* object)
457 return object
458 ? reinterpret_cast<const PERF_INSTANCE_DEFINITION*>(reinterpret_cast<const char*>(object) + object->DefinitionLength)
459 : 0;
463 static const PERF_INSTANCE_DEFINITION*
464 next_instance(const PERF_INSTANCE_DEFINITION* instance)
466 if (instance) {
467 const PERF_COUNTER_BLOCK* counter_block =
468 reinterpret_cast<const PERF_COUNTER_BLOCK*>(reinterpret_cast<const char*>(instance) + instance->ByteLength);
470 return reinterpret_cast<const PERF_INSTANCE_DEFINITION*>(reinterpret_cast<const char*>(counter_block) + counter_block->ByteLength);
472 else {
473 return 0;
478 static const wchar_t*
479 instance_name(const PERF_INSTANCE_DEFINITION* instance)
481 return instance
482 ? reinterpret_cast<const wchar_t*>(reinterpret_cast<const char*>(instance) + instance->NameOffset)
483 : 0;
487 static const void*
488 counter_data(const PERF_INSTANCE_DEFINITION* instance,
489 const PERF_COUNTER_DEFINITION* counter)
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;
496 else {
497 return 0;
502 static bool
503 list_process(PERF_DATA_BLOCK* perf_data, wchar_t* process_name)
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));
511 const PERF_INSTANCE_DEFINITION* instance = first_instance(process);
512 int index = 0;
514 bool found = false;
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))));
525 found = true;
528 instance = next_instance(instance);
529 ++index;
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
543 return found;
548 main(int argc, char* argv[])
550 wchar_t process_name[32];
552 int interval = 10000; // msec
554 int i = 0;
555 while (++i < argc) {
556 if (argv[i][0] != '-')
557 break;
559 switch (argv[i][1]) {
560 case 'i':
561 interval = atoi(argv[++i]) * 1000;
562 break;
564 default:
565 fprintf(stderr, "unknown option `%c'\n", argv[i][1]);
566 exit(1);
570 if (argv[i]) {
571 char* p = argv[i];
572 wchar_t* q = process_name;
573 while (*q++ = wchar_t(*p++))
574 continue;
576 else {
577 fprintf(stderr, "no image name specified\n");
578 exit(1);
581 init_entries();
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;
588 do {
589 char buf[64];
590 sprintf(buf, "%ld %ld",
591 key_for_index(PN_PROCESS),
592 key_for_index(PN_THREAD));
594 get_perf_data(HKEY_PERFORMANCE_DATA, buf, &perf_data, &perf_data_size);
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));
602 get_perf_data(HKEY_PERFORMANCE_DATA, buf, &costly_data, &costly_data_size);
603 #endif
605 if (! list_process(perf_data, process_name))
606 break;
608 _sleep(interval);
609 } while (1);
611 return 0;