Bug 458256. Use LoadLibraryW instead of LoadLibrary (patch by DougT). r+sr=vlad
[wine-gecko.git] / tools / footprint / wm.cpp
blob232a1583a845488f31cf32a0516c17073ca01a97
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
14 * License.
16 * The Original Code is wm.cpp, released
17 * November 15, 2000.
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.
24 * Contributor(s):
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.
47 #include <assert.h>
48 #include <windows.h>
49 #include <winperf.h>
50 #include <stdio.h>
51 #include <stdlib.h>
53 #define PN_PROCESS 1
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
66 #define PN_THREAD 14
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
77 #define PN_IMAGE 25
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
112 struct entry_t {
113 int e_key;
114 int e_index;
115 char* e_title;
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") },
177 { 0, 0, 0 },
180 #define NENTRIES ((sizeof(entries) / sizeof(entry_t)) - 1)
182 static int
183 key_for_index(int key)
185 entry_t* entry = entries + NENTRIES / 2;
186 unsigned int step = 64 / 4; // XXX
188 while (step) {
189 if (key < entry->e_key)
190 entry -= step;
191 else if (key > entry->e_key)
192 entry += step;
194 if (key == entry->e_key)
195 return entry->e_index;
197 step >>= 1;
200 assert(false);
201 return 0;
205 class auto_hkey {
206 protected:
207 HKEY hkey;
209 HKEY* begin_assignment() {
210 if (hkey) {
211 ::RegCloseKey(hkey);
212 hkey = 0;
214 return &hkey;
217 public:
218 auto_hkey() : hkey(0) {}
219 ~auto_hkey() { ::RegCloseKey(hkey); }
221 HKEY get() const { return hkey; }
222 operator HKEY() const { return get(); }
224 friend HKEY*
225 getter_Acquires(auto_hkey& hkey);
228 static HKEY*
229 getter_Acquires(auto_hkey& hkey)
231 return hkey.begin_assignment();
235 static int
236 get_perf_titles(char*& buffer, char**& titles, int& last_title_index)
238 DWORD result;
240 // Open the perflib key to find out the last counter's index and
241 // system version.
242 auto_hkey perflib_hkey;
243 result = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE,
244 TEXT("software\\microsoft\\windows nt\\currentversion\\perflib"),
246 KEY_READ,
247 getter_Acquires(perflib_hkey));
249 if (result != ERROR_SUCCESS)
250 return result;
252 // Get the last counter's index so we know how much memory to
253 // allocate for titles
254 DWORD data_size = sizeof(DWORD);
255 DWORD type;
256 result = ::RegQueryValueEx(perflib_hkey,
257 TEXT("Last Counter"),
259 &type,
260 reinterpret_cast<BYTE*>(&last_title_index),
261 &data_size);
263 if (result != ERROR_SUCCESS)
264 return result;
266 // Find system version, for system earlier than 1.0a, there's no
267 // version value.
268 int version;
269 result = ::RegQueryValueEx(perflib_hkey,
270 TEXT("Version"),
272 &type,
273 reinterpret_cast<BYTE*>(&version),
274 &data_size);
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;
281 HKEY counter_hkey;
282 if (is_nt_10) {
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"),
289 KEY_READ,
290 getter_Acquires(counter_autohkey));
292 if (result != ERROR_SUCCESS)
293 return result;
295 counter_hkey = counter_autohkey;
297 else {
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,
306 counter_value_name,
308 &type,
310 &data_size);
312 if (result != ERROR_SUCCESS)
313 return result;
315 // Allocate memory
316 buffer = new char[data_size];
317 titles = new char*[last_title_index + 1];
318 for (int i = 0; i <= last_title_index; ++i)
319 titles[i] = 0;
321 // Query the data
322 result = ::RegQueryValueEx(counter_hkey,
323 counter_value_name,
325 &type,
326 reinterpret_cast<BYTE*>(buffer),
327 &data_size);
328 if (result != ERROR_SUCCESS)
329 return result;
331 // Setup the titles array of pointers to point to beginning of
332 // each title string.
333 char* title = buffer;
334 int len;
336 while (len = lstrlen(title)) {
337 int index = atoi(title);
338 title += len + 1;
340 if (index <= last_title_index)
341 titles[index] = title;
343 #ifdef DEBUG
344 printf("%d=%s\n", index, title);
345 #endif
347 title += lstrlen(title) + 1;
350 return ERROR_SUCCESS;
353 static void
354 init_entries()
356 char* buffer;
357 char** titles;
358 int last = 0;
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;
368 break;
372 if (entry->e_index == 0) {
373 fprintf(stderr, "warning: unable to find index for ``%s''\n", entry->e_title);
377 delete[] buffer;
378 delete[] titles;
383 static DWORD
384 get_perf_data(HKEY perf_hkey, char* object_index, PERF_DATA_BLOCK** data, DWORD* size)
386 if (! *data)
387 *data = reinterpret_cast<PERF_DATA_BLOCK*>(new char[*size]);
389 DWORD result;
391 while (1) {
392 DWORD type;
393 DWORD real_size = *size;
395 result = ::RegQueryValueEx(perf_hkey,
396 object_index,
398 &type,
399 reinterpret_cast<BYTE*>(*data),
400 &real_size);
402 if (result != ERROR_MORE_DATA)
403 break;
405 delete[] *data;
406 *size += 1024;
407 *data = reinterpret_cast<PERF_DATA_BLOCK*>(new char[*size]);
409 if (! *data)
410 return ERROR_NOT_ENOUGH_MEMORY;
413 return result;
417 static const PERF_OBJECT_TYPE*
418 first_object(const PERF_DATA_BLOCK* data)
420 return data
421 ? reinterpret_cast<const PERF_OBJECT_TYPE*>(reinterpret_cast<const char*>(data) + data->HeaderLength)
422 : 0;
425 static const PERF_OBJECT_TYPE*
426 next_object(const PERF_OBJECT_TYPE* object)
428 return object
429 ? reinterpret_cast<const PERF_OBJECT_TYPE*>(reinterpret_cast<const char*>(object) + object->TotalByteLength)
430 : 0;
433 const PERF_OBJECT_TYPE*
434 find_object(const PERF_DATA_BLOCK* data, DWORD index)
436 const PERF_OBJECT_TYPE* object = first_object(data);
437 if (! object)
438 return 0;
440 for (int i = 0; i < data->NumObjectTypes; ++i) {
441 if (object->ObjectNameTitleIndex == index)
442 return object;
444 object = next_object(object);
447 return 0;
451 static const PERF_COUNTER_DEFINITION*
452 first_counter(const PERF_OBJECT_TYPE* object)
454 return object
455 ? reinterpret_cast<const PERF_COUNTER_DEFINITION*>(reinterpret_cast<const char*>(object) + object->HeaderLength)
456 : 0;
459 static const PERF_COUNTER_DEFINITION*
460 next_counter(const PERF_COUNTER_DEFINITION* counter)
462 return counter ?
463 reinterpret_cast<const PERF_COUNTER_DEFINITION*>(reinterpret_cast<const char*>(counter) + counter->ByteLength)
464 : 0;
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);
474 if (! counter)
475 return 0;
477 for (int i; i < object->NumCounters; ++i) {
478 if (counter->CounterNameTitleIndex == index)
479 return counter;
481 counter = next_counter(counter);
484 return 0;
488 static const PERF_INSTANCE_DEFINITION*
489 first_instance(const PERF_OBJECT_TYPE* object)
491 return object
492 ? reinterpret_cast<const PERF_INSTANCE_DEFINITION*>(reinterpret_cast<const char*>(object) + object->DefinitionLength)
493 : 0;
497 static const PERF_INSTANCE_DEFINITION*
498 next_instance(const PERF_INSTANCE_DEFINITION* instance)
500 if (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);
506 else {
507 return 0;
512 static const wchar_t*
513 instance_name(const PERF_INSTANCE_DEFINITION* instance)
515 return instance
516 ? reinterpret_cast<const wchar_t*>(reinterpret_cast<const char*>(instance) + instance->NameOffset)
517 : 0;
521 static const void*
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;
530 else {
531 return 0;
536 static bool
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);
546 int index = 0;
548 bool found = false;
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))));
559 found = true;
562 instance = next_instance(instance);
563 ++index;
566 if (found) {
567 #if 0
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);
574 #endif
577 return found;
582 main(int argc, char* argv[])
584 wchar_t process_name[32];
586 int interval = 10000; // msec
588 int i = 0;
589 while (++i < argc) {
590 if (argv[i][0] != '-')
591 break;
593 switch (argv[i][1]) {
594 case 'i':
595 interval = atoi(argv[++i]) * 1000;
596 break;
598 default:
599 fprintf(stderr, "unknown option `%c'\n", argv[i][1]);
600 exit(1);
604 if (argv[i]) {
605 char* p = argv[i];
606 wchar_t* q = process_name;
607 while (*q++ = wchar_t(*p++))
608 continue;
610 else {
611 fprintf(stderr, "no image name specified\n");
612 exit(1);
615 init_entries();
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;
622 do {
623 char buf[64];
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);
630 #if 0
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);
637 #endif
639 if (! list_process(perf_data, process_name))
640 break;
642 _sleep(interval);
643 } while (1);
645 return 0;