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/. */
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.
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
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
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
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") },
146 #define NENTRIES ((sizeof(entries) / sizeof(entry_t)) - 1)
149 key_for_index(int key
)
151 entry_t
* entry
= entries
+ NENTRIES
/ 2;
152 unsigned int step
= 64 / 4; // XXX
155 if (key
< entry
->e_key
)
157 else if (key
> entry
->e_key
)
160 if (key
== entry
->e_key
)
161 return entry
->e_index
;
175 HKEY
* begin_assignment() {
184 auto_hkey() : hkey(0) {}
185 ~auto_hkey() { ::RegCloseKey(hkey
); }
187 HKEY
get() const { return hkey
; }
188 operator HKEY() const { return get(); }
191 getter_Acquires(auto_hkey
& hkey
);
195 getter_Acquires(auto_hkey
& hkey
)
197 return hkey
.begin_assignment();
202 get_perf_titles(char*& buffer
, char**& titles
, int& last_title_index
)
206 // Open the perflib key to find out the last counter's index and
208 auto_hkey perflib_hkey
;
209 result
= ::RegOpenKeyEx(HKEY_LOCAL_MACHINE
,
210 TEXT("software\\microsoft\\windows nt\\currentversion\\perflib"),
213 getter_Acquires(perflib_hkey
));
215 if (result
!= ERROR_SUCCESS
)
218 // Get the last counter's index so we know how much memory to
219 // allocate for titles
220 DWORD data_size
= sizeof(DWORD
);
222 result
= ::RegQueryValueEx(perflib_hkey
,
223 TEXT("Last Counter"),
226 reinterpret_cast<BYTE
*>(&last_title_index
),
229 if (result
!= ERROR_SUCCESS
)
232 // Find system version, for system earlier than 1.0a, there's no
235 result
= ::RegQueryValueEx(perflib_hkey
,
239 reinterpret_cast<BYTE
*>(&version
),
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
;
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"),
256 getter_Acquires(counter_autohkey
));
258 if (result
!= ERROR_SUCCESS
)
261 counter_hkey
= counter_autohkey
;
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
,
278 if (result
!= ERROR_SUCCESS
)
282 buffer
= new char[data_size
];
283 titles
= new char*[last_title_index
+ 1];
284 for (int i
= 0; i
<= last_title_index
; ++i
)
288 result
= ::RegQueryValueEx(counter_hkey
,
292 reinterpret_cast<BYTE
*>(buffer
),
294 if (result
!= ERROR_SUCCESS
)
297 // Setup the titles array of pointers to point to beginning of
298 // each title string.
299 char* title
= buffer
;
302 while (len
= lstrlen(title
)) {
303 int index
= atoi(title
);
306 if (index
<= last_title_index
)
307 titles
[index
] = title
;
310 printf("%d=%s\n", index
, title
);
313 title
+= lstrlen(title
) + 1;
316 return ERROR_SUCCESS
;
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
;
338 if (entry
->e_index
== 0) {
339 fprintf(stderr
, "warning: unable to find index for ``%s''\n", entry
->e_title
);
350 get_perf_data(HKEY perf_hkey
, char* object_index
, PERF_DATA_BLOCK
** data
, DWORD
* size
)
353 *data
= reinterpret_cast<PERF_DATA_BLOCK
*>(new char[*size
]);
359 DWORD real_size
= *size
;
361 result
= ::RegQueryValueEx(perf_hkey
,
365 reinterpret_cast<BYTE
*>(*data
),
368 if (result
!= ERROR_MORE_DATA
)
373 *data
= reinterpret_cast<PERF_DATA_BLOCK
*>(new char[*size
]);
376 return ERROR_NOT_ENOUGH_MEMORY
;
383 static const PERF_OBJECT_TYPE
*
384 first_object(const PERF_DATA_BLOCK
* data
)
387 ? reinterpret_cast<const PERF_OBJECT_TYPE
*>(reinterpret_cast<const char*>(data
) + data
->HeaderLength
)
391 static const PERF_OBJECT_TYPE
*
392 next_object(const PERF_OBJECT_TYPE
* object
)
395 ? reinterpret_cast<const PERF_OBJECT_TYPE
*>(reinterpret_cast<const char*>(object
) + object
->TotalByteLength
)
399 const PERF_OBJECT_TYPE
*
400 find_object(const PERF_DATA_BLOCK
* data
, DWORD index
)
402 const PERF_OBJECT_TYPE
* object
= first_object(data
);
406 for (int i
= 0; i
< data
->NumObjectTypes
; ++i
) {
407 if (object
->ObjectNameTitleIndex
== index
)
410 object
= next_object(object
);
417 static const PERF_COUNTER_DEFINITION
*
418 first_counter(const PERF_OBJECT_TYPE
* object
)
421 ? reinterpret_cast<const PERF_COUNTER_DEFINITION
*>(reinterpret_cast<const char*>(object
) + object
->HeaderLength
)
425 static const PERF_COUNTER_DEFINITION
*
426 next_counter(const PERF_COUNTER_DEFINITION
* counter
)
429 reinterpret_cast<const PERF_COUNTER_DEFINITION
*>(reinterpret_cast<const char*>(counter
) + counter
->ByteLength
)
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
);
443 for (int i
; i
< object
->NumCounters
; ++i
) {
444 if (counter
->CounterNameTitleIndex
== index
)
447 counter
= next_counter(counter
);
454 static const PERF_INSTANCE_DEFINITION
*
455 first_instance(const PERF_OBJECT_TYPE
* object
)
458 ? reinterpret_cast<const PERF_INSTANCE_DEFINITION
*>(reinterpret_cast<const char*>(object
) + object
->DefinitionLength
)
463 static const PERF_INSTANCE_DEFINITION
*
464 next_instance(const PERF_INSTANCE_DEFINITION
* 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
);
478 static const wchar_t*
479 instance_name(const PERF_INSTANCE_DEFINITION
* instance
)
482 ? reinterpret_cast<const wchar_t*>(reinterpret_cast<const char*>(instance
) + instance
->NameOffset
)
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
;
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
);
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
))));
528 instance
= next_instance(instance
);
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
);
548 main(int argc
, char* argv
[])
550 wchar_t process_name
[32];
552 int interval
= 10000; // msec
556 if (argv
[i
][0] != '-')
559 switch (argv
[i
][1]) {
561 interval
= atoi(argv
[++i
]) * 1000;
565 fprintf(stderr
, "unknown option `%c'\n", argv
[i
][1]);
572 wchar_t* q
= process_name
;
573 while (*q
++ = wchar_t(*p
++))
577 fprintf(stderr
, "no image name specified\n");
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;
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
);
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
);
605 if (! list_process(perf_data
, process_name
))