*
* Author: Paolo Molaro (lupus@ximian.com)
*
- * (C) 2008 Novell, Inc
+ * Copyright 2008-2009 Novell, Inc (http://www.novell.com)
*/
#include "config.h"
#include <time.h>
#include <string.h>
#include <stdlib.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if defined (__OpenBSD__)
+#include <sys/param.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
+#if defined (__NetBSD__) || defined (__APPLE__)
+#include <sys/sysctl.h>
+#endif
#include "metadata/mono-perfcounters.h"
#include "metadata/appdomain.h"
+#include "metadata/object-internals.h"
/* for mono_stats */
#include "metadata/class-internals.h"
#include "utils/mono-time.h"
#include "utils/mono-mmap.h"
+#include "utils/mono-proclib.h"
+#include "utils/mono-networkinterfaces.h"
+#include "utils/mono-error-internals.h"
#include <mono/io-layer/io-layer.h>
/* map of CounterSample.cs */
ThreadInstance,
CPUInstance,
MonoInstance,
+ NetworkInterfaceInstance,
CustomInstance
};
#define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_ ## id,
-#define PERFCTR_COUNTER(id,name,help,type)
+#define PERFCTR_COUNTER(id,name,help,type,field)
enum {
#include "mono-perfcounters-def.h"
NUM_CATEGORIES
#undef PERFCTR_CAT
#undef PERFCTR_COUNTER
#define PERFCTR_CAT(id,name,help,type,inst,first_counter) CATEGORY_START_ ## id = -1,
-#define PERFCTR_COUNTER(id,name,help,type) COUNTER_ ## id,
+#define PERFCTR_COUNTER(id,name,help,type,field) COUNTER_ ## id,
/* each counter is assigned an id starting from 0 inside the category */
enum {
#include "mono-perfcounters-def.h"
#undef PERFCTR_CAT
#undef PERFCTR_COUNTER
#define PERFCTR_CAT(id,name,help,type,inst,first_counter)
-#define PERFCTR_COUNTER(id,name,help,type) CCOUNTER_ ## id,
+#define PERFCTR_COUNTER(id,name,help,type,field) CCOUNTER_ ## id,
/* this is used just to count the number of counters */
enum {
#include "mono-perfcounters-def.h"
SharedHeader header;
unsigned int category_offset;
/* variable length data follows */
- char name [1];
+ char instance_name [1];
} SharedInstance;
typedef struct {
unsigned char type;
+ guint8 seq_num;
/* variable length data follows */
char name [1];
} SharedCounter;
typedef struct {
const char *name;
const char *help;
- int id;
+ short id;
+ unsigned short offset; // offset inside MonoPerfCounters
int type;
} CounterDesc;
#undef PERFCTR_CAT
#undef PERFCTR_COUNTER
#define PERFCTR_CAT(id,name,help,type,inst,first_counter) {name, help, CATEGORY_ ## id, type, inst ## Instance, CCOUNTER_ ## first_counter},
-#define PERFCTR_COUNTER(id,name,help,type)
+#define PERFCTR_COUNTER(id,name,help,type,field)
static const CategoryDesc
predef_categories [] = {
#include "mono-perfcounters-def.h"
#undef PERFCTR_CAT
#undef PERFCTR_COUNTER
#define PERFCTR_CAT(id,name,help,type,inst,first_counter)
-#define PERFCTR_COUNTER(id,name,help,type) {name, help, COUNTER_ ## id, type},
+#define PERFCTR_COUNTER(id,name,help,type,field) {name, help, COUNTER_ ## id, G_STRUCT_OFFSET (MonoPerfCounters, field), type},
static const CounterDesc
predef_counters [] = {
#include "mono-perfcounters-def.h"
- {NULL, NULL, -1, 0}
+ {NULL, NULL, -1, 0, 0}
};
/*
CleanupFunc cleanup;
};
+typedef struct {
+ int id;
+ char *name;
+} NetworkVtableArg;
+
typedef struct {
ImplVtable vtable;
MonoPerfCounters *counters;
int pid;
} PredefVtable;
+typedef struct {
+ ImplVtable vtable;
+ SharedInstance *instance_desc;
+ SharedCounter *counter_desc;
+} CustomVTable;
+
static ImplVtable*
create_vtable (void *arg, SampleFunc sample, UpdateFunc update)
{
}
static void
-predef_cleanup (ImplVtable *vtable)
+unref_pid_unlocked (int pid)
{
- PredefVtable *vt = (PredefVtable*)vtable;
ExternalSArea *data;
- perfctr_lock ();
- if (!pid_to_shared_area) {
- perfctr_unlock ();
- return;
- }
- data = g_hash_table_lookup (pid_to_shared_area, GINT_TO_POINTER (vt->pid));
+ data = g_hash_table_lookup (pid_to_shared_area, GINT_TO_POINTER (pid));
if (data) {
data->refcount--;
if (!data->refcount) {
- g_hash_table_remove (pid_to_shared_area, GINT_TO_POINTER (vt->pid));
+ g_hash_table_remove (pid_to_shared_area, GINT_TO_POINTER (pid));
mono_shared_area_unload (data->sarea);
g_free (data);
}
}
+}
+
+static void
+predef_cleanup (ImplVtable *vtable)
+{
+ PredefVtable *vt = (PredefVtable*)vtable;
+ /* ExternalSArea *data; */
+
+ perfctr_lock ();
+ if (!pid_to_shared_area) {
+ perfctr_unlock ();
+ return;
+ }
+ unref_pid_unlocked (vt->pid);
perfctr_unlock ();
}
+static guint64
+mono_determine_physical_ram_size (void)
+{
+#if defined (TARGET_WIN32)
+ MEMORYSTATUSEX memstat;
+
+ memstat.dwLength = sizeof (memstat);
+ GlobalMemoryStatusEx (&memstat);
+ return (guint64)memstat.ullTotalPhys;
+#elif defined (__NetBSD__) || defined (__APPLE__)
+#ifdef __NetBSD__
+ unsigned long value;
+#else
+ guint64 value;
+#endif
+ int mib[2] = {
+ CTL_HW,
+#ifdef __NetBSD__
+ HW_PHYSMEM
+#else
+ HW_MEMSIZE
+#endif
+ };
+ size_t size_sys = sizeof (value);
+
+ sysctl (mib, 2, &value, &size_sys, NULL, 0);
+ if (value == 0)
+ return 134217728;
+
+ return (guint64)value;
+#elif defined (HAVE_SYSCONF)
+ guint64 page_size = 0, num_pages = 0;
+
+ /* sysconf works on most *NIX operating systems, if your system doesn't have it or if it
+ * reports invalid values, please add your OS specific code below. */
+#ifdef _SC_PAGESIZE
+ page_size = (guint64)sysconf (_SC_PAGESIZE);
+#endif
+
+#ifdef _SC_PHYS_PAGES
+ num_pages = (guint64)sysconf (_SC_PHYS_PAGES);
+#endif
+
+ if (!page_size || !num_pages) {
+ g_warning ("Your operating system's sysconf (3) function doesn't correctly report physical memory size!");
+ return 134217728;
+ }
+
+ return page_size * num_pages;
+#else
+ return 134217728;
+#endif
+}
+
void
mono_perfcounters_init (void)
{
unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
unsigned char *end = (unsigned char *)shared_area + shared_area->size;
- size += 3;
- size &= ~3;
+ size += 7;
+ size &= ~7;
while (p < end) {
unsigned short *next;
if (*p == FTYPE_END) {
typedef gboolean (*SharedFunc) (SharedHeader *header, void *data);
static void
-foreach_shared_item (SharedFunc func, void *data)
+foreach_shared_item_in_area (unsigned char *p, unsigned char *end, SharedFunc func, void *data)
{
- unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
- unsigned char *end = (unsigned char *)shared_area + shared_area->size;
-
while (p < end) {
unsigned short *next;
if (p + 4 > end)
}
}
+static void
+foreach_shared_item (SharedFunc func, void *data)
+{
+ unsigned char *p = (unsigned char *)shared_area + shared_area->data_start;
+ unsigned char *end = (unsigned char *)shared_area + shared_area->size;
+
+ foreach_shared_item_in_area (p, end, func, data);
+}
+
static int
mono_string_compare_ascii (MonoString *str, const char *ascii_str)
{
return NULL;
}
+typedef struct {
+ unsigned int cat_offset;
+ SharedCategory* cat;
+ MonoString *instance;
+ SharedInstance* result;
+ GSList *list;
+} InstanceSearch;
+
+static gboolean
+instance_search (SharedHeader *header, void *data)
+{
+ InstanceSearch *search = data;
+ if (header->ftype == FTYPE_INSTANCE) {
+ SharedInstance *ins = (SharedInstance*)header;
+ if (search->cat_offset == ins->category_offset) {
+ if (search->instance) {
+ if (mono_string_compare_ascii (search->instance, ins->instance_name) == 0) {
+ search->result = ins;
+ return FALSE;
+ }
+ } else {
+ search->list = g_slist_prepend (search->list, ins);
+ }
+ }
+ }
+ return TRUE;
+}
+
+static SharedInstance*
+find_custom_instance (SharedCategory* cat, MonoString *instance)
+{
+ InstanceSearch search;
+ search.cat_offset = (char*)cat - (char*)shared_area;
+ search.cat = cat;
+ search.instance = instance;
+ search.list = NULL;
+ search.result = NULL;
+ foreach_shared_item (instance_search, &search);
+ return search.result;
+}
+
+static GSList*
+get_custom_instances_list (SharedCategory* cat)
+{
+ InstanceSearch search;
+ search.cat_offset = (char*)cat - (char*)shared_area;
+ search.cat = cat;
+ search.instance = NULL;
+ search.list = NULL;
+ search.result = NULL;
+ foreach_shared_item (instance_search, &search);
+ return search.list;
+}
+
static char*
custom_category_help (SharedCategory* cat)
{
}
static int
-id_from_string (MonoString *instance)
+id_from_string (MonoString *instance, gboolean is_process)
{
int id = -1;
if (mono_string_length (instance)) {
char *id_str = mono_string_to_utf8 (instance);
char *end;
id = strtol (id_str, &end, 0);
+ if (end == id_str && !is_process)
+ id = -1;
g_free (id_str);
- if (end == id_str)
- return -1;
}
return id;
}
-static void
-get_cpu_times (int cpu_id, gint64 *user, gint64 *systemt, gint64 *irq, gint64 *sirq, gint64 *idle)
-{
- char buf [256];
- char *s;
- int hz = 100 * 2; // 2 numprocs
- long long unsigned int user_ticks, nice_ticks, system_ticks, idle_ticks, iowait_ticks, irq_ticks, sirq_ticks;
- FILE *f = fopen ("/proc/stat", "r");
- if (!f)
- return;
- while ((s = fgets (buf, sizeof (buf), f))) {
- char *data = NULL;
- if (cpu_id < 0 && strncmp (s, "cpu", 3) == 0 && g_ascii_isspace (s [3])) {
- data = s + 4;
- } else if (cpu_id >= 0 && strncmp (s, "cpu", 3) == 0 && strtol (s + 3, &data, 10) == cpu_id) {
- if (data == s + 3)
- continue;
- data++;
- } else {
- continue;
- }
- sscanf (data, "%Lu %Lu %Lu %Lu %Lu %Lu %Lu", &user_ticks, &nice_ticks, &system_ticks, &idle_ticks, &iowait_ticks, &irq_ticks, &sirq_ticks);
- }
- fclose (f);
-
- if (user)
- *user = (user_ticks + nice_ticks) * 10000000 / hz;
- if (systemt)
- *systemt = (system_ticks) * 10000000 / hz;
- if (irq)
- *irq = (irq_ticks) * 10000000 / hz;
- if (sirq)
- *sirq = (sirq_ticks) * 10000000 / hz;
- if (idle)
- *idle = (idle_ticks) * 10000000 / hz;
-}
-
static MonoBoolean
get_cpu_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
{
- gint64 value = 0;
+ MonoProcessError error;
int id = GPOINTER_TO_INT (vtable->arg);
int pid = id >> 5;
id &= 0x1f;
fill_sample (sample);
sample->baseValue = 1;
}
- sample->counterType = predef_counters [predef_categories [CATEGORY_PROC].first_counter + id].type;
+ sample->counterType = predef_counters [predef_categories [CATEGORY_CPU].first_counter + id].type;
switch (id) {
case COUNTER_CPU_USER_TIME:
- get_cpu_times (pid, &value, NULL, NULL, NULL, NULL);
- sample->rawValue = value;
+ sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_USER_TIME, &error);
return TRUE;
case COUNTER_CPU_PRIV_TIME:
- get_cpu_times (pid, NULL, &value, NULL, NULL, NULL);
- sample->rawValue = value;
+ sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_PRIV_TIME, &error);
return TRUE;
case COUNTER_CPU_INTR_TIME:
- get_cpu_times (pid, NULL, NULL, &value, NULL, NULL);
- sample->rawValue = value;
+ sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_INTR_TIME, &error);
return TRUE;
case COUNTER_CPU_DCP_TIME:
- get_cpu_times (pid, NULL, NULL, NULL, &value, NULL);
- sample->rawValue = value;
+ sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_DCP_TIME, &error);
return TRUE;
case COUNTER_CPU_PROC_TIME:
- get_cpu_times (pid, NULL, NULL, NULL, NULL, &value);
- sample->rawValue = value;
+ sample->rawValue = mono_cpu_get_data (pid, MONO_CPU_IDLE_TIME, &error);
return TRUE;
}
return FALSE;
static void*
cpu_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
{
- int id = id_from_string (instance) << 5;
+ int id = id_from_string (instance, FALSE) << 5;
const CounterDesc *cdesc;
*custom = FALSE;
/* increase the shift above and the mask also in the implementation functions */
return NULL;
}
-/*
- * /proc/pid/stat format:
- * pid (cmdname) S
- * [0] ppid pgid sid tty_nr tty_pgrp flags min_flt cmin_flt maj_flt cmaj_flt
- * [10] utime stime cutime cstime prio nice threads start_time vsize rss
- * [20] rsslim start_code end_code start_stack esp eip pending blocked sigign sigcatch
- * [30] wchan 0 0 exit_signal cpu rt_prio policy
- */
-
-static gint64
-get_process_time (int pid, int pos, int sum)
+static MonoBoolean
+get_network_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
{
- char buf [512];
- char *s, *end;
- FILE *f;
- int len, i;
- gint64 value;
-
- g_snprintf (buf, sizeof (buf), "/proc/%d/stat", pid);
- f = fopen (buf, "r");
- if (!f)
- return 0;
- len = fread (buf, 1, sizeof (buf), f);
- fclose (f);
- if (len <= 0)
- return 0;
- s = strchr (buf, ')');
- if (!s)
- return 0;
- s++;
- while (g_ascii_isspace (*s)) s++;
- if (!*s)
- return 0;
- /* skip the status char */
- while (*s && !g_ascii_isspace (*s)) s++;
- if (!*s)
- return 0;
- for (i = 0; i < pos; ++i) {
- while (g_ascii_isspace (*s)) s++;
- if (!*s)
- return 0;
- while (*s && !g_ascii_isspace (*s)) s++;
- if (!*s)
- return 0;
+ MonoNetworkError error = MONO_NETWORK_ERROR_OTHER;
+ NetworkVtableArg *narg = (NetworkVtableArg*) vtable->arg;
+ if (!only_value) {
+ fill_sample (sample);
}
- /* we are finally at the needed item */
- value = strtoul (s, &end, 0);
- /* add also the following value */
- if (sum) {
- while (g_ascii_isspace (*s)) s++;
- if (!*s)
- return 0;
- value += strtoul (s, &end, 0);
+
+ sample->counterType = predef_counters [predef_categories [CATEGORY_NETWORK].first_counter + narg->id].type;
+ switch (narg->id) {
+ case COUNTER_NETWORK_BYTESRECSEC:
+ sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESREC, &error);
+ break;
+ case COUNTER_NETWORK_BYTESSENTSEC:
+ sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESSENT, &error);
+ break;
+ case COUNTER_NETWORK_BYTESTOTALSEC:
+ sample->rawValue = mono_network_get_data (narg->name, MONO_NETWORK_BYTESTOTAL, &error);
+ break;
}
- return value;
+
+ if (error == MONO_NETWORK_ERROR_NONE)
+ return TRUE;
+ else
+ return FALSE;
}
-static gint64
-get_pid_stat_item (int pid, const char *item)
+static void
+network_cleanup (ImplVtable *vtable)
{
- char buf [256];
- char *s;
- FILE *f;
- int len = strlen (item);
-
- g_snprintf (buf, sizeof (buf), "/proc/%d/status", pid);
- f = fopen (buf, "r");
- if (!f)
- return 0;
- while ((s = fgets (buf, sizeof (buf), f))) {
- if (*item != *buf)
- continue;
- if (strncmp (buf, item, len))
- continue;
- if (buf [len] != ':')
- continue;
- fclose (f);
- return atoi (buf + len + 1);
+ NetworkVtableArg *narg;
+
+ if (vtable == NULL)
+ return;
+
+ narg = vtable->arg;
+ if (narg == NULL)
+ return;
+
+ g_free (narg->name);
+ narg->name = NULL;
+ g_free (narg);
+ vtable->arg = NULL;
+}
+
+static void*
+network_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
+{
+ const CounterDesc *cdesc;
+ NetworkVtableArg *narg;
+ ImplVtable *vtable;
+ char *instance_name;
+
+ *custom = FALSE;
+ if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_NETWORK], counter))) {
+ instance_name = mono_string_to_utf8 (instance);
+ narg = g_new0 (NetworkVtableArg, 1);
+ narg->id = cdesc->id;
+ narg->name = instance_name;
+ *type = cdesc->type;
+ vtable = create_vtable (narg, get_network_counter, NULL);
+ vtable->cleanup = network_cleanup;
+ return vtable;
}
- fclose (f);
- return 0;
+ return NULL;
}
static MonoBoolean
sample->counterType = predef_counters [predef_categories [CATEGORY_PROC].first_counter + id].type;
switch (id) {
case COUNTER_PROC_USER_TIME:
- sample->rawValue = get_process_time (pid, 12, FALSE);
+ sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_USER_TIME);
return TRUE;
case COUNTER_PROC_PRIV_TIME:
- sample->rawValue = get_process_time (pid, 13, FALSE);
+ sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_SYSTEM_TIME);
return TRUE;
case COUNTER_PROC_PROC_TIME:
- sample->rawValue = get_process_time (pid, 12, TRUE);
+ sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_TOTAL_TIME);
return TRUE;
case COUNTER_PROC_THREADS:
- sample->rawValue = get_pid_stat_item (pid, "Threads");
+ sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_NUM_THREADS);
return TRUE;
case COUNTER_PROC_VBYTES:
- sample->rawValue = get_pid_stat_item (pid, "VmSize") * 1024;
+ sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_VIRTUAL_BYTES);
return TRUE;
case COUNTER_PROC_WSET:
- sample->rawValue = get_pid_stat_item (pid, "VmRSS") * 1024;
+ sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_WORKING_SET);
return TRUE;
case COUNTER_PROC_PBYTES:
- sample->rawValue = get_pid_stat_item (pid, "VmData") * 1024;
+ sample->rawValue = mono_process_get_data (GINT_TO_POINTER (pid), MONO_PROCESS_PRIVATE_BYTES);
return TRUE;
}
return FALSE;
static void*
process_get_impl (MonoString* counter, MonoString* instance, int *type, MonoBoolean *custom)
{
- int id = id_from_string (instance) << 5;
+ int id = id_from_string (instance, TRUE) << 5;
const CounterDesc *cdesc;
*custom = FALSE;
/* increase the shift above and the mask also in the implementation functions */
case COUNTER_MEM_NUM_OBJECTS:
sample->rawValue = mono_stats.new_object_count;
return TRUE;
+ case COUNTER_MEM_PHYS_TOTAL:
+ sample->rawValue = mono_determine_physical_ram_size ();;
+ return TRUE;
}
return FALSE;
}
*custom = FALSE;
if ((cdesc = get_counter_in_category (&predef_categories [CATEGORY_MONO_MEM], counter))) {
*type = cdesc->type;
- return create_vtable (GINT_TO_POINTER (cdesc->id), mono_mem_counter, NULL);
+ return create_vtable (GINT_TO_POINTER ((gint) cdesc->id), mono_mem_counter, NULL);
}
return NULL;
}
predef_readonly_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
{
PredefVtable *vt = (PredefVtable *)vtable;
+ const CounterDesc *desc;
int cat_id = GPOINTER_TO_INT (vtable->arg);
int id = cat_id >> 16;
cat_id &= 0xffff;
fill_sample (sample);
sample->baseValue = 1;
}
- sample->counterType = predef_counters [predef_categories [cat_id].first_counter + id].type;
- switch (cat_id) {
- case CATEGORY_JIT:
- switch (id) {
- case COUNTER_JIT_BYTES:
- case COUNTER_JIT_BYTES_PSEC:
- sample->rawValue = vt->counters->jit_bytes;
- return TRUE;
- case COUNTER_JIT_METHODS:
- sample->rawValue = vt->counters->jit_methods;
- return TRUE;
- }
- break;
- case CATEGORY_EXC:
- switch (id) {
- case COUNTER_EXC_THROWN:
- sample->rawValue = vt->counters->exceptions_thrown;
- return TRUE;
- }
- break;
- }
- return FALSE;
+ desc = &predef_counters [predef_categories [cat_id].first_counter + id];
+ sample->counterType = desc->type;
+ /* FIXME: check that the offset fits inside imported counters */
+ /*g_print ("loading %s at %d\n", desc->name, desc->offset);*/
+ sample->rawValue = *(guint32*)((char*)vt->counters + desc->offset);
+ return TRUE;
}
static ImplVtable*
vtable->vtable.arg = arg;
vtable->vtable.sample = predef_readonly_counter;
vtable->vtable.cleanup = predef_cleanup;
- vtable->counters = &area->counters;
+ vtable->counters = (MonoPerfCounters*)((char*)area + area->counters_start);
vtable->pid = pid;
- return vtable;
+ return (ImplVtable*)vtable;
}
/* consider storing the pointer directly in vtable->arg, so the runtime overhead is lower:
}
sample->counterType = predef_counters [predef_categories [cat_id].first_counter + id].type;
switch (cat_id) {
+ case CATEGORY_EXC:
+ switch (id) {
+ case COUNTER_EXC_THROWN:
+ sample->rawValue = mono_perfcounters->exceptions_thrown;
+ return TRUE;
+ }
+ break;
case CATEGORY_ASPNET:
switch (id) {
case COUNTER_ASPNET_REQ_Q:
sample->rawValue = mono_perfcounters->aspnet_requests_queued;
return TRUE;
+ case COUNTER_ASPNET_REQ_TOTAL:
+ sample->rawValue = mono_perfcounters->aspnet_requests;
+ return TRUE;
+ }
+ break;
+ case CATEGORY_THREADPOOL:
+ switch (id) {
+ case COUNTER_THREADPOOL_WORKITEMS:
+ sample->rawValue = mono_perfcounters->threadpool_workitems;
+ return TRUE;
+ case COUNTER_THREADPOOL_IOWORKITEMS:
+ sample->rawValue = mono_perfcounters->threadpool_ioworkitems;
+ return TRUE;
+ case COUNTER_THREADPOOL_THREADS:
+ sample->rawValue = mono_perfcounters->threadpool_threads;
+ return TRUE;
+ case COUNTER_THREADPOOL_IOTHREADS:
+ sample->rawValue = mono_perfcounters->threadpool_iothreads;
+ return TRUE;
}
break;
}
static gint64
predef_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
{
- guint32 *ptr = NULL;
+ guint32 *volatile ptr = NULL;
+ gint64 *volatile ptr64 = NULL;
int cat_id = GPOINTER_TO_INT (vtable->arg);
int id = cat_id >> 16;
cat_id &= 0xffff;
case CATEGORY_ASPNET:
switch (id) {
case COUNTER_ASPNET_REQ_Q: ptr = &mono_perfcounters->aspnet_requests_queued; break;
+ case COUNTER_ASPNET_REQ_TOTAL: ptr = &mono_perfcounters->aspnet_requests; break;
+ }
+ break;
+ case CATEGORY_THREADPOOL:
+ switch (id) {
+ case COUNTER_THREADPOOL_WORKITEMS: ptr64 = (gint64 *) &mono_perfcounters->threadpool_workitems; break;
+ case COUNTER_THREADPOOL_IOWORKITEMS: ptr64 = (gint64 *) &mono_perfcounters->threadpool_ioworkitems; break;
+ case COUNTER_THREADPOOL_THREADS: ptr = &mono_perfcounters->threadpool_threads; break;
+ case COUNTER_THREADPOOL_IOTHREADS: ptr = &mono_perfcounters->threadpool_iothreads; break;
}
break;
}
if (ptr) {
if (do_incr) {
- /* FIXME: we need to do this atomically */
+ if (value == 1)
+ return InterlockedIncrement ((gint32 *) ptr); /* FIXME: sign */
+ if (value == -1)
+ return InterlockedDecrement ((gint32 *) ptr); /* FIXME: sign */
+
*ptr += value;
return *ptr;
}
/* this can be non-atomic */
*ptr = value;
return value;
+ } else if (ptr64) {
+ if (do_incr) {
+ /* FIXME: we need to do this atomically */
+ /* No InterlockedIncrement64() yet */
+ /*
+ if (value == 1)
+ return InterlockedIncrement64 (ptr);
+ if (value == -1)
+ return InterlockedDecrement64 (ptr);
+ */
+
+ *ptr64 += value;
+ return *ptr64;
+ }
+ /* this can be non-atomic */
+ *ptr64 = value;
+ return value;
}
return 0;
}
static MonoBoolean
custom_writable_counter (ImplVtable *vtable, MonoBoolean only_value, MonoCounterSample *sample)
{
- SharedCounter *scounter = vtable->arg;
+ CustomVTable *counter_data = (CustomVTable *)vtable;
if (!only_value) {
fill_sample (sample);
sample->baseValue = 1;
}
- sample->counterType = simple_type_to_type [scounter->type];
- /* FIXME */
- sample->rawValue = 0;
+ sample->counterType = simple_type_to_type [counter_data->counter_desc->type];
+ if (!vtable->arg)
+ sample->rawValue = 0;
+ else
+ sample->rawValue = *(guint64*)vtable->arg;
return TRUE;
}
static gint64
custom_writable_update (ImplVtable *vtable, MonoBoolean do_incr, gint64 value)
{
- SharedCounter *scounter = vtable->arg;
- /* FIXME */
- guint32 *ptr = NULL;
+ /* FIXME: check writability */
+ guint64 *ptr = vtable->arg;
if (ptr) {
if (do_incr) {
/* FIXME: we need to do this atomically */
return 0;
}
+static SharedInstance*
+custom_get_instance (SharedCategory *cat, SharedCounter *scounter, MonoString* instance)
+{
+ SharedInstance* inst;
+ unsigned char *ptr;
+ char *p;
+ int size, data_offset;
+ char *name;
+ inst = find_custom_instance (cat, instance);
+ if (inst)
+ return inst;
+ name = mono_string_to_utf8 (instance);
+ size = sizeof (SharedInstance) + strlen (name);
+ size += 7;
+ size &= ~7;
+ data_offset = size;
+ size += (sizeof (guint64) * cat->num_counters);
+ perfctr_lock ();
+ ptr = shared_data_find_room (size);
+ if (!ptr) {
+ perfctr_unlock ();
+ g_free (name);
+ return NULL;
+ }
+ inst = (SharedInstance*)ptr;
+ inst->header.extra = 0; /* data_offset could overflow here, so we leave this field unused */
+ inst->header.size = size;
+ inst->category_offset = (char*)cat - (char*)shared_area;
+ cat->num_instances++;
+ /* now copy the variable data */
+ p = inst->instance_name;
+ strcpy (p, name);
+ p += strlen (name) + 1;
+ inst->header.ftype = FTYPE_INSTANCE;
+ perfctr_unlock ();
+ g_free (name);
+
+ return inst;
+}
+
+static ImplVtable*
+custom_vtable (SharedCounter *scounter, SharedInstance* inst, char *data)
+{
+ CustomVTable* vtable;
+ vtable = g_new0 (CustomVTable, 1);
+ vtable->vtable.arg = data;
+ vtable->vtable.sample = custom_writable_counter;
+ vtable->vtable.update = custom_writable_update;
+ vtable->instance_desc = inst;
+ vtable->counter_desc = scounter;
+
+ return (ImplVtable*)vtable;
+}
+
static void*
custom_get_impl (SharedCategory *cat, MonoString* counter, MonoString* instance, int *type)
{
SharedCounter *scounter;
+ SharedInstance* inst;
+ int size;
scounter = find_custom_counter (cat, counter);
if (!scounter)
return NULL;
*type = simple_type_to_type [scounter->type];
- /* FIXME: use instance */
- return create_vtable (scounter, custom_writable_counter, custom_writable_update);
+ inst = custom_get_instance (cat, scounter, instance);
+ if (!inst)
+ return NULL;
+ size = sizeof (SharedInstance) + strlen (inst->instance_name);
+ size += 7;
+ size &= ~7;
+ return custom_vtable (scounter, inst, (char*)inst + size + scounter->seq_num * sizeof (guint64));
}
static const CategoryDesc*
return process_get_impl (counter, instance, type, custom);
case CATEGORY_MONO_MEM:
return mono_mem_get_impl (counter, instance, type, custom);
+ case CATEGORY_NETWORK:
+ return network_get_impl (counter, instance, type, custom);
case CATEGORY_JIT:
case CATEGORY_EXC:
+ case CATEGORY_GC:
+ case CATEGORY_REMOTING:
+ case CATEGORY_LOADING:
+ case CATEGORY_THREAD:
+ case CATEGORY_INTEROP:
+ case CATEGORY_SECURITY:
case CATEGORY_ASPNET:
+ case CATEGORY_THREADPOOL:
return predef_writable_get_impl (cdesc->id, counter, instance, type, custom);
}
return NULL;
MonoBoolean
mono_perfcounter_create (MonoString *category, MonoString *help, int type, MonoArray *items)
{
+ MonoError error;
int result = FALSE;
int i, size;
int num_counters = mono_array_length (items);
int counters_data_size;
- char *name = mono_string_to_utf8 (category);
- char *chelp = mono_string_to_utf8 (help);
- char **counter_info;
+ char *name = NULL;
+ char *chelp = NULL;
+ char **counter_info = NULL;
unsigned char *ptr;
char *p;
SharedCategory *cat;
+ /* FIXME: ensure there isn't a category created already */
+ mono_error_init (&error);
+ name = mono_string_to_utf8_checked (category, &error);
+ if (!mono_error_ok (&error))
+ goto failure;
+ chelp = mono_string_to_utf8_checked (help, &error);
+ if (!mono_error_ok (&error))
+ goto failure;
counter_info = g_new0 (char*, num_counters * 2);
/* calculate the size we need structure size + name/help + 2 0 string terminators */
size = G_STRUCT_OFFSET (SharedCategory, name) + strlen (name) + strlen (chelp) + 2;
for (i = 0; i < num_counters; ++i) {
CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
- counter_info [i * 2] = mono_string_to_utf8 (data->name);
- counter_info [i * 2 + 1] = mono_string_to_utf8 (data->help);
- size += 3; /* type and two 0 string terminators */
+ counter_info [i * 2] = mono_string_to_utf8_checked (data->name, &error);
+ if (!mono_error_ok (&error))
+ goto failure;
+ counter_info [i * 2 + 1] = mono_string_to_utf8_checked (data->help, &error);
+ if (!mono_error_ok (&error))
+ goto failure;
+ size += sizeof (SharedCounter) + 1; /* 1 is for the help 0 terminator */
}
for (i = 0; i < num_counters * 2; ++i) {
if (!counter_info [i])
goto failure;
- size += strlen (counter_info [i]);
+ size += strlen (counter_info [i]) + 1;
}
+ size += 7;
+ size &= ~7;
counters_data_size = num_counters * 8; /* optimize for size later */
if (size > 65535)
goto failure;
p += strlen (chelp) + 1;
for (i = 0; i < num_counters; ++i) {
CounterCreationData *data = mono_array_get (items, CounterCreationData*, i);
+ /* emit the SharedCounter structures */
*p++ = perfctr_type_compress (data->type);
+ *p++ = i;
strcpy (p, counter_info [i * 2]);
p += strlen (counter_info [i * 2]) + 1;
strcpy (p, counter_info [i * 2 + 1]);
perfctr_unlock ();
result = TRUE;
failure:
- for (i = 0; i < num_counters * 2; ++i) {
- g_free (counter_info [i]);
+ if (counter_info) {
+ for (i = 0; i < num_counters * 2; ++i) {
+ g_free (counter_info [i]);
+ }
+ g_free (counter_info);
}
- g_free (counter_info);
g_free (name);
g_free (chelp);
+ mono_error_cleanup (&error);
return result;
}
{
const CategoryDesc *cdesc;
/* no support for counters on other machines */
+ /*FIXME: machine appears to be wrong
if (mono_string_compare_ascii (machine, "."))
- return FALSE;
+ return FALSE;*/
cdesc = find_category (category);
- if (!cdesc)
- return FALSE;
+ if (!cdesc) {
+ SharedCategory *scat;
+ scat = find_custom_category (category);
+ if (!scat)
+ return FALSE;
+ if (find_custom_instance (scat, instance))
+ return TRUE;
+ } else {
+ /* FIXME: search instance */
+ }
return FALSE;
}
return mono_array_new (domain, mono_get_string_class (), 0);
}
+static MonoArray*
+get_string_array (void **array, int count, gboolean is_process)
+{
+ int i;
+ MonoDomain *domain = mono_domain_get ();
+ MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
+ for (i = 0; i < count; ++i) {
+ char buf [128];
+ char *p;
+ if (is_process) {
+ char *pname = mono_process_get_name (array [i], buf, sizeof (buf));
+ p = g_strdup_printf ("%d/%s", GPOINTER_TO_INT (array [i]), pname);
+ } else {
+ sprintf (buf, "%d", GPOINTER_TO_INT (array [i]));
+ p = buf;
+ }
+ mono_array_setref (res, i, mono_string_new (domain, p));
+ if (p != buf)
+ g_free (p);
+ }
+ return res;
+}
+
+static MonoArray*
+get_string_array_of_strings (void **array, int count)
+{
+ int i;
+ MonoDomain *domain = mono_domain_get ();
+ MonoArray * res = mono_array_new (mono_domain_get (), mono_get_string_class (), count);
+ for (i = 0; i < count; ++i) {
+ char* p = array[i];
+ mono_array_setref (res, i, mono_string_new (domain, p));
+ }
+
+ return res;
+}
+
+static MonoArray*
+get_mono_instances (void)
+{
+ int count = 64;
+ int res;
+ void **buf = NULL;
+ MonoArray *array;
+ do {
+ count *= 2;
+ g_free (buf);
+ buf = g_new (void*, count);
+ res = mono_shared_area_instances (buf, count);
+ } while (res == count);
+ array = get_string_array (buf, res, TRUE);
+ g_free (buf);
+ return array;
+}
+
+static MonoArray*
+get_cpu_instances (void)
+{
+ void **buf = NULL;
+ int i, count;
+ MonoArray *array;
+
+ count = mono_cpu_count () + 1; /* +1 for "_Total" */
+ buf = g_new (void*, count);
+ for (i = 0; i < count; ++i)
+ buf [i] = GINT_TO_POINTER (i - 1); /* -1 => _Total */
+ array = get_string_array (buf, count, FALSE);
+ g_free (buf);
+ mono_array_setref (array, 0, mono_string_new (mono_domain_get (), "_Total"));
+ return array;
+}
+
+static MonoArray*
+get_processes_instances (void)
+{
+ MonoArray *array;
+ int count = 0;
+ void **buf = mono_process_list (&count);
+ if (!buf)
+ return get_string_array (NULL, 0, FALSE);
+ array = get_string_array (buf, count, TRUE);
+ g_free (buf);
+ return array;
+}
+
+static MonoArray*
+get_networkinterface_instances (void)
+{
+ MonoArray *array;
+ int count = 0;
+ void **buf = mono_networkinterface_list (&count);
+ if (!buf)
+ return get_string_array_of_strings (NULL, 0);
+ array = get_string_array_of_strings (buf, count);
+ g_strfreev ((char **) buf);
+ return array;
+}
+
+static MonoArray*
+get_custom_instances (MonoString *category)
+{
+ SharedCategory *scat;
+ scat = find_custom_category (category);
+ if (scat) {
+ GSList *list = get_custom_instances_list (scat);
+ GSList *tmp;
+ int i = 0;
+ MonoArray *array = mono_array_new (mono_domain_get (), mono_get_string_class (), g_slist_length (list));
+ for (tmp = list; tmp; tmp = tmp->next) {
+ SharedInstance *inst = tmp->data;
+ mono_array_setref (array, i, mono_string_new (mono_domain_get (), inst->instance_name));
+ i++;
+ }
+ g_slist_free (list);
+ return array;
+ }
+ return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
+}
+
MonoArray*
mono_perfcounter_instance_names (MonoString *category, MonoString *machine)
{
+ const CategoryDesc* cat;
if (mono_string_compare_ascii (machine, "."))
return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
- return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
+ cat = find_category (category);
+ if (!cat)
+ return get_custom_instances (category);
+ switch (cat->instance_type) {
+ case MonoInstance:
+ return get_mono_instances ();
+ case CPUInstance:
+ return get_cpu_instances ();
+ case ProcessInstance:
+ return get_processes_instances ();
+ case NetworkInterfaceInstance:
+ return get_networkinterface_instances ();
+ case ThreadInstance:
+ default:
+ return mono_array_new (mono_domain_get (), mono_get_string_class (), 0);
+ }
}