michael@0: /* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * This program tracks a process's working memory usage using the michael@0: * ``performance'' entries in the Win32 registry. It borrows from michael@0: * the ``pviewer'' source code in the MS SDK. michael@0: */ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #define PN_PROCESS 1 michael@0: #define PN_PROCESS_CPU 2 michael@0: #define PN_PROCESS_PRIV 3 michael@0: #define PN_PROCESS_USER 4 michael@0: #define PN_PROCESS_WORKING_SET 5 michael@0: #define PN_PROCESS_PEAK_WS 6 michael@0: #define PN_PROCESS_PRIO 7 michael@0: #define PN_PROCESS_ELAPSE 8 michael@0: #define PN_PROCESS_ID 9 michael@0: #define PN_PROCESS_PRIVATE_PAGE 10 michael@0: #define PN_PROCESS_VIRTUAL_SIZE 11 michael@0: #define PN_PROCESS_PEAK_VS 12 michael@0: #define PN_PROCESS_FAULT_COUNT 13 michael@0: #define PN_THREAD 14 michael@0: #define PN_THREAD_CPU 15 michael@0: #define PN_THREAD_PRIV 16 michael@0: #define PN_THREAD_USER 17 michael@0: #define PN_THREAD_START 18 michael@0: #define PN_THREAD_SWITCHES 19 michael@0: #define PN_THREAD_PRIO 20 michael@0: #define PN_THREAD_BASE_PRIO 21 michael@0: #define PN_THREAD_ELAPSE 22 michael@0: #define PN_THREAD_DETAILS 23 michael@0: #define PN_THREAD_PC 24 michael@0: #define PN_IMAGE 25 michael@0: #define PN_IMAGE_NOACCESS 26 michael@0: #define PN_IMAGE_READONLY 27 michael@0: #define PN_IMAGE_READWRITE 28 michael@0: #define PN_IMAGE_WRITECOPY 29 michael@0: #define PN_IMAGE_EXECUTABLE 30 michael@0: #define PN_IMAGE_EXE_READONLY 31 michael@0: #define PN_IMAGE_EXE_READWRITE 32 michael@0: #define PN_IMAGE_EXE_WRITECOPY 33 michael@0: #define PN_PROCESS_ADDRESS_SPACE 34 michael@0: #define PN_PROCESS_PRIVATE_NOACCESS 35 michael@0: #define PN_PROCESS_PRIVATE_READONLY 36 michael@0: #define PN_PROCESS_PRIVATE_READWRITE 37 michael@0: #define PN_PROCESS_PRIVATE_WRITECOPY 38 michael@0: #define PN_PROCESS_PRIVATE_EXECUTABLE 39 michael@0: #define PN_PROCESS_PRIVATE_EXE_READONLY 40 michael@0: #define PN_PROCESS_PRIVATE_EXE_READWRITE 41 michael@0: #define PN_PROCESS_PRIVATE_EXE_WRITECOPY 42 michael@0: #define PN_PROCESS_MAPPED_NOACCESS 43 michael@0: #define PN_PROCESS_MAPPED_READONLY 44 michael@0: #define PN_PROCESS_MAPPED_READWRITE 45 michael@0: #define PN_PROCESS_MAPPED_WRITECOPY 46 michael@0: #define PN_PROCESS_MAPPED_EXECUTABLE 47 michael@0: #define PN_PROCESS_MAPPED_EXE_READONLY 48 michael@0: #define PN_PROCESS_MAPPED_EXE_READWRITE 49 michael@0: #define PN_PROCESS_MAPPED_EXE_WRITECOPY 50 michael@0: #define PN_PROCESS_IMAGE_NOACCESS 51 michael@0: #define PN_PROCESS_IMAGE_READONLY 52 michael@0: #define PN_PROCESS_IMAGE_READWRITE 53 michael@0: #define PN_PROCESS_IMAGE_WRITECOPY 54 michael@0: #define PN_PROCESS_IMAGE_EXECUTABLE 55 michael@0: #define PN_PROCESS_IMAGE_EXE_READONLY 56 michael@0: #define PN_PROCESS_IMAGE_EXE_READWRITE 57 michael@0: #define PN_PROCESS_IMAGE_EXE_WRITECOPY 58 michael@0: michael@0: struct entry_t { michael@0: int e_key; michael@0: int e_index; michael@0: char* e_title; michael@0: }; michael@0: michael@0: entry_t entries[] = { michael@0: { PN_PROCESS, 0, TEXT("Process") }, michael@0: { PN_PROCESS_CPU, 0, TEXT("% Processor Time") }, michael@0: { PN_PROCESS_PRIV, 0, TEXT("% Privileged Time") }, michael@0: { PN_PROCESS_USER, 0, TEXT("% User Time") }, michael@0: { PN_PROCESS_WORKING_SET, 0, TEXT("Working Set") }, michael@0: { PN_PROCESS_PEAK_WS, 0, TEXT("Working Set Peak") }, michael@0: { PN_PROCESS_PRIO, 0, TEXT("Priority Base") }, michael@0: { PN_PROCESS_ELAPSE, 0, TEXT("Elapsed Time") }, michael@0: { PN_PROCESS_ID, 0, TEXT("ID Process") }, michael@0: { PN_PROCESS_PRIVATE_PAGE, 0, TEXT("Private Bytes") }, michael@0: { PN_PROCESS_VIRTUAL_SIZE, 0, TEXT("Virtual Bytes") }, michael@0: { PN_PROCESS_PEAK_VS, 0, TEXT("Virtual Bytes Peak") }, michael@0: { PN_PROCESS_FAULT_COUNT, 0, TEXT("Page Faults/sec") }, michael@0: { PN_THREAD, 0, TEXT("Thread") }, michael@0: { PN_THREAD_CPU, 0, TEXT("% Processor Time") }, michael@0: { PN_THREAD_PRIV, 0, TEXT("% Privileged Time") }, michael@0: { PN_THREAD_USER, 0, TEXT("% User Time") }, michael@0: { PN_THREAD_START, 0, TEXT("Start Address") }, michael@0: { PN_THREAD_SWITCHES, 0, TEXT("Con0, TEXT Switches/sec") }, michael@0: { PN_THREAD_PRIO, 0, TEXT("Priority Current") }, michael@0: { PN_THREAD_BASE_PRIO, 0, TEXT("Priority Base") }, michael@0: { PN_THREAD_ELAPSE, 0, TEXT("Elapsed Time") }, michael@0: { PN_THREAD_DETAILS, 0, TEXT("Thread Details") }, michael@0: { PN_THREAD_PC, 0, TEXT("User PC") }, michael@0: { PN_IMAGE, 0, TEXT("Image") }, michael@0: { PN_IMAGE_NOACCESS, 0, TEXT("No Access") }, michael@0: { PN_IMAGE_READONLY, 0, TEXT("Read Only") }, michael@0: { PN_IMAGE_READWRITE, 0, TEXT("Read/Write") }, michael@0: { PN_IMAGE_WRITECOPY, 0, TEXT("Write Copy") }, michael@0: { PN_IMAGE_EXECUTABLE, 0, TEXT("Executable") }, michael@0: { PN_IMAGE_EXE_READONLY, 0, TEXT("Exec Read Only") }, michael@0: { PN_IMAGE_EXE_READWRITE, 0, TEXT("Exec Read/Write") }, michael@0: { PN_IMAGE_EXE_WRITECOPY, 0, TEXT("Exec Write Copy") }, michael@0: { PN_PROCESS_ADDRESS_SPACE, 0, TEXT("Process Address Space") }, michael@0: { PN_PROCESS_PRIVATE_NOACCESS, 0, TEXT("Reserved Space No Access") }, michael@0: { PN_PROCESS_PRIVATE_READONLY, 0, TEXT("Reserved Space Read Only") }, michael@0: { PN_PROCESS_PRIVATE_READWRITE, 0, TEXT("Reserved Space Read/Write") }, michael@0: { PN_PROCESS_PRIVATE_WRITECOPY, 0, TEXT("Reserved Space Write Copy") }, michael@0: { PN_PROCESS_PRIVATE_EXECUTABLE, 0, TEXT("Reserved Space Executable") }, michael@0: { PN_PROCESS_PRIVATE_EXE_READONLY, 0, TEXT("Reserved Space Exec Read Only") }, michael@0: { PN_PROCESS_PRIVATE_EXE_READWRITE, 0, TEXT("Reserved Space Exec Read/Write") }, michael@0: { PN_PROCESS_PRIVATE_EXE_WRITECOPY, 0, TEXT("Reserved Space Exec Write Copy") }, michael@0: { PN_PROCESS_MAPPED_NOACCESS, 0, TEXT("Mapped Space No Access") }, michael@0: { PN_PROCESS_MAPPED_READONLY, 0, TEXT("Mapped Space Read Only") }, michael@0: { PN_PROCESS_MAPPED_READWRITE, 0, TEXT("Mapped Space Read/Write") }, michael@0: { PN_PROCESS_MAPPED_WRITECOPY, 0, TEXT("Mapped Space Write Copy") }, michael@0: { PN_PROCESS_MAPPED_EXECUTABLE, 0, TEXT("Mapped Space Executable") }, michael@0: { PN_PROCESS_MAPPED_EXE_READONLY, 0, TEXT("Mapped Space Exec Read Only") }, michael@0: { PN_PROCESS_MAPPED_EXE_READWRITE, 0, TEXT("Mapped Space Exec Read/Write") }, michael@0: { PN_PROCESS_MAPPED_EXE_WRITECOPY, 0, TEXT("Mapped Space Exec Write Copy") }, michael@0: { PN_PROCESS_IMAGE_NOACCESS, 0, TEXT("Image Space No Access") }, michael@0: { PN_PROCESS_IMAGE_READONLY, 0, TEXT("Image Space Read Only") }, michael@0: { PN_PROCESS_IMAGE_READWRITE, 0, TEXT("Image Space Read/Write") }, michael@0: { PN_PROCESS_IMAGE_WRITECOPY, 0, TEXT("Image Space Write Copy") }, michael@0: { PN_PROCESS_IMAGE_EXECUTABLE, 0, TEXT("Image Space Executable") }, michael@0: { PN_PROCESS_IMAGE_EXE_READONLY, 0, TEXT("Image Space Exec Read Only") }, michael@0: { PN_PROCESS_IMAGE_EXE_READWRITE, 0, TEXT("Image Space Exec Read/Write") }, michael@0: { PN_PROCESS_IMAGE_EXE_WRITECOPY, 0, TEXT("Image Space Exec Write Copy") }, michael@0: { 0, 0, 0 }, michael@0: }; michael@0: michael@0: #define NENTRIES ((sizeof(entries) / sizeof(entry_t)) - 1) michael@0: michael@0: static int michael@0: key_for_index(int key) michael@0: { michael@0: entry_t* entry = entries + NENTRIES / 2; michael@0: unsigned int step = 64 / 4; // XXX michael@0: michael@0: while (step) { michael@0: if (key < entry->e_key) michael@0: entry -= step; michael@0: else if (key > entry->e_key) michael@0: entry += step; michael@0: michael@0: if (key == entry->e_key) michael@0: return entry->e_index; michael@0: michael@0: step >>= 1; michael@0: } michael@0: michael@0: assert(false); michael@0: return 0; michael@0: } michael@0: michael@0: michael@0: class auto_hkey { michael@0: protected: michael@0: HKEY hkey; michael@0: michael@0: HKEY* begin_assignment() { michael@0: if (hkey) { michael@0: ::RegCloseKey(hkey); michael@0: hkey = 0; michael@0: } michael@0: return &hkey; michael@0: } michael@0: michael@0: public: michael@0: auto_hkey() : hkey(0) {} michael@0: ~auto_hkey() { ::RegCloseKey(hkey); } michael@0: michael@0: HKEY get() const { return hkey; } michael@0: operator HKEY() const { return get(); } michael@0: michael@0: friend HKEY* michael@0: getter_Acquires(auto_hkey& hkey); michael@0: }; michael@0: michael@0: static HKEY* michael@0: getter_Acquires(auto_hkey& hkey) michael@0: { michael@0: return hkey.begin_assignment(); michael@0: } michael@0: michael@0: michael@0: static int michael@0: get_perf_titles(char*& buffer, char**& titles, int& last_title_index) michael@0: { michael@0: DWORD result; michael@0: michael@0: // Open the perflib key to find out the last counter's index and michael@0: // system version. michael@0: auto_hkey perflib_hkey; michael@0: result = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, michael@0: TEXT("software\\microsoft\\windows nt\\currentversion\\perflib"), michael@0: 0, michael@0: KEY_READ, michael@0: getter_Acquires(perflib_hkey)); michael@0: michael@0: if (result != ERROR_SUCCESS) michael@0: return result; michael@0: michael@0: // Get the last counter's index so we know how much memory to michael@0: // allocate for titles michael@0: DWORD data_size = sizeof(DWORD); michael@0: DWORD type; michael@0: result = ::RegQueryValueEx(perflib_hkey, michael@0: TEXT("Last Counter"), michael@0: 0, michael@0: &type, michael@0: reinterpret_cast(&last_title_index), michael@0: &data_size); michael@0: michael@0: if (result != ERROR_SUCCESS) michael@0: return result; michael@0: michael@0: // Find system version, for system earlier than 1.0a, there's no michael@0: // version value. michael@0: int version; michael@0: result = ::RegQueryValueEx(perflib_hkey, michael@0: TEXT("Version"), michael@0: 0, michael@0: &type, michael@0: reinterpret_cast(&version), michael@0: &data_size); michael@0: michael@0: bool is_nt_10 = (result == ERROR_SUCCESS); michael@0: michael@0: // Now, get ready for the counter names and indexes. michael@0: char* counter_value_name; michael@0: auto_hkey counter_autohkey; michael@0: HKEY counter_hkey; michael@0: if (is_nt_10) { michael@0: // NT 1.0, so make hKey2 point to ...\perflib\009 and get michael@0: // the counters from value "Counters" michael@0: counter_value_name = TEXT("Counters"); michael@0: result = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, michael@0: TEXT("software\\microsoft\\windows nt\\currentversion\\perflib\\009"), michael@0: 0, michael@0: KEY_READ, michael@0: getter_Acquires(counter_autohkey)); michael@0: michael@0: if (result != ERROR_SUCCESS) michael@0: return result; michael@0: michael@0: counter_hkey = counter_autohkey; michael@0: } michael@0: else { michael@0: // NT 1.0a or later. Get the counters in key HKEY_PERFORMANCE_KEY michael@0: // and from value "Counter 009" michael@0: counter_value_name = TEXT("Counter 009"); michael@0: counter_hkey = HKEY_PERFORMANCE_DATA; michael@0: } michael@0: michael@0: // Find out the size of the data. michael@0: result = ::RegQueryValueEx(counter_hkey, michael@0: counter_value_name, michael@0: 0, michael@0: &type, michael@0: 0, michael@0: &data_size); michael@0: michael@0: if (result != ERROR_SUCCESS) michael@0: return result; michael@0: michael@0: // Allocate memory michael@0: buffer = new char[data_size]; michael@0: titles = new char*[last_title_index + 1]; michael@0: for (int i = 0; i <= last_title_index; ++i) michael@0: titles[i] = 0; michael@0: michael@0: // Query the data michael@0: result = ::RegQueryValueEx(counter_hkey, michael@0: counter_value_name, michael@0: 0, michael@0: &type, michael@0: reinterpret_cast(buffer), michael@0: &data_size); michael@0: if (result != ERROR_SUCCESS) michael@0: return result; michael@0: michael@0: // Setup the titles array of pointers to point to beginning of michael@0: // each title string. michael@0: char* title = buffer; michael@0: int len; michael@0: michael@0: while (len = lstrlen(title)) { michael@0: int index = atoi(title); michael@0: title += len + 1; michael@0: michael@0: if (index <= last_title_index) michael@0: titles[index] = title; michael@0: michael@0: #ifdef DEBUG michael@0: printf("%d=%s\n", index, title); michael@0: #endif michael@0: michael@0: title += lstrlen(title) + 1; michael@0: } michael@0: michael@0: return ERROR_SUCCESS; michael@0: } michael@0: michael@0: static void michael@0: init_entries() michael@0: { michael@0: char* buffer; michael@0: char** titles; michael@0: int last = 0; michael@0: michael@0: DWORD result = get_perf_titles(buffer, titles, last); michael@0: michael@0: assert(result == ERROR_SUCCESS); michael@0: michael@0: for (entry_t* entry = entries; entry->e_key != 0; ++entry) { michael@0: for (int index = 0; index <= last; ++index) { michael@0: if (titles[index] && 0 == lstrcmpi(titles[index], entry->e_title)) { michael@0: entry->e_index = index; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: if (entry->e_index == 0) { michael@0: fprintf(stderr, "warning: unable to find index for ``%s''\n", entry->e_title); michael@0: } michael@0: } michael@0: michael@0: delete[] buffer; michael@0: delete[] titles; michael@0: } michael@0: michael@0: michael@0: michael@0: static DWORD michael@0: get_perf_data(HKEY perf_hkey, char* object_index, PERF_DATA_BLOCK** data, DWORD* size) michael@0: { michael@0: if (! *data) michael@0: *data = reinterpret_cast(new char[*size]); michael@0: michael@0: DWORD result; michael@0: michael@0: while (1) { michael@0: DWORD type; michael@0: DWORD real_size = *size; michael@0: michael@0: result = ::RegQueryValueEx(perf_hkey, michael@0: object_index, michael@0: 0, michael@0: &type, michael@0: reinterpret_cast(*data), michael@0: &real_size); michael@0: michael@0: if (result != ERROR_MORE_DATA) michael@0: break; michael@0: michael@0: delete[] *data; michael@0: *size += 1024; michael@0: *data = reinterpret_cast(new char[*size]); michael@0: michael@0: if (! *data) michael@0: return ERROR_NOT_ENOUGH_MEMORY; michael@0: } michael@0: michael@0: return result; michael@0: } michael@0: michael@0: michael@0: static const PERF_OBJECT_TYPE* michael@0: first_object(const PERF_DATA_BLOCK* data) michael@0: { michael@0: return data michael@0: ? reinterpret_cast(reinterpret_cast(data) + data->HeaderLength) michael@0: : 0; michael@0: } michael@0: michael@0: static const PERF_OBJECT_TYPE* michael@0: next_object(const PERF_OBJECT_TYPE* object) michael@0: { michael@0: return object michael@0: ? reinterpret_cast(reinterpret_cast(object) + object->TotalByteLength) michael@0: : 0; michael@0: } michael@0: michael@0: const PERF_OBJECT_TYPE* michael@0: find_object(const PERF_DATA_BLOCK* data, DWORD index) michael@0: { michael@0: const PERF_OBJECT_TYPE* object = first_object(data); michael@0: if (! object) michael@0: return 0; michael@0: michael@0: for (int i = 0; i < data->NumObjectTypes; ++i) { michael@0: if (object->ObjectNameTitleIndex == index) michael@0: return object; michael@0: michael@0: object = next_object(object); michael@0: } michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: michael@0: static const PERF_COUNTER_DEFINITION* michael@0: first_counter(const PERF_OBJECT_TYPE* object) michael@0: { michael@0: return object michael@0: ? reinterpret_cast(reinterpret_cast(object) + object->HeaderLength) michael@0: : 0; michael@0: } michael@0: michael@0: static const PERF_COUNTER_DEFINITION* michael@0: next_counter(const PERF_COUNTER_DEFINITION* counter) michael@0: { michael@0: return counter ? michael@0: reinterpret_cast(reinterpret_cast(counter) + counter->ByteLength) michael@0: : 0; michael@0: } michael@0: michael@0: michael@0: static const PERF_COUNTER_DEFINITION* michael@0: find_counter(const PERF_OBJECT_TYPE* object, int index) michael@0: { michael@0: const PERF_COUNTER_DEFINITION* counter = michael@0: first_counter(object); michael@0: michael@0: if (! counter) michael@0: return 0; michael@0: michael@0: for (int i; i < object->NumCounters; ++i) { michael@0: if (counter->CounterNameTitleIndex == index) michael@0: return counter; michael@0: michael@0: counter = next_counter(counter); michael@0: } michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: michael@0: static const PERF_INSTANCE_DEFINITION* michael@0: first_instance(const PERF_OBJECT_TYPE* object) michael@0: { michael@0: return object michael@0: ? reinterpret_cast(reinterpret_cast(object) + object->DefinitionLength) michael@0: : 0; michael@0: } michael@0: michael@0: michael@0: static const PERF_INSTANCE_DEFINITION* michael@0: next_instance(const PERF_INSTANCE_DEFINITION* instance) michael@0: { michael@0: if (instance) { michael@0: const PERF_COUNTER_BLOCK* counter_block = michael@0: reinterpret_cast(reinterpret_cast(instance) + instance->ByteLength); michael@0: michael@0: return reinterpret_cast(reinterpret_cast(counter_block) + counter_block->ByteLength); michael@0: } michael@0: else { michael@0: return 0; michael@0: } michael@0: } michael@0: michael@0: michael@0: static const wchar_t* michael@0: instance_name(const PERF_INSTANCE_DEFINITION* instance) michael@0: { michael@0: return instance michael@0: ? reinterpret_cast(reinterpret_cast(instance) + instance->NameOffset) michael@0: : 0; michael@0: } michael@0: michael@0: michael@0: static const void* michael@0: counter_data(const PERF_INSTANCE_DEFINITION* instance, michael@0: const PERF_COUNTER_DEFINITION* counter) michael@0: { michael@0: if (counter && instance) { michael@0: const PERF_COUNTER_BLOCK* counter_block; michael@0: counter_block = reinterpret_cast(reinterpret_cast(instance) + instance->ByteLength); michael@0: return reinterpret_cast(counter_block) + counter->CounterOffset; michael@0: } michael@0: else { michael@0: return 0; michael@0: } michael@0: } michael@0: michael@0: michael@0: static bool michael@0: list_process(PERF_DATA_BLOCK* perf_data, wchar_t* process_name) michael@0: { michael@0: const PERF_OBJECT_TYPE* process = find_object(perf_data, key_for_index(PN_PROCESS)); michael@0: const PERF_COUNTER_DEFINITION* working_set = find_counter(process, key_for_index(PN_PROCESS_WORKING_SET)); michael@0: const PERF_COUNTER_DEFINITION* peak_working_set = find_counter(process, key_for_index(PN_PROCESS_PEAK_WS)); michael@0: const PERF_COUNTER_DEFINITION* private_page = find_counter(process, key_for_index(PN_PROCESS_PRIVATE_PAGE)); michael@0: const PERF_COUNTER_DEFINITION* virtual_size = find_counter(process, key_for_index(PN_PROCESS_VIRTUAL_SIZE)); michael@0: michael@0: const PERF_INSTANCE_DEFINITION* instance = first_instance(process); michael@0: int index = 0; michael@0: michael@0: bool found = false; michael@0: michael@0: while (instance && index < process->NumInstances) { michael@0: const wchar_t* name = instance_name(instance); michael@0: if (lstrcmpW(process_name, name) == 0) { michael@0: printf("%d %d %d %d\n", michael@0: *(static_cast(counter_data(instance, working_set))), michael@0: *(static_cast(counter_data(instance, peak_working_set))), michael@0: *(static_cast(counter_data(instance, private_page))), michael@0: *(static_cast(counter_data(instance, virtual_size)))); michael@0: michael@0: found = true; michael@0: } michael@0: michael@0: instance = next_instance(instance); michael@0: ++index; michael@0: } michael@0: michael@0: if (found) { michael@0: #if 0 michael@0: // Dig up address space data. michael@0: PERF_OBJECT_TYPE* address_space = FindObject(costly_data, PX_PROCESS_ADDRESS_SPACE); michael@0: PERF_COUNTER_DEFINITION* image_executable = FindCounter(process, PX_PROCESS_IMAGE_EXECUTABLE); michael@0: PERF_COUNTER_DEFINITION* image_exe_readonly = FindCounter(process, PX_PROCESS_IMAGE_EXE_READONLY); michael@0: PERF_COUNTER_DEFINITION* image_exe_readwrite = FindCounter(process, PX_PROCESS_IMAGE_EXE_READWRITE); michael@0: PERF_COUNTER_DEFINITION* image_exe_writecopy = FindCounter(process, PX_PROCESS_IMAGE_EXE_WRITECOPY); michael@0: #endif michael@0: } michael@0: michael@0: return found; michael@0: } michael@0: michael@0: michael@0: int michael@0: main(int argc, char* argv[]) michael@0: { michael@0: wchar_t process_name[32]; michael@0: michael@0: int interval = 10000; // msec michael@0: michael@0: int i = 0; michael@0: while (++i < argc) { michael@0: if (argv[i][0] != '-') michael@0: break; michael@0: michael@0: switch (argv[i][1]) { michael@0: case 'i': michael@0: interval = atoi(argv[++i]) * 1000; michael@0: break; michael@0: michael@0: default: michael@0: fprintf(stderr, "unknown option `%c'\n", argv[i][1]); michael@0: exit(1); michael@0: } michael@0: } michael@0: michael@0: if (argv[i]) { michael@0: char* p = argv[i]; michael@0: wchar_t* q = process_name; michael@0: while (*q++ = wchar_t(*p++)) michael@0: continue; michael@0: } michael@0: else { michael@0: fprintf(stderr, "no image name specified\n"); michael@0: exit(1); michael@0: } michael@0: michael@0: init_entries(); michael@0: michael@0: PERF_DATA_BLOCK* perf_data = 0; michael@0: PERF_DATA_BLOCK* costly_data = 0; michael@0: DWORD perf_data_size = 50 * 1024; michael@0: DWORD costly_data_size = 100 * 1024; michael@0: michael@0: do { michael@0: char buf[64]; michael@0: sprintf(buf, "%ld %ld", michael@0: key_for_index(PN_PROCESS), michael@0: key_for_index(PN_THREAD)); michael@0: michael@0: get_perf_data(HKEY_PERFORMANCE_DATA, buf, &perf_data, &perf_data_size); michael@0: michael@0: #if 0 michael@0: sprintf(buf, "%ld %ld %ld", michael@0: key_for_index(PN_PROCESS_ADDRESS_SPACE), michael@0: key_for_index(PN_IMAGE), michael@0: key_for_index(PN_THREAD_DETAILS)); michael@0: michael@0: get_perf_data(HKEY_PERFORMANCE_DATA, buf, &costly_data, &costly_data_size); michael@0: #endif michael@0: michael@0: if (! list_process(perf_data, process_name)) michael@0: break; michael@0: michael@0: _sleep(interval); michael@0: } while (1); michael@0: michael@0: return 0; michael@0: } michael@0: