tools/footprint/wm.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial