First checked in.
git-svn-id: svn://10.0.0.236/trunk@82675 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
parent
3068abcb1c
commit
83f2806d96
@ -49,7 +49,11 @@
|
||||
# You can tweak ``win32.gnuplot.in'' to change the graph's output.
|
||||
#
|
||||
# You should use this with ``make --unix'' (which will use
|
||||
# sh.exe instead of cmd.exe to process commands).
|
||||
# sh.exe instead of cmd.exe to process commands); e.g.,
|
||||
#
|
||||
# make --unix -f win32-gdf.mk \
|
||||
# PROGRAM=winEmbed \
|
||||
# BUSTER_URL="http://localhost/cgi-bin/buster.cgi?refresh=10"
|
||||
#
|
||||
# What You'll Need
|
||||
# ----------------
|
||||
@ -115,7 +119,11 @@ mozilla-ws.dat: mozilla.dat
|
||||
mozilla-pws.dat: mozilla.dat
|
||||
awk '{ print NR, $$2 / 1024; }' $? > $@
|
||||
|
||||
# Build ``wm.exe'', the memory spy
|
||||
wm.exe: wm.cpp
|
||||
cl wm.cpp advapi32.lib
|
||||
|
||||
# Clean up the mess.
|
||||
clean:
|
||||
rm -f *-gdf.png *~
|
||||
rm -f wm.exe *-gdf.png *~
|
||||
|
||||
|
||||
625
mozilla/tools/footprint/wm.cpp
Normal file
625
mozilla/tools/footprint/wm.cpp
Normal file
@ -0,0 +1,625 @@
|
||||
/* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public
|
||||
* License Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License. You may obtain a copy of
|
||||
* the License at http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS
|
||||
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express oqr
|
||||
* implied. See the License for the specific language governing
|
||||
* rights and limitations under the License.
|
||||
*
|
||||
* The Original Code is wm.cpp, released November 15, 2000.
|
||||
*
|
||||
* The Initial Developer of the Original Code is Netscape
|
||||
* Communications Corporation. Portions created by Netscape are
|
||||
* Copyright (C) 2000 Netscape Communications Corporation. All
|
||||
* Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* Chris Waterson <waterson@netscape.com>
|
||||
*
|
||||
*
|
||||
* This program tracks a process's working memory usage using the
|
||||
* ``performance'' entries in the Win32 registry. It borrows from
|
||||
* the ``pviewer'' source code in the MS SDK.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <windows.h>
|
||||
#include <winperf.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define PN_PROCESS 1
|
||||
#define PN_PROCESS_CPU 2
|
||||
#define PN_PROCESS_PRIV 3
|
||||
#define PN_PROCESS_USER 4
|
||||
#define PN_PROCESS_WORKING_SET 5
|
||||
#define PN_PROCESS_PEAK_WS 6
|
||||
#define PN_PROCESS_PRIO 7
|
||||
#define PN_PROCESS_ELAPSE 8
|
||||
#define PN_PROCESS_ID 9
|
||||
#define PN_PROCESS_PRIVATE_PAGE 10
|
||||
#define PN_PROCESS_VIRTUAL_SIZE 11
|
||||
#define PN_PROCESS_PEAK_VS 12
|
||||
#define PN_PROCESS_FAULT_COUNT 13
|
||||
#define PN_THREAD 14
|
||||
#define PN_THREAD_CPU 15
|
||||
#define PN_THREAD_PRIV 16
|
||||
#define PN_THREAD_USER 17
|
||||
#define PN_THREAD_START 18
|
||||
#define PN_THREAD_SWITCHES 19
|
||||
#define PN_THREAD_PRIO 20
|
||||
#define PN_THREAD_BASE_PRIO 21
|
||||
#define PN_THREAD_ELAPSE 22
|
||||
#define PN_THREAD_DETAILS 23
|
||||
#define PN_THREAD_PC 24
|
||||
#define PN_IMAGE 25
|
||||
#define PN_IMAGE_NOACCESS 26
|
||||
#define PN_IMAGE_READONLY 27
|
||||
#define PN_IMAGE_READWRITE 28
|
||||
#define PN_IMAGE_WRITECOPY 29
|
||||
#define PN_IMAGE_EXECUTABLE 30
|
||||
#define PN_IMAGE_EXE_READONLY 31
|
||||
#define PN_IMAGE_EXE_READWRITE 32
|
||||
#define PN_IMAGE_EXE_WRITECOPY 33
|
||||
#define PN_PROCESS_ADDRESS_SPACE 34
|
||||
#define PN_PROCESS_PRIVATE_NOACCESS 35
|
||||
#define PN_PROCESS_PRIVATE_READONLY 36
|
||||
#define PN_PROCESS_PRIVATE_READWRITE 37
|
||||
#define PN_PROCESS_PRIVATE_WRITECOPY 38
|
||||
#define PN_PROCESS_PRIVATE_EXECUTABLE 39
|
||||
#define PN_PROCESS_PRIVATE_EXE_READONLY 40
|
||||
#define PN_PROCESS_PRIVATE_EXE_READWRITE 41
|
||||
#define PN_PROCESS_PRIVATE_EXE_WRITECOPY 42
|
||||
#define PN_PROCESS_MAPPED_NOACCESS 43
|
||||
#define PN_PROCESS_MAPPED_READONLY 44
|
||||
#define PN_PROCESS_MAPPED_READWRITE 45
|
||||
#define PN_PROCESS_MAPPED_WRITECOPY 46
|
||||
#define PN_PROCESS_MAPPED_EXECUTABLE 47
|
||||
#define PN_PROCESS_MAPPED_EXE_READONLY 48
|
||||
#define PN_PROCESS_MAPPED_EXE_READWRITE 49
|
||||
#define PN_PROCESS_MAPPED_EXE_WRITECOPY 50
|
||||
#define PN_PROCESS_IMAGE_NOACCESS 51
|
||||
#define PN_PROCESS_IMAGE_READONLY 52
|
||||
#define PN_PROCESS_IMAGE_READWRITE 53
|
||||
#define PN_PROCESS_IMAGE_WRITECOPY 54
|
||||
#define PN_PROCESS_IMAGE_EXECUTABLE 55
|
||||
#define PN_PROCESS_IMAGE_EXE_READONLY 56
|
||||
#define PN_PROCESS_IMAGE_EXE_READWRITE 57
|
||||
#define PN_PROCESS_IMAGE_EXE_WRITECOPY 58
|
||||
|
||||
struct entry_t {
|
||||
int e_key;
|
||||
int e_index;
|
||||
char* e_title;
|
||||
};
|
||||
|
||||
entry_t entries[] = {
|
||||
{ PN_PROCESS, 0, TEXT("Process") },
|
||||
{ PN_PROCESS_CPU, 0, TEXT("% Processor Time") },
|
||||
{ PN_PROCESS_PRIV, 0, TEXT("% Privileged Time") },
|
||||
{ PN_PROCESS_USER, 0, TEXT("% User Time") },
|
||||
{ PN_PROCESS_WORKING_SET, 0, TEXT("Working Set") },
|
||||
{ PN_PROCESS_PEAK_WS, 0, TEXT("Working Set Peak") },
|
||||
{ PN_PROCESS_PRIO, 0, TEXT("Priority Base") },
|
||||
{ PN_PROCESS_ELAPSE, 0, TEXT("Elapsed Time") },
|
||||
{ PN_PROCESS_ID, 0, TEXT("ID Process") },
|
||||
{ PN_PROCESS_PRIVATE_PAGE, 0, TEXT("Private Bytes") },
|
||||
{ PN_PROCESS_VIRTUAL_SIZE, 0, TEXT("Virtual Bytes") },
|
||||
{ PN_PROCESS_PEAK_VS, 0, TEXT("Virtual Bytes Peak") },
|
||||
{ PN_PROCESS_FAULT_COUNT, 0, TEXT("Page Faults/sec") },
|
||||
{ PN_THREAD, 0, TEXT("Thread") },
|
||||
{ PN_THREAD_CPU, 0, TEXT("% Processor Time") },
|
||||
{ PN_THREAD_PRIV, 0, TEXT("% Privileged Time") },
|
||||
{ PN_THREAD_USER, 0, TEXT("% User Time") },
|
||||
{ PN_THREAD_START, 0, TEXT("Start Address") },
|
||||
{ PN_THREAD_SWITCHES, 0, TEXT("Con0, TEXT Switches/sec") },
|
||||
{ PN_THREAD_PRIO, 0, TEXT("Priority Current") },
|
||||
{ PN_THREAD_BASE_PRIO, 0, TEXT("Priority Base") },
|
||||
{ PN_THREAD_ELAPSE, 0, TEXT("Elapsed Time") },
|
||||
{ PN_THREAD_DETAILS, 0, TEXT("Thread Details") },
|
||||
{ PN_THREAD_PC, 0, TEXT("User PC") },
|
||||
{ PN_IMAGE, 0, TEXT("Image") },
|
||||
{ PN_IMAGE_NOACCESS, 0, TEXT("No Access") },
|
||||
{ PN_IMAGE_READONLY, 0, TEXT("Read Only") },
|
||||
{ PN_IMAGE_READWRITE, 0, TEXT("Read/Write") },
|
||||
{ PN_IMAGE_WRITECOPY, 0, TEXT("Write Copy") },
|
||||
{ PN_IMAGE_EXECUTABLE, 0, TEXT("Executable") },
|
||||
{ PN_IMAGE_EXE_READONLY, 0, TEXT("Exec Read Only") },
|
||||
{ PN_IMAGE_EXE_READWRITE, 0, TEXT("Exec Read/Write") },
|
||||
{ PN_IMAGE_EXE_WRITECOPY, 0, TEXT("Exec Write Copy") },
|
||||
{ PN_PROCESS_ADDRESS_SPACE, 0, TEXT("Process Address Space") },
|
||||
{ PN_PROCESS_PRIVATE_NOACCESS, 0, TEXT("Reserved Space No Access") },
|
||||
{ PN_PROCESS_PRIVATE_READONLY, 0, TEXT("Reserved Space Read Only") },
|
||||
{ PN_PROCESS_PRIVATE_READWRITE, 0, TEXT("Reserved Space Read/Write") },
|
||||
{ PN_PROCESS_PRIVATE_WRITECOPY, 0, TEXT("Reserved Space Write Copy") },
|
||||
{ PN_PROCESS_PRIVATE_EXECUTABLE, 0, TEXT("Reserved Space Executable") },
|
||||
{ PN_PROCESS_PRIVATE_EXE_READONLY, 0, TEXT("Reserved Space Exec Read Only") },
|
||||
{ PN_PROCESS_PRIVATE_EXE_READWRITE, 0, TEXT("Reserved Space Exec Read/Write") },
|
||||
{ PN_PROCESS_PRIVATE_EXE_WRITECOPY, 0, TEXT("Reserved Space Exec Write Copy") },
|
||||
{ PN_PROCESS_MAPPED_NOACCESS, 0, TEXT("Mapped Space No Access") },
|
||||
{ PN_PROCESS_MAPPED_READONLY, 0, TEXT("Mapped Space Read Only") },
|
||||
{ PN_PROCESS_MAPPED_READWRITE, 0, TEXT("Mapped Space Read/Write") },
|
||||
{ PN_PROCESS_MAPPED_WRITECOPY, 0, TEXT("Mapped Space Write Copy") },
|
||||
{ PN_PROCESS_MAPPED_EXECUTABLE, 0, TEXT("Mapped Space Executable") },
|
||||
{ PN_PROCESS_MAPPED_EXE_READONLY, 0, TEXT("Mapped Space Exec Read Only") },
|
||||
{ PN_PROCESS_MAPPED_EXE_READWRITE, 0, TEXT("Mapped Space Exec Read/Write") },
|
||||
{ PN_PROCESS_MAPPED_EXE_WRITECOPY, 0, TEXT("Mapped Space Exec Write Copy") },
|
||||
{ PN_PROCESS_IMAGE_NOACCESS, 0, TEXT("Image Space No Access") },
|
||||
{ PN_PROCESS_IMAGE_READONLY, 0, TEXT("Image Space Read Only") },
|
||||
{ PN_PROCESS_IMAGE_READWRITE, 0, TEXT("Image Space Read/Write") },
|
||||
{ PN_PROCESS_IMAGE_WRITECOPY, 0, TEXT("Image Space Write Copy") },
|
||||
{ PN_PROCESS_IMAGE_EXECUTABLE, 0, TEXT("Image Space Executable") },
|
||||
{ PN_PROCESS_IMAGE_EXE_READONLY, 0, TEXT("Image Space Exec Read Only") },
|
||||
{ PN_PROCESS_IMAGE_EXE_READWRITE, 0, TEXT("Image Space Exec Read/Write") },
|
||||
{ PN_PROCESS_IMAGE_EXE_WRITECOPY, 0, TEXT("Image Space Exec Write Copy") },
|
||||
{ 0, 0, 0 },
|
||||
};
|
||||
|
||||
#define NENTRIES ((sizeof(entries) / sizeof(entry_t)) - 1)
|
||||
|
||||
static int
|
||||
key_for_index(int key)
|
||||
{
|
||||
entry_t* entry = entries + NENTRIES / 2;
|
||||
unsigned int step = 64 / 4; // XXX
|
||||
|
||||
while (step) {
|
||||
if (key < entry->e_key)
|
||||
entry -= step;
|
||||
else if (key > entry->e_key)
|
||||
entry += step;
|
||||
|
||||
if (key == entry->e_key)
|
||||
return entry->e_index;
|
||||
|
||||
step >>= 1;
|
||||
}
|
||||
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
class auto_hkey {
|
||||
protected:
|
||||
HKEY hkey;
|
||||
|
||||
HKEY* begin_assignment() {
|
||||
if (hkey) {
|
||||
::RegCloseKey(hkey);
|
||||
hkey = 0;
|
||||
}
|
||||
return &hkey;
|
||||
}
|
||||
|
||||
public:
|
||||
auto_hkey() : hkey(0) {}
|
||||
~auto_hkey() { ::RegCloseKey(hkey); }
|
||||
|
||||
HKEY get() const { return hkey; }
|
||||
operator HKEY() const { return get(); }
|
||||
|
||||
friend HKEY*
|
||||
getter_Acquires(auto_hkey& hkey);
|
||||
};
|
||||
|
||||
static HKEY*
|
||||
getter_Acquires(auto_hkey& hkey)
|
||||
{
|
||||
return hkey.begin_assignment();
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
get_perf_titles(char*& buffer, char**& titles, int& last_title_index)
|
||||
{
|
||||
DWORD result;
|
||||
|
||||
// Open the perflib key to find out the last counter's index and
|
||||
// system version.
|
||||
auto_hkey perflib_hkey;
|
||||
result = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
||||
TEXT("software\\microsoft\\windows nt\\currentversion\\perflib"),
|
||||
0,
|
||||
KEY_READ,
|
||||
getter_Acquires(perflib_hkey));
|
||||
|
||||
if (result != ERROR_SUCCESS)
|
||||
return result;
|
||||
|
||||
// Get the last counter's index so we know how much memory to
|
||||
// allocate for titles
|
||||
DWORD data_size = sizeof(DWORD);
|
||||
DWORD type;
|
||||
result = ::RegQueryValueEx(perflib_hkey,
|
||||
TEXT("Last Counter"),
|
||||
0,
|
||||
&type,
|
||||
reinterpret_cast<BYTE*>(&last_title_index),
|
||||
&data_size);
|
||||
|
||||
if (result != ERROR_SUCCESS)
|
||||
return result;
|
||||
|
||||
// Find system version, for system earlier than 1.0a, there's no
|
||||
// version value.
|
||||
int version;
|
||||
result = ::RegQueryValueEx(perflib_hkey,
|
||||
TEXT("Version"),
|
||||
0,
|
||||
&type,
|
||||
reinterpret_cast<BYTE*>(&version),
|
||||
&data_size);
|
||||
|
||||
bool is_nt_10 = (result == ERROR_SUCCESS);
|
||||
|
||||
// Now, get ready for the counter names and indexes.
|
||||
char* counter_value_name;
|
||||
auto_hkey counter_autohkey;
|
||||
HKEY counter_hkey;
|
||||
if (is_nt_10) {
|
||||
// NT 1.0, so make hKey2 point to ...\perflib\009 and get
|
||||
// the counters from value "Counters"
|
||||
counter_value_name = TEXT("Counters");
|
||||
result = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
||||
TEXT("software\\microsoft\\windows nt\\currentversion\\perflib\\009"),
|
||||
0,
|
||||
KEY_READ,
|
||||
getter_Acquires(counter_autohkey));
|
||||
|
||||
if (result != ERROR_SUCCESS)
|
||||
return result;
|
||||
|
||||
counter_hkey = counter_autohkey;
|
||||
}
|
||||
else {
|
||||
// NT 1.0a or later. Get the counters in key HKEY_PERFORMANCE_KEY
|
||||
// and from value "Counter 009"
|
||||
counter_value_name = TEXT("Counter 009");
|
||||
counter_hkey = HKEY_PERFORMANCE_DATA;
|
||||
}
|
||||
|
||||
// Find out the size of the data.
|
||||
result = ::RegQueryValueEx(counter_hkey,
|
||||
counter_value_name,
|
||||
0,
|
||||
&type,
|
||||
0,
|
||||
&data_size);
|
||||
|
||||
if (result != ERROR_SUCCESS)
|
||||
return result;
|
||||
|
||||
// Allocate memory
|
||||
buffer = new char[data_size];
|
||||
titles = new char*[last_title_index + 1];
|
||||
for (int i = 0; i <= last_title_index; ++i)
|
||||
titles[i] = 0;
|
||||
|
||||
// Query the data
|
||||
result = ::RegQueryValueEx(counter_hkey,
|
||||
counter_value_name,
|
||||
0,
|
||||
&type,
|
||||
reinterpret_cast<BYTE*>(buffer),
|
||||
&data_size);
|
||||
if (result != ERROR_SUCCESS)
|
||||
return result;
|
||||
|
||||
// Setup the titles array of pointers to point to beginning of
|
||||
// each title string.
|
||||
char* title = buffer;
|
||||
int len;
|
||||
|
||||
while (len = lstrlen(title)) {
|
||||
int index = atoi(title);
|
||||
title += len + 1;
|
||||
|
||||
if (index <= last_title_index)
|
||||
titles[index] = title;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("%d=%s\n", index, title);
|
||||
#endif
|
||||
|
||||
title += lstrlen(title) + 1;
|
||||
}
|
||||
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
static void
|
||||
init_entries()
|
||||
{
|
||||
char* buffer;
|
||||
char** titles;
|
||||
int last = 0;
|
||||
|
||||
DWORD result = get_perf_titles(buffer, titles, last);
|
||||
|
||||
assert(result == ERROR_SUCCESS);
|
||||
|
||||
for (entry_t* entry = entries; entry->e_key != 0; ++entry) {
|
||||
for (int index = 0; index <= last; ++index) {
|
||||
if (titles[index] && 0 == lstrcmpi(titles[index], entry->e_title)) {
|
||||
entry->e_index = index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (entry->e_index == 0) {
|
||||
fprintf(stderr, "warning: unable to find index for ``%s''\n", entry->e_title);
|
||||
}
|
||||
}
|
||||
|
||||
delete[] buffer;
|
||||
delete[] titles;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static DWORD
|
||||
get_perf_data(HKEY perf_hkey, char* object_index, PERF_DATA_BLOCK** data, DWORD* size)
|
||||
{
|
||||
if (! *data)
|
||||
*data = reinterpret_cast<PERF_DATA_BLOCK*>(new char[*size]);
|
||||
|
||||
DWORD result;
|
||||
|
||||
while (1) {
|
||||
DWORD type;
|
||||
DWORD real_size = *size;
|
||||
|
||||
result = ::RegQueryValueEx(perf_hkey,
|
||||
object_index,
|
||||
0,
|
||||
&type,
|
||||
reinterpret_cast<BYTE*>(*data),
|
||||
&real_size);
|
||||
|
||||
if (result != ERROR_MORE_DATA)
|
||||
break;
|
||||
|
||||
delete[] *data;
|
||||
*size += 1024;
|
||||
*data = reinterpret_cast<PERF_DATA_BLOCK*>(new char[*size]);
|
||||
|
||||
if (! *data)
|
||||
return ERROR_NOT_ENOUGH_MEMORY;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static const PERF_OBJECT_TYPE*
|
||||
first_object(const PERF_DATA_BLOCK* data)
|
||||
{
|
||||
return data
|
||||
? reinterpret_cast<const PERF_OBJECT_TYPE*>(reinterpret_cast<const char*>(data) + data->HeaderLength)
|
||||
: 0;
|
||||
}
|
||||
|
||||
static const PERF_OBJECT_TYPE*
|
||||
next_object(const PERF_OBJECT_TYPE* object)
|
||||
{
|
||||
return object
|
||||
? reinterpret_cast<const PERF_OBJECT_TYPE*>(reinterpret_cast<const char*>(object) + object->TotalByteLength)
|
||||
: 0;
|
||||
}
|
||||
|
||||
const PERF_OBJECT_TYPE*
|
||||
find_object(const PERF_DATA_BLOCK* data, DWORD index)
|
||||
{
|
||||
const PERF_OBJECT_TYPE* object = first_object(data);
|
||||
if (! object)
|
||||
return 0;
|
||||
|
||||
for (int i = 0; i < data->NumObjectTypes; ++i) {
|
||||
if (object->ObjectNameTitleIndex == index)
|
||||
return object;
|
||||
|
||||
object = next_object(object);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const PERF_COUNTER_DEFINITION*
|
||||
first_counter(const PERF_OBJECT_TYPE* object)
|
||||
{
|
||||
return object
|
||||
? reinterpret_cast<const PERF_COUNTER_DEFINITION*>(reinterpret_cast<const char*>(object) + object->HeaderLength)
|
||||
: 0;
|
||||
}
|
||||
|
||||
static const PERF_COUNTER_DEFINITION*
|
||||
next_counter(const PERF_COUNTER_DEFINITION* counter)
|
||||
{
|
||||
return counter ?
|
||||
reinterpret_cast<const PERF_COUNTER_DEFINITION*>(reinterpret_cast<const char*>(counter) + counter->ByteLength)
|
||||
: 0;
|
||||
}
|
||||
|
||||
|
||||
static const PERF_COUNTER_DEFINITION*
|
||||
find_counter(const PERF_OBJECT_TYPE* object, int index)
|
||||
{
|
||||
const PERF_COUNTER_DEFINITION* counter =
|
||||
first_counter(object);
|
||||
|
||||
if (! counter)
|
||||
return 0;
|
||||
|
||||
for (int i; i < object->NumCounters; ++i) {
|
||||
if (counter->CounterNameTitleIndex == index)
|
||||
return counter;
|
||||
|
||||
counter = next_counter(counter);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const PERF_INSTANCE_DEFINITION*
|
||||
first_instance(const PERF_OBJECT_TYPE* object)
|
||||
{
|
||||
return object
|
||||
? reinterpret_cast<const PERF_INSTANCE_DEFINITION*>(reinterpret_cast<const char*>(object) + object->DefinitionLength)
|
||||
: 0;
|
||||
}
|
||||
|
||||
|
||||
static const PERF_INSTANCE_DEFINITION*
|
||||
next_instance(const PERF_INSTANCE_DEFINITION* instance)
|
||||
{
|
||||
if (instance) {
|
||||
const PERF_COUNTER_BLOCK* counter_block =
|
||||
reinterpret_cast<const PERF_COUNTER_BLOCK*>(reinterpret_cast<const char*>(instance) + instance->ByteLength);
|
||||
|
||||
return reinterpret_cast<const PERF_INSTANCE_DEFINITION*>(reinterpret_cast<const char*>(counter_block) + counter_block->ByteLength);
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static const wchar_t*
|
||||
instance_name(const PERF_INSTANCE_DEFINITION* instance)
|
||||
{
|
||||
return instance
|
||||
? reinterpret_cast<const wchar_t*>(reinterpret_cast<const char*>(instance) + instance->NameOffset)
|
||||
: 0;
|
||||
}
|
||||
|
||||
|
||||
static const void*
|
||||
counter_data(const PERF_INSTANCE_DEFINITION* instance,
|
||||
const PERF_COUNTER_DEFINITION* counter)
|
||||
{
|
||||
if (counter && instance) {
|
||||
const PERF_COUNTER_BLOCK* counter_block;
|
||||
counter_block = reinterpret_cast<const PERF_COUNTER_BLOCK*>(reinterpret_cast<const char*>(instance) + instance->ByteLength);
|
||||
return reinterpret_cast<const char*>(counter_block) + counter->CounterOffset;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
list_process(PERF_DATA_BLOCK* perf_data, wchar_t* process_name)
|
||||
{
|
||||
const PERF_OBJECT_TYPE* process = find_object(perf_data, key_for_index(PN_PROCESS));
|
||||
const PERF_COUNTER_DEFINITION* working_set = find_counter(process, key_for_index(PN_PROCESS_WORKING_SET));
|
||||
const PERF_COUNTER_DEFINITION* peak_working_set = find_counter(process, key_for_index(PN_PROCESS_PEAK_WS));
|
||||
const PERF_COUNTER_DEFINITION* private_page = find_counter(process, key_for_index(PN_PROCESS_PRIVATE_PAGE));
|
||||
const PERF_COUNTER_DEFINITION* virtual_size = find_counter(process, key_for_index(PN_PROCESS_VIRTUAL_SIZE));
|
||||
|
||||
const PERF_INSTANCE_DEFINITION* instance = first_instance(process);
|
||||
int index = 0;
|
||||
|
||||
bool found = false;
|
||||
|
||||
while (instance && index < process->NumInstances) {
|
||||
const wchar_t* name = instance_name(instance);
|
||||
if (lstrcmpW(process_name, name) == 0) {
|
||||
printf("%d %d %d %d\n",
|
||||
*(static_cast<const int*>(counter_data(instance, working_set))),
|
||||
*(static_cast<const int*>(counter_data(instance, peak_working_set))),
|
||||
*(static_cast<const int*>(counter_data(instance, private_page))),
|
||||
*(static_cast<const int*>(counter_data(instance, virtual_size))));
|
||||
|
||||
found = true;
|
||||
}
|
||||
|
||||
instance = next_instance(instance);
|
||||
++index;
|
||||
}
|
||||
|
||||
if (found) {
|
||||
#if 0
|
||||
// Dig up address space data.
|
||||
PERF_OBJECT_TYPE* address_space = FindObject(costly_data, PX_PROCESS_ADDRESS_SPACE);
|
||||
PERF_COUNTER_DEFINITION* image_executable = FindCounter(process, PX_PROCESS_IMAGE_EXECUTABLE);
|
||||
PERF_COUNTER_DEFINITION* image_exe_readonly = FindCounter(process, PX_PROCESS_IMAGE_EXE_READONLY);
|
||||
PERF_COUNTER_DEFINITION* image_exe_readwrite = FindCounter(process, PX_PROCESS_IMAGE_EXE_READWRITE);
|
||||
PERF_COUNTER_DEFINITION* image_exe_writecopy = FindCounter(process, PX_PROCESS_IMAGE_EXE_WRITECOPY);
|
||||
#endif
|
||||
}
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char* argv[])
|
||||
{
|
||||
wchar_t process_name[32];
|
||||
|
||||
int interval = 10000; // msec
|
||||
|
||||
int i = 0;
|
||||
while (++i < argc) {
|
||||
if (argv[i][0] != '-')
|
||||
break;
|
||||
|
||||
switch (argv[i][1]) {
|
||||
case 'i':
|
||||
interval = atoi(argv[++i]) * 1000;
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "unknown option `%c'\n", argv[i][1]);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
char* p = argv[i];
|
||||
wchar_t* q = process_name;
|
||||
while (*q++ = wchar_t(*p++))
|
||||
continue;
|
||||
}
|
||||
|
||||
init_entries();
|
||||
|
||||
PERF_DATA_BLOCK* perf_data = 0;
|
||||
PERF_DATA_BLOCK* costly_data = 0;
|
||||
DWORD perf_data_size = 50 * 1024;
|
||||
DWORD costly_data_size = 100 * 1024;
|
||||
|
||||
do {
|
||||
char buf[64];
|
||||
sprintf(buf, "%ld %ld",
|
||||
key_for_index(PN_PROCESS),
|
||||
key_for_index(PN_THREAD));
|
||||
|
||||
get_perf_data(HKEY_PERFORMANCE_DATA, buf, &perf_data, &perf_data_size);
|
||||
|
||||
#if 0
|
||||
sprintf(buf, "%ld %ld %ld",
|
||||
key_for_index(PN_PROCESS_ADDRESS_SPACE),
|
||||
key_for_index(PN_IMAGE),
|
||||
key_for_index(PN_THREAD_DETAILS));
|
||||
|
||||
get_perf_data(HKEY_PERFORMANCE_DATA, buf, &costly_data, &costly_data_size);
|
||||
#endif
|
||||
|
||||
if (! list_process(perf_data, process_name))
|
||||
break;
|
||||
|
||||
_sleep(interval);
|
||||
} while (1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user