Skip to content

Instantly share code, notes, and snippets.

@luhenry
Created March 17, 2014 19:12
Show Gist options
  • Save luhenry/9606220 to your computer and use it in GitHub Desktop.
Save luhenry/9606220 to your computer and use it in GitHub Desktop.
diff --git a/mono/mini/driver.c b/mono/mini/driver.c
index d71c077..be72e56 100644
--- a/mono/mini/driver.c
+++ b/mono/mini/driver.c
@@ -47,6 +47,7 @@
#include <mono/metadata/coree.h>
#include <mono/metadata/attach.h>
#include "mono/utils/mono-counters.h"
+#include <mono/utils/mono-counters-internals.h>
#include "mono/utils/mono-hwcap.h"
#include "mini.h"
@@ -1382,9 +1383,7 @@ mono_jit_parse_options (int argc, char * argv[])
opt->break_on_exc = TRUE;
} else if (strcmp (argv [i], "--stats") == 0) {
- mono_counters_enable (-1);
- mono_stats.enabled = TRUE;
- mono_jit_stats.enabled = TRUE;
+ mono_counters_dump_on_cleanup();
} else if (strcmp (argv [i], "--break") == 0) {
if (i+1 >= argc){
fprintf (stderr, "Missing method name in --break command line option\n");
@@ -1406,6 +1405,11 @@ mono_jit_parse_options (int argc, char * argv[])
exit (1);
}
}
+
+ // Always enable mono counters
+ mono_counters_enable (-1);
+ mono_stats.enabled = TRUE;
+ mono_jit_stats.enabled = TRUE;
if (trace_options != NULL) {
/*
@@ -1653,9 +1657,7 @@ mono_main (int argc, char* argv[])
} else if (strcmp (argv [i], "--print-vtable") == 0) {
mono_print_vtable = TRUE;
} else if (strcmp (argv [i], "--stats") == 0) {
- mono_counters_enable (-1);
- mono_stats.enabled = TRUE;
- mono_jit_stats.enabled = TRUE;
+ mono_counters_dump_on_cleanup();
#ifndef DISABLE_AOT
} else if (strcmp (argv [i], "--aot") == 0) {
error_if_aot_unsupported ();
@@ -1824,6 +1826,11 @@ mono_main (int argc, char* argv[])
}
}
+ // Always enable mono counters
+ mono_counters_enable (-1);
+ mono_stats.enabled = TRUE;
+ mono_jit_stats.enabled = TRUE;
+
#ifdef __native_client_codegen__
if (g_getenv ("MONO_NACL_ALIGN_MASK_OFF"))
{
diff --git a/mono/mini/mini.c b/mono/mini/mini.c
index 616d696..2a0e06c 100644
--- a/mono/mini/mini.c
+++ b/mono/mini/mini.c
@@ -53,6 +53,7 @@
#include <mono/utils/mono-math.h>
#include <mono/utils/mono-compiler.h>
#include <mono/utils/mono-counters.h>
+#include <mono/utils/mono-counters-internals.h>
#include <mono/utils/mono-logger-internal.h>
#include <mono/utils/mono-mmap.h>
#include <mono/utils/mono-path.h>
@@ -7643,7 +7644,7 @@ MonoJitStats mono_jit_stats = {0};
static void
print_jit_stats (void)
{
- if (mono_jit_stats.enabled) {
+ if (mono_jit_stats.enabled && mono_counters_dump_on_cleanup_enable()) {
g_print ("Mono Jit statistics\n");
g_print ("Max code size ratio: %.2f (%s)\n", mono_jit_stats.max_code_size_ratio/100.0,
mono_jit_stats.max_ratio_method);
diff --git a/mono/utils/Makefile.am b/mono/utils/Makefile.am
index 1a4f6ac..2e7961c 100644
--- a/mono/utils/Makefile.am
+++ b/mono/utils/Makefile.am
@@ -21,7 +21,7 @@ monoutils_sources = \
dlmalloc.c \
mono-counters.c \
mono-compiler.h \
- mono-counters-internals.h \
+ mono-compiler-internals.h \
mono-dl.c \
mono-dl.h \
mono-internal-hash.c \
@@ -69,6 +69,7 @@ monoutils_sources = \
monobitset.h \
mono-codeman.h \
mono-counters.h \
+ mono-counters-internals.h \
mono-digest.h \
mono-error.h \
mono-machine.h \
@@ -111,7 +112,9 @@ monoutils_sources = \
mono-hwcap.c \
bsearch.h \
bsearch.c \
- mono-signal-handler.h
+ mono-signal-handler.h \
+ mono-counters-agent.c \
+ mono-counters-agent.h
arch_sources =
diff --git a/mono/utils/mono-counters-agent.c b/mono/utils/mono-counters-agent.c
new file mode 100644
index 0000000..2737c17
--- /dev/null
+++ b/mono/utils/mono-counters-agent.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright 2014 Xamarin Inc
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <pthread.h>
+#include <glib.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include "mono-counters.h"
+#include "mono-counters-internals.h"
+#include "mono-counters-agent.h"
+#include "mono-time.h"
+
+/**
+ * Protocol :
+ * | Headers | Values | Values | ... [Infinity]
+ *
+ * Headers :
+ * | Count (2) | Counter | Counter | ... [Count]
+ *
+ * Counter :
+ * | Category (4) | Name Length (8) | Name (Name Length) | Type (4) | Unit (4) | Variance (4) | Index (2) |
+ *
+ * Values :
+ * | Timestamp (8) | Value | Value | ... | -1 (2) |
+ *
+ * Value :
+ * | Index (2) | Size (2) | Value (Size) |
+ */
+
+typedef struct MonoCounterAgent {
+ MonoCounter* counter;
+ // MonoCounterAgent specific data :
+ void* value;
+ short index;
+} MonoCounterAgent;
+
+static pthread_t agent_thread;
+
+static GSList* counters;
+
+static const gchar* inspector_ip;
+static int inspector_port;
+static int frequency;
+
+static int enable;
+
+int
+parse_counters_all (MonoCounter* counter, void** args)
+{
+ static short index = 0;
+
+ MonoCounterAgent* _counter = g_malloc(sizeof(MonoCounterAgent));
+ _counter->counter = counter;
+ _counter->value = NULL;
+ _counter->index = index;
+
+ counters = g_slist_append (counters, _counter);
+ index += 1;
+
+ return 1;
+}
+
+void
+parse_counters_names (const char *counters_names)
+{
+ if (!counters_names) {
+ mono_counters_foreach(parse_counters_all, NULL);
+ } else {
+ short index = 0;
+ gchar **names = g_strsplit (counters_names, ";", -1), **ptr;
+
+ for (ptr = names; *ptr; ++ptr) {
+ gchar **split = g_strsplit(*ptr, "/", 2);
+ if (!split[0] || !split[1])
+ continue; //FIXME warning
+
+ MonoCounterCategory category = mono_counters_category_name_to_id (split[0]);
+ MonoCounter* counter = mono_counters_get (category, split[1]);
+ if (!counter)
+ continue; // FIXME warning=
+
+ MonoCounterAgent* _counter = g_malloc(sizeof(MonoCounterAgent));
+ _counter->counter = counter;
+ _counter->index = index;
+ _counter->value = NULL;
+
+ counters = g_slist_append (counters, _counter);
+ index += 1;
+
+ g_strfreev(split);
+ }
+ g_strfreev (names);
+ }
+}
+
+void
+parse_address (const char *address)
+{
+ if (!address) {
+ inspector_ip = "127.0.0.1";
+ inspector_port = 8888;
+ } else {
+ gchar **split = g_strsplit(address, ":", 2);
+
+ inspector_ip = (split[0]) ? g_strdup(split[0]) : "127.0.0.1";
+ inspector_port = (split[1]) ? strtol(split[1], NULL, 10) : 8888;
+
+ g_strfreev(split);
+ }
+}
+
+int
+write_buffer_to_socket(int socketfd, char* buffer, ssize_t size)
+{
+ ssize_t left = size, sent;
+
+ while (left > 0) {
+ sent = send(socketfd, buffer + (size - left), left, 0);
+ if (sent <= 0)
+ return 0;
+ left -= sent;
+ }
+
+ return 1;
+}
+
+static void*
+mono_counters_agent_sampling_thread (void* ptr)
+{
+ int socketfd = 0;
+ struct sockaddr_in inspector_addr;
+
+ if ((socketfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ g_printf("mono-counters-agent | error with socket : %s", strerror(errno));
+ return NULL;
+ }
+
+ memset(&inspector_addr, 0, sizeof(inspector_addr));
+
+ inspector_addr.sin_family = AF_INET;
+ inspector_addr.sin_port = htons(inspector_port);
+
+ if (!inet_pton(AF_INET, inspector_ip, &inspector_addr.sin_addr)) {
+ g_printf("mono-counters-agent | error with inet_pton : %s", strerror(errno));
+ return NULL;
+ }
+
+ if (connect(socketfd, (struct sockaddr*)&inspector_addr, sizeof(inspector_addr)) < 0) {
+ g_printf("mono-counters-agent | error with connect : %s", strerror(errno));
+ return NULL;
+ }
+
+ GSList* item;
+ char* buffer = g_malloc(8);
+
+ short count = 0;
+ for (item = counters; item; item = item->next)
+ count += 1;
+
+ if (!write_buffer_to_socket(socketfd, (char*)&count, 2))
+ goto cleanup;
+
+ for (item = counters; item; item = item->next) {
+ MonoCounterAgent* counter = item->data;
+
+ int len = strlen(counter->counter->name);
+
+ if (!write_buffer_to_socket(socketfd, (char*)&counter->counter->category, 4)
+ || !write_buffer_to_socket(socketfd, (char*)&len, 4)
+ || !write_buffer_to_socket(socketfd, (char*) counter->counter->name, len)
+ || !write_buffer_to_socket(socketfd, (char*)&counter->counter->type, 4)
+ || !write_buffer_to_socket(socketfd, (char*)&counter->counter->unit, 4)
+ || !write_buffer_to_socket(socketfd, (char*)&counter->counter->variance, 4)
+ || !write_buffer_to_socket(socketfd, (char*)&counter->index, 2))
+ goto cleanup; // FIXME error
+ }
+
+ while (enable) {
+ gint64 timestamp = mono_100ns_ticks();
+ if (!write_buffer_to_socket(socketfd, (char*)&timestamp, 8))
+ goto cleanup; // FIXME error
+
+ for (item = counters; item; item = item->next) {
+ MonoCounterAgent* counter = item->data;
+
+ if (!counter)
+ continue; // FIXME warning
+
+ int size = mono_counters_size(counter->counter);
+
+ if (mono_counters_sample (counter->counter, buffer, size) != size)
+ goto cleanup;
+
+ if (!counter->value)
+ counter->value = g_malloc(size);
+ else if (memcmp(counter->value, buffer, size) == 0)
+ continue;
+
+ memcpy(counter->value, buffer, size);
+
+ if (!write_buffer_to_socket(socketfd, (char*)&counter->index, 2)
+ || !write_buffer_to_socket(socketfd, (char*)&size, 2)
+ || !write_buffer_to_socket(socketfd, (char*) buffer, size))
+ goto cleanup;
+ }
+
+ short end = -1;
+ if (!write_buffer_to_socket(socketfd, (char*)&end, 2))
+ goto cleanup; // FIXME error
+
+ usleep (1000000 / frequency);
+ }
+
+cleanup:
+ close(socketfd);
+ g_free(buffer);
+
+ return NULL;
+}
+
+void
+parse_configuration (const gchar* configuration)
+{
+ const char *counters_names = NULL;
+ const char *address = NULL;
+
+ if (configuration == NULL)
+ return;
+
+ gchar **opts = g_strsplit (configuration, ",", -1), **ptr;
+ for (ptr = opts; *ptr; ++ptr) {
+ const char *opt = *ptr;
+ if (g_str_has_prefix (opt, "address=")) {
+ opt = strchr (opt, '=') + 1;
+ address = g_strdup(opt);
+ } else if (g_str_has_prefix (opt, "counters=")) {
+ opt = strchr (opt, '=') + 1;
+ counters_names = g_strdup(opt);
+ } else if (g_str_has_prefix (opt, "frequency=")) {
+ opt = strchr (opt, '=') + 1;
+ frequency = strtol (opt, NULL, 10);
+ }
+ }
+
+ parse_counters_names(counters_names);
+ parse_address(address);
+
+ g_free((void*)counters_names);
+ g_free((void*)address);
+ g_strfreev(opts);
+}
+
+void
+mono_counters_agent_start (void)
+{
+ enable = 1;
+
+ // counters = g_slist_alloc();
+
+ parse_configuration(g_getenv ("MONO_PERF_AGENT"));
+ pthread_create(&agent_thread, NULL, mono_counters_agent_sampling_thread, NULL);
+}
+
+void
+mono_counters_agent_stop(void)
+{
+ enable = 0;
+
+ g_slist_free(counters);
+
+ pthread_join(agent_thread, NULL);
+}
\ No newline at end of file
diff --git a/mono/utils/mono-counters-agent.h b/mono/utils/mono-counters-agent.h
new file mode 100644
index 0000000..c7e275a
--- /dev/null
+++ b/mono/utils/mono-counters-agent.h
@@ -0,0 +1,7 @@
+#ifndef __MONO_COUNTERS_AGENTS_H__
+#define __MONO_COUNTERS_AGENTS_H__
+
+MONO_API void mono_counters_agent_start (void);
+MONO_API void mono_counters_agent_stop (void);
+
+#endif /* __MONO_COUNTERS_AGENTS_H__ */
\ No newline at end of file
diff --git a/mono/utils/mono-counters-internals.h b/mono/utils/mono-counters-internals.h
index a8cbfef..22304bf 100644
--- a/mono/utils/mono-counters-internals.h
+++ b/mono/utils/mono-counters-internals.h
@@ -1,6 +1,7 @@
#ifndef __MONO_COUNTERS_INTERNALS_H__
#define __MONO_COUNTERS_INTERNALS_H__
+#include <glib.h>
#include "mono-counters.h"
#include "mono-compiler.h"
@@ -41,7 +42,16 @@ typedef enum {
MONO_COUNTER_UNIT_VARIABLE, /* This counter value can be anything on each sampling */
} MonoCounterVariance;
-typedef struct _MonoCounter MonoCounter;
+typedef struct _MonoCounter {
+ const char* name;
+ void* addr;
+ MonoCounterType type;
+ MonoCounterCategory category;
+ MonoCounterUnit unit;
+ MonoCounterVariance variance;
+ gboolean is_callback;
+} MonoCounter;
+
/*
Limitations:
The old-style string counter type won't work as they cannot be safely sampled during execution.
@@ -68,5 +78,19 @@ mono_counters_register_full (MonoCounterCategory category, const char *name, Mon
#define mono_counters_new_long_const(cat,name,unit,value) do { gint64 *__ptr = mono_counters_new(cat,name,MONO_COUNTER_TYPE_INT,unit,variance); *__ptr = value; } while (0)
#define mono_counters_new_double_const(cat,name,unit,value) do { double *__ptr = mono_counters_new(cat,name,MONO_COUNTER_TYPE_INT,unit,variance); *__ptr = value; } while (0)
+MONO_API void mono_counters_dump_on_cleanup (void);
+MONO_API int mono_counters_dump_on_cleanup_enable (void);
+
+MONO_API MonoCounter* mono_counters_get (MonoCounterCategory category, const char* name);
+MONO_API int mono_counters_sample (MonoCounter* counter, char* buffer, int size);
+MONO_API int mono_counters_size (MonoCounter* counter);
+
+MONO_API MonoCounterCategory mono_counters_category_name_to_id (const char* name);
+MONO_API const char* mono_counters_category_id_to_name (MonoCounterCategory id);
+
+typedef int (*MonoCountersForeachFunc) (MonoCounter* counter, void** args);
+MONO_API void mono_counters_foreach(MonoCountersForeachFunc func, void** args);
+
+
+#endif /* __MONO_COUNTERS_INTERNALS_H__ */
-#endif
diff --git a/mono/utils/mono-counters.c b/mono/utils/mono-counters.c
index 9f22e01..430500b 100644
--- a/mono/utils/mono-counters.c
+++ b/mono/utils/mono-counters.c
@@ -5,22 +5,14 @@
#include <stdlib.h>
#include <glib.h>
+#include "mono-counters.h"
#include "mono-counters-internals.h"
-struct _MonoCounter {
- MonoCounter *next;
- const char *name;
- void *addr;
- MonoCounterType type;
- MonoCounterCategory category;
- MonoCounterUnit unit;
- MonoCounterVariance variance;
- gboolean is_callback;
-};
-
-static MonoCounter *counters = NULL;
+static GSList *counters = NULL;
static int valid_mask = 0;
+static int dump_on_cleanup = 0;
+
/**
* mono_counters_enable:
* @section_mask: a mask listing the sections that will be displayed
@@ -46,17 +38,9 @@ mono_counters_register_full (MonoCounterCategory category, const char *name, Mon
counter->category = category;
counter->unit = unit;
counter->variance = variance;
- counter->next = NULL;
-
- /* Append */
- if (counters) {
- MonoCounter *item = counters;
- while (item->next)
- item = item->next;
- item->next = counter;
- } else {
- counters = counter;
- }
+
+ counters = g_slist_append (counters, counter);
+
return counter;
}
@@ -104,6 +88,18 @@ section_to_category (int type)
}
}
+void
+mono_counters_dump_on_cleanup (void)
+{
+ dump_on_cleanup = 1;
+}
+
+int
+mono_counters_dump_on_cleanup_enable (void)
+{
+ return dump_on_cleanup;
+}
+
/**
* mono_counters_register:
* @name: The name for this counters.
@@ -153,6 +149,8 @@ mono_counters_register (const char* name, int type, void *addr)
counter = mono_counters_register_full (cat, name, counter_type, unit, MONO_COUNTER_UNIT_VARIABLE, addr);
if (counter && type & MONO_COUNTER_CALLBACK)
counter->is_callback = TRUE;
+ else
+ counter->is_callback = FALSE;
}
@@ -220,11 +218,12 @@ section_names [][10] = {
static void
mono_counters_dump_category (MonoCounterCategory category, FILE *outfile)
{
- MonoCounter *counter = counters;
- while (counter) {
+ GSList *item = counters;
+ while (item) {
+ MonoCounter* counter = item->data;
if (counter->category == category)
dump_counter (counter, outfile);
- counter = counter->next;
+ item = item->next;
}
}
@@ -238,6 +237,9 @@ mono_counters_dump_category (MonoCounterCategory category, FILE *outfile)
void
mono_counters_dump (int section_mask, FILE *outfile)
{
+ if (!dump_on_cleanup)
+ return;
+
int i, j;
section_mask &= valid_mask;
if (!counters)
@@ -260,13 +262,8 @@ mono_counters_dump (int section_mask, FILE *outfile)
void
mono_counters_cleanup (void)
{
- MonoCounter *counter = counters;
- counters = NULL;
- while (counter) {
- MonoCounter *tmp = counter;
- counter = counter->next;
- free (tmp);
- }
+ if (counters)
+ g_slist_free(counters);
}
static MonoResourceCallback limit_reached = NULL;
@@ -334,4 +331,156 @@ mono_runtime_resource_set_callback (MonoResourceCallback callback)
limit_reached = callback;
}
+MonoCounter*
+mono_counters_get (MonoCounterCategory category, const char* name)
+{
+ if (!counters)
+ return NULL;
+
+ GSList* item = counters;
+
+ do {
+ MonoCounter* counter = item->data;
+ if (counter->category == category && strcmp(counter->name, name) ==0)
+ return counter;
+ } while ((item = item->next));
+}
+
+int
+mono_counters_sample (MonoCounter* counter, char* buffer, int size)
+{
+ switch (counter->type) {
+ case MONO_COUNTER_TYPE_INT:
+#if SIZEOF_VOID_P == 4
+ case MONO_COUNTER_TYPE_WORD:
+#endif
+ if (size < 4)
+ return -1;
+
+ if (counter->is_callback) {
+ int value = ((IntFunc)counter->addr) ();
+ memcpy(buffer, &value, 4);
+ } else {
+ memcpy(buffer, counter->addr, 4);
+ }
+
+ return 4;
+ case MONO_COUNTER_TYPE_LONG:
+#if SIZEOF_VOID_P == 8
+ case MONO_COUNTER_TYPE_WORD:
+#endif
+ if (size < 8)
+ return -1;
+
+ if (counter->is_callback) {
+ long value = ((LongFunc)counter->addr) ();
+ memcpy(buffer, &value, 8);
+ } else {
+ memcpy(buffer, counter->addr, 8);
+ }
+
+ return 8;
+ case MONO_COUNTER_DOUBLE:
+ if (size < 8)
+ return -1;
+
+ if (counter->is_callback) {
+ double value = ((DoubleFunc)counter->addr) ();
+ memcpy(buffer, &value, 8);
+ } else {
+ memcpy(buffer, counter->addr, 8);
+ }
+
+ return 8;
+ }
+}
+
+int
+mono_counters_size (MonoCounter* counter)
+{
+ switch (counter->type) {
+ case MONO_COUNTER_TYPE_INT:
+#if SIZEOF_VOID_P == 4
+ case MONO_COUNTER_TYPE_WORD:
+#endif
+ return 4;
+ case MONO_COUNTER_TYPE_LONG:
+#if SIZEOF_VOID_P == 8
+ case MONO_COUNTER_TYPE_WORD:
+#endif
+ case MONO_COUNTER_DOUBLE:
+ return 8;
+ }
+}
+
+MonoCounterCategory
+mono_counters_category_name_to_id (const char* name)
+{
+ if (strcmp("Mono JIT", name) == 0) {
+ return MONO_COUNTER_CAT_JIT;
+ } else if (strcmp("Mono GC", name) == 0) {
+ return MONO_COUNTER_CAT_GC;
+ } else if (strcmp("Mono Metadata", name) == 0) {
+ return MONO_COUNTER_CAT_METADATA;
+ } else if (strcmp("Mono Generics", name) == 0) {
+ return MONO_COUNTER_CAT_GENERICS;
+ } else if (strcmp("Mono Security", name) == 0) {
+ return MONO_COUNTER_CAT_SECURITY;
+ } else if (strcmp("Mono Remoting", name) == 0) {
+ return MONO_COUNTER_CAT_REMOTING;
+ } else if (strcmp("Mono EXC", name) == 0) {
+ return MONO_COUNTER_CAT_EXC;
+ } else if (strcmp("Mono Thread", name) == 0) {
+ return MONO_COUNTER_CAT_THREAD;
+ } else if (strcmp("Mono Threadpool", name) == 0) {
+ return MONO_COUNTER_CAT_THREADPOOL;
+ } else if (strcmp("Mono IO", name) == 0) {
+ return MONO_COUNTER_CAT_IO;
+ } else {
+ return -1;
+ }
+}
+
+const char*
+mono_counters_category_id_to_name (MonoCounterCategory id)
+{
+ switch (id) {
+ case MONO_COUNTER_CAT_JIT:
+ return "Mono JIT";
+ case MONO_COUNTER_CAT_GC:
+ return "Mono GC";
+ case MONO_COUNTER_CAT_METADATA:
+ return "Mono Metadata";
+ case MONO_COUNTER_CAT_GENERICS:
+ return "Mono Generics";
+ case MONO_COUNTER_CAT_SECURITY:
+ return "Mono Security";
+ case MONO_COUNTER_CAT_REMOTING:
+ return "Mono Remoting";
+ case MONO_COUNTER_CAT_EXC:
+ return "Mono EXC";
+ case MONO_COUNTER_CAT_THREAD:
+ return "Mono Thread";
+ case MONO_COUNTER_CAT_THREADPOOL:
+ return "Mono Threadpool";
+ case MONO_COUNTER_CAT_IO:
+ return "Mono IO";
+ default:
+ return NULL;
+ }
+}
+
+void
+mono_counters_foreach(MonoCountersForeachFunc func, void** args)
+{
+ if (!counters)
+ return;
+
+ GSList* item = counters;
+
+ do {
+ if (!func(item->data, args))
+ return;
+ } while ((item = item->next));
+}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment