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