1 /* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is wm.cpp, released
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 2000
22 * the Initial Developer. All Rights Reserved.
25 * Chris Waterson <waterson@netscape.com>
27 * Alternatively, the contents of this file may be used under the terms of
28 * either the GNU General Public License Version 2 or later (the "GPL"), or
29 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
42 * This program tracks a process's working memory usage using the
43 * ``performance'' entries in the Win32 registry. It borrows from
44 * the ``pviewer'' source code in the MS SDK.
54 #define PN_PROCESS_CPU 2
55 #define PN_PROCESS_PRIV 3
56 #define PN_PROCESS_USER 4
57 #define PN_PROCESS_WORKING_SET 5
58 #define PN_PROCESS_PEAK_WS 6
59 #define PN_PROCESS_PRIO 7
60 #define PN_PROCESS_ELAPSE 8
61 #define PN_PROCESS_ID 9
62 #define PN_PROCESS_PRIVATE_PAGE 10
63 #define PN_PROCESS_VIRTUAL_SIZE 11
64 #define PN_PROCESS_PEAK_VS 12
65 #define PN_PROCESS_FAULT_COUNT 13
67 #define PN_THREAD_CPU 15
68 #define PN_THREAD_PRIV 16
69 #define PN_THREAD_USER 17
70 #define PN_THREAD_START 18
71 #define PN_THREAD_SWITCHES 19
72 #define PN_THREAD_PRIO 20
73 #define PN_THREAD_BASE_PRIO 21
74 #define PN_THREAD_ELAPSE 22
75 #define PN_THREAD_DETAILS 23
76 #define PN_THREAD_PC 24
78 #define PN_IMAGE_NOACCESS 26
79 #define PN_IMAGE_READONLY 27
80 #define PN_IMAGE_READWRITE 28
81 #define PN_IMAGE_WRITECOPY 29
82 #define PN_IMAGE_EXECUTABLE 30
83 #define PN_IMAGE_EXE_READONLY 31
84 #define PN_IMAGE_EXE_READWRITE 32
85 #define PN_IMAGE_EXE_WRITECOPY 33
86 #define PN_PROCESS_ADDRESS_SPACE 34
87 #define PN_PROCESS_PRIVATE_NOACCESS 35
88 #define PN_PROCESS_PRIVATE_READONLY 36
89 #define PN_PROCESS_PRIVATE_READWRITE 37
90 #define PN_PROCESS_PRIVATE_WRITECOPY 38
91 #define PN_PROCESS_PRIVATE_EXECUTABLE 39
92 #define PN_PROCESS_PRIVATE_EXE_READONLY 40
93 #define PN_PROCESS_PRIVATE_EXE_READWRITE 41
94 #define PN_PROCESS_PRIVATE_EXE_WRITECOPY 42
95 #define PN_PROCESS_MAPPED_NOACCESS 43
96 #define PN_PROCESS_MAPPED_READONLY 44
97 #define PN_PROCESS_MAPPED_READWRITE 45
98 #define PN_PROCESS_MAPPED_WRITECOPY 46
99 #define PN_PROCESS_MAPPED_EXECUTABLE 47
100 #define PN_PROCESS_MAPPED_EXE_READONLY 48
101 #define PN_PROCESS_MAPPED_EXE_READWRITE 49
102 #define PN_PROCESS_MAPPED_EXE_WRITECOPY 50
103 #define PN_PROCESS_IMAGE_NOACCESS 51
104 #define PN_PROCESS_IMAGE_READONLY 52
105 #define PN_PROCESS_IMAGE_READWRITE 53
106 #define PN_PROCESS_IMAGE_WRITECOPY 54
107 #define PN_PROCESS_IMAGE_EXECUTABLE 55
108 #define PN_PROCESS_IMAGE_EXE_READONLY 56
109 #define PN_PROCESS_IMAGE_EXE_READWRITE 57
110 #define PN_PROCESS_IMAGE_EXE_WRITECOPY 58
118 entry_t entries
[] = {
119 { PN_PROCESS
, 0, TEXT("Process") },
120 { PN_PROCESS_CPU
, 0, TEXT("% Processor Time") },
121 { PN_PROCESS_PRIV
, 0, TEXT("% Privileged Time") },
122 { PN_PROCESS_USER
, 0, TEXT("% User Time") },
123 { PN_PROCESS_WORKING_SET
, 0, TEXT("Working Set") },
124 { PN_PROCESS_PEAK_WS
, 0, TEXT("Working Set Peak") },
125 { PN_PROCESS_PRIO
, 0, TEXT("Priority Base") },
126 { PN_PROCESS_ELAPSE
, 0, TEXT("Elapsed Time") },
127 { PN_PROCESS_ID
, 0, TEXT("ID Process") },
128 { PN_PROCESS_PRIVATE_PAGE
, 0, TEXT("Private Bytes") },
129 { PN_PROCESS_VIRTUAL_SIZE
, 0, TEXT("Virtual Bytes") },
130 { PN_PROCESS_PEAK_VS
, 0, TEXT("Virtual Bytes Peak") },
131 { PN_PROCESS_FAULT_COUNT
, 0, TEXT("Page Faults/sec") },
132 { PN_THREAD
, 0, TEXT("Thread") },
133 { PN_THREAD_CPU
, 0, TEXT("% Processor Time") },
134 { PN_THREAD_PRIV
, 0, TEXT("% Privileged Time") },
135 { PN_THREAD_USER
, 0, TEXT("% User Time") },
136 { PN_THREAD_START
, 0, TEXT("Start Address") },
137 { PN_THREAD_SWITCHES
, 0, TEXT("Con0, TEXT Switches/sec") },
138 { PN_THREAD_PRIO
, 0, TEXT("Priority Current") },
139 { PN_THREAD_BASE_PRIO
, 0, TEXT("Priority Base") },
140 { PN_THREAD_ELAPSE
, 0, TEXT("Elapsed Time") },
141 { PN_THREAD_DETAILS
, 0, TEXT("Thread Details") },
142 { PN_THREAD_PC
, 0, TEXT("User PC") },
143 { PN_IMAGE
, 0, TEXT("Image") },
144 { PN_IMAGE_NOACCESS
, 0, TEXT("No Access") },
145 { PN_IMAGE_READONLY
, 0, TEXT("Read Only") },
146 { PN_IMAGE_READWRITE
, 0, TEXT("Read/Write") },
147 { PN_IMAGE_WRITECOPY
, 0, TEXT("Write Copy") },
148 { PN_IMAGE_EXECUTABLE
, 0, TEXT("Executable") },
149 { PN_IMAGE_EXE_READONLY
, 0, TEXT("Exec Read Only") },
150 { PN_IMAGE_EXE_READWRITE
, 0, TEXT("Exec Read/Write") },
151 { PN_IMAGE_EXE_WRITECOPY
, 0, TEXT("Exec Write Copy") },
152 { PN_PROCESS_ADDRESS_SPACE
, 0, TEXT("Process Address Space") },
153 { PN_PROCESS_PRIVATE_NOACCESS
, 0, TEXT("Reserved Space No Access") },
154 { PN_PROCESS_PRIVATE_READONLY
, 0, TEXT("Reserved Space Read Only") },
155 { PN_PROCESS_PRIVATE_READWRITE
, 0, TEXT("Reserved Space Read/Write") },
156 { PN_PROCESS_PRIVATE_WRITECOPY
, 0, TEXT("Reserved Space Write Copy") },
157 { PN_PROCESS_PRIVATE_EXECUTABLE
, 0, TEXT("Reserved Space Executable") },
158 { PN_PROCESS_PRIVATE_EXE_READONLY
, 0, TEXT("Reserved Space Exec Read Only") },
159 { PN_PROCESS_PRIVATE_EXE_READWRITE
, 0, TEXT("Reserved Space Exec Read/Write") },
160 { PN_PROCESS_PRIVATE_EXE_WRITECOPY
, 0, TEXT("Reserved Space Exec Write Copy") },
161 { PN_PROCESS_MAPPED_NOACCESS
, 0, TEXT("Mapped Space No Access") },
162 { PN_PROCESS_MAPPED_READONLY
, 0, TEXT("Mapped Space Read Only") },
163 { PN_PROCESS_MAPPED_READWRITE
, 0, TEXT("Mapped Space Read/Write") },
164 { PN_PROCESS_MAPPED_WRITECOPY
, 0, TEXT("Mapped Space Write Copy") },
165 { PN_PROCESS_MAPPED_EXECUTABLE
, 0, TEXT("Mapped Space Executable") },
166 { PN_PROCESS_MAPPED_EXE_READONLY
, 0, TEXT("Mapped Space Exec Read Only") },
167 { PN_PROCESS_MAPPED_EXE_READWRITE
, 0, TEXT("Mapped Space Exec Read/Write") },
168 { PN_PROCESS_MAPPED_EXE_WRITECOPY
, 0, TEXT("Mapped Space Exec Write Copy") },
169 { PN_PROCESS_IMAGE_NOACCESS
, 0, TEXT("Image Space No Access") },
170 { PN_PROCESS_IMAGE_READONLY
, 0, TEXT("Image Space Read Only") },
171 { PN_PROCESS_IMAGE_READWRITE
, 0, TEXT("Image Space Read/Write") },
172 { PN_PROCESS_IMAGE_WRITECOPY
, 0, TEXT("Image Space Write Copy") },
173 { PN_PROCESS_IMAGE_EXECUTABLE
, 0, TEXT("Image Space Executable") },
174 { PN_PROCESS_IMAGE_EXE_READONLY
, 0, TEXT("Image Space Exec Read Only") },
175 { PN_PROCESS_IMAGE_EXE_READWRITE
, 0, TEXT("Image Space Exec Read/Write") },
176 { PN_PROCESS_IMAGE_EXE_WRITECOPY
, 0, TEXT("Image Space Exec Write Copy") },
180 #define NENTRIES ((sizeof(entries) / sizeof(entry_t)) - 1)
183 key_for_index(int key
)
185 entry_t
* entry
= entries
+ NENTRIES
/ 2;
186 unsigned int step
= 64 / 4; // XXX
189 if (key
< entry
->e_key
)
191 else if (key
> entry
->e_key
)
194 if (key
== entry
->e_key
)
195 return entry
->e_index
;
209 HKEY
* begin_assignment() {
218 auto_hkey() : hkey(0) {}
219 ~auto_hkey() { ::RegCloseKey(hkey
); }
221 HKEY
get() const { return hkey
; }
222 operator HKEY() const { return get(); }
225 getter_Acquires(auto_hkey
& hkey
);
229 getter_Acquires(auto_hkey
& hkey
)
231 return hkey
.begin_assignment();
236 get_perf_titles(char*& buffer
, char**& titles
, int& last_title_index
)
240 // Open the perflib key to find out the last counter's index and
242 auto_hkey perflib_hkey
;
243 result
= ::RegOpenKeyEx(HKEY_LOCAL_MACHINE
,
244 TEXT("software\\microsoft\\windows nt\\currentversion\\perflib"),
247 getter_Acquires(perflib_hkey
));
249 if (result
!= ERROR_SUCCESS
)
252 // Get the last counter's index so we know how much memory to
253 // allocate for titles
254 DWORD data_size
= sizeof(DWORD
);
256 result
= ::RegQueryValueEx(perflib_hkey
,
257 TEXT("Last Counter"),
260 reinterpret_cast<BYTE
*>(&last_title_index
),
263 if (result
!= ERROR_SUCCESS
)
266 // Find system version, for system earlier than 1.0a, there's no
269 result
= ::RegQueryValueEx(perflib_hkey
,
273 reinterpret_cast<BYTE
*>(&version
),
276 bool is_nt_10
= (result
== ERROR_SUCCESS
);
278 // Now, get ready for the counter names and indexes.
279 char* counter_value_name
;
280 auto_hkey counter_autohkey
;
283 // NT 1.0, so make hKey2 point to ...\perflib\009 and get
284 // the counters from value "Counters"
285 counter_value_name
= TEXT("Counters");
286 result
= ::RegOpenKeyEx(HKEY_LOCAL_MACHINE
,
287 TEXT("software\\microsoft\\windows nt\\currentversion\\perflib\\009"),
290 getter_Acquires(counter_autohkey
));
292 if (result
!= ERROR_SUCCESS
)
295 counter_hkey
= counter_autohkey
;
298 // NT 1.0a or later. Get the counters in key HKEY_PERFORMANCE_KEY
299 // and from value "Counter 009"
300 counter_value_name
= TEXT("Counter 009");
301 counter_hkey
= HKEY_PERFORMANCE_DATA
;
304 // Find out the size of the data.
305 result
= ::RegQueryValueEx(counter_hkey
,
312 if (result
!= ERROR_SUCCESS
)
316 buffer
= new char[data_size
];
317 titles
= new char*[last_title_index
+ 1];
318 for (int i
= 0; i
<= last_title_index
; ++i
)
322 result
= ::RegQueryValueEx(counter_hkey
,
326 reinterpret_cast<BYTE
*>(buffer
),
328 if (result
!= ERROR_SUCCESS
)
331 // Setup the titles array of pointers to point to beginning of
332 // each title string.
333 char* title
= buffer
;
336 while (len
= lstrlen(title
)) {
337 int index
= atoi(title
);
340 if (index
<= last_title_index
)
341 titles
[index
] = title
;
344 printf("%d=%s\n", index
, title
);
347 title
+= lstrlen(title
) + 1;
350 return ERROR_SUCCESS
;
360 DWORD result
= get_perf_titles(buffer
, titles
, last
);
362 assert(result
== ERROR_SUCCESS
);
364 for (entry_t
* entry
= entries
; entry
->e_key
!= 0; ++entry
) {
365 for (int index
= 0; index
<= last
; ++index
) {
366 if (titles
[index
] && 0 == lstrcmpi(titles
[index
], entry
->e_title
)) {
367 entry
->e_index
= index
;
372 if (entry
->e_index
== 0) {
373 fprintf(stderr
, "warning: unable to find index for ``%s''\n", entry
->e_title
);
384 get_perf_data(HKEY perf_hkey
, char* object_index
, PERF_DATA_BLOCK
** data
, DWORD
* size
)
387 *data
= reinterpret_cast<PERF_DATA_BLOCK
*>(new char[*size
]);
393 DWORD real_size
= *size
;
395 result
= ::RegQueryValueEx(perf_hkey
,
399 reinterpret_cast<BYTE
*>(*data
),
402 if (result
!= ERROR_MORE_DATA
)
407 *data
= reinterpret_cast<PERF_DATA_BLOCK
*>(new char[*size
]);
410 return ERROR_NOT_ENOUGH_MEMORY
;
417 static const PERF_OBJECT_TYPE
*
418 first_object(const PERF_DATA_BLOCK
* data
)
421 ? reinterpret_cast<const PERF_OBJECT_TYPE
*>(reinterpret_cast<const char*>(data
) + data
->HeaderLength
)
425 static const PERF_OBJECT_TYPE
*
426 next_object(const PERF_OBJECT_TYPE
* object
)
429 ? reinterpret_cast<const PERF_OBJECT_TYPE
*>(reinterpret_cast<const char*>(object
) + object
->TotalByteLength
)
433 const PERF_OBJECT_TYPE
*
434 find_object(const PERF_DATA_BLOCK
* data
, DWORD index
)
436 const PERF_OBJECT_TYPE
* object
= first_object(data
);
440 for (int i
= 0; i
< data
->NumObjectTypes
; ++i
) {
441 if (object
->ObjectNameTitleIndex
== index
)
444 object
= next_object(object
);
451 static const PERF_COUNTER_DEFINITION
*
452 first_counter(const PERF_OBJECT_TYPE
* object
)
455 ? reinterpret_cast<const PERF_COUNTER_DEFINITION
*>(reinterpret_cast<const char*>(object
) + object
->HeaderLength
)
459 static const PERF_COUNTER_DEFINITION
*
460 next_counter(const PERF_COUNTER_DEFINITION
* counter
)
463 reinterpret_cast<const PERF_COUNTER_DEFINITION
*>(reinterpret_cast<const char*>(counter
) + counter
->ByteLength
)
468 static const PERF_COUNTER_DEFINITION
*
469 find_counter(const PERF_OBJECT_TYPE
* object
, int index
)
471 const PERF_COUNTER_DEFINITION
* counter
=
472 first_counter(object
);
477 for (int i
; i
< object
->NumCounters
; ++i
) {
478 if (counter
->CounterNameTitleIndex
== index
)
481 counter
= next_counter(counter
);
488 static const PERF_INSTANCE_DEFINITION
*
489 first_instance(const PERF_OBJECT_TYPE
* object
)
492 ? reinterpret_cast<const PERF_INSTANCE_DEFINITION
*>(reinterpret_cast<const char*>(object
) + object
->DefinitionLength
)
497 static const PERF_INSTANCE_DEFINITION
*
498 next_instance(const PERF_INSTANCE_DEFINITION
* instance
)
501 const PERF_COUNTER_BLOCK
* counter_block
=
502 reinterpret_cast<const PERF_COUNTER_BLOCK
*>(reinterpret_cast<const char*>(instance
) + instance
->ByteLength
);
504 return reinterpret_cast<const PERF_INSTANCE_DEFINITION
*>(reinterpret_cast<const char*>(counter_block
) + counter_block
->ByteLength
);
512 static const wchar_t*
513 instance_name(const PERF_INSTANCE_DEFINITION
* instance
)
516 ? reinterpret_cast<const wchar_t*>(reinterpret_cast<const char*>(instance
) + instance
->NameOffset
)
522 counter_data(const PERF_INSTANCE_DEFINITION
* instance
,
523 const PERF_COUNTER_DEFINITION
* counter
)
525 if (counter
&& instance
) {
526 const PERF_COUNTER_BLOCK
* counter_block
;
527 counter_block
= reinterpret_cast<const PERF_COUNTER_BLOCK
*>(reinterpret_cast<const char*>(instance
) + instance
->ByteLength
);
528 return reinterpret_cast<const char*>(counter_block
) + counter
->CounterOffset
;
537 list_process(PERF_DATA_BLOCK
* perf_data
, wchar_t* process_name
)
539 const PERF_OBJECT_TYPE
* process
= find_object(perf_data
, key_for_index(PN_PROCESS
));
540 const PERF_COUNTER_DEFINITION
* working_set
= find_counter(process
, key_for_index(PN_PROCESS_WORKING_SET
));
541 const PERF_COUNTER_DEFINITION
* peak_working_set
= find_counter(process
, key_for_index(PN_PROCESS_PEAK_WS
));
542 const PERF_COUNTER_DEFINITION
* private_page
= find_counter(process
, key_for_index(PN_PROCESS_PRIVATE_PAGE
));
543 const PERF_COUNTER_DEFINITION
* virtual_size
= find_counter(process
, key_for_index(PN_PROCESS_VIRTUAL_SIZE
));
545 const PERF_INSTANCE_DEFINITION
* instance
= first_instance(process
);
550 while (instance
&& index
< process
->NumInstances
) {
551 const wchar_t* name
= instance_name(instance
);
552 if (lstrcmpW(process_name
, name
) == 0) {
553 printf("%d %d %d %d\n",
554 *(static_cast<const int*>(counter_data(instance
, working_set
))),
555 *(static_cast<const int*>(counter_data(instance
, peak_working_set
))),
556 *(static_cast<const int*>(counter_data(instance
, private_page
))),
557 *(static_cast<const int*>(counter_data(instance
, virtual_size
))));
562 instance
= next_instance(instance
);
568 // Dig up address space data.
569 PERF_OBJECT_TYPE
* address_space
= FindObject(costly_data
, PX_PROCESS_ADDRESS_SPACE
);
570 PERF_COUNTER_DEFINITION
* image_executable
= FindCounter(process
, PX_PROCESS_IMAGE_EXECUTABLE
);
571 PERF_COUNTER_DEFINITION
* image_exe_readonly
= FindCounter(process
, PX_PROCESS_IMAGE_EXE_READONLY
);
572 PERF_COUNTER_DEFINITION
* image_exe_readwrite
= FindCounter(process
, PX_PROCESS_IMAGE_EXE_READWRITE
);
573 PERF_COUNTER_DEFINITION
* image_exe_writecopy
= FindCounter(process
, PX_PROCESS_IMAGE_EXE_WRITECOPY
);
582 main(int argc
, char* argv
[])
584 wchar_t process_name
[32];
586 int interval
= 10000; // msec
590 if (argv
[i
][0] != '-')
593 switch (argv
[i
][1]) {
595 interval
= atoi(argv
[++i
]) * 1000;
599 fprintf(stderr
, "unknown option `%c'\n", argv
[i
][1]);
606 wchar_t* q
= process_name
;
607 while (*q
++ = wchar_t(*p
++))
611 fprintf(stderr
, "no image name specified\n");
617 PERF_DATA_BLOCK
* perf_data
= 0;
618 PERF_DATA_BLOCK
* costly_data
= 0;
619 DWORD perf_data_size
= 50 * 1024;
620 DWORD costly_data_size
= 100 * 1024;
624 sprintf(buf
, "%ld %ld",
625 key_for_index(PN_PROCESS
),
626 key_for_index(PN_THREAD
));
628 get_perf_data(HKEY_PERFORMANCE_DATA
, buf
, &perf_data
, &perf_data_size
);
631 sprintf(buf
, "%ld %ld %ld",
632 key_for_index(PN_PROCESS_ADDRESS_SPACE
),
633 key_for_index(PN_IMAGE
),
634 key_for_index(PN_THREAD_DETAILS
));
636 get_perf_data(HKEY_PERFORMANCE_DATA
, buf
, &costly_data
, &costly_data_size
);
639 if (! list_process(perf_data
, process_name
))