[profiler] log profiler: limit method instrumentation to selected methods (#5517)
authorUri Simchoni <urisimchoni@users.noreply.github.com>
Mon, 2 Oct 2017 14:29:49 +0000 (17:29 +0300)
committerZoltan Varga <vargaz@gmail.com>
Mon, 2 Oct 2017 14:29:49 +0000 (16:29 +0200)
* [trace] remove code that has no effect

* [trace] move program assembly out of MonoTraceSpec

The assembly member represents the program assembly. It's
information the trace spec is matched against, not part of
the trace spec per-se (if two trace specs exist for two purposes
we would still have one program assembly)

* [trace] remove side effects from get_string()

On the way towards supporting multiple trace specs,
remove side effects from get_string() function.

* [trace] remove side effects from get_token()

Remove side effects from get_token() to allow handling
multiple trace specs.

* [trace] fix handling of double exclusion and disabled

When encountering "disabled", do not add an entry to
the trace spec

Fix error handling around double "-" - get_spec()
never returns TOKEN_EXCLUDE and the recursion can
be easily avoided.

* [trace] remove side effects from get_spec()

Another step in making the trace options reusable.

* [trace] rename mono_trace_parse_options() to mono_trace_set_options()

This routine actually sets the tracing options, not just parses an
option string, so set_ is more suitable. This frees up the
mono_trace_parse_options() name for pure parsing, when we later
reuse the parsing code.

* [trace] make tracing options API reusable

Add APIs that parse tracing options and evaluate a method
against the parsed options. The tracing functionality now uses
this API, but other components can use this to apply an operation
to a method based on policy (e.g. profiling)

* [trace] rename MonoTraceSpec to MonoCallSpec

In preparation for reusing call specification beyond the
tracer, rename the data structure - it now only specifies
a set of calls, without indication what should be done with
them.

* [trace] rename mono_trace_set_assembly to mono_callspec_set_assembly

As it becoming a reuse candidate, we remove the "trace" component
from the name.

* [trace-metadata] move callspec code into its own module

Introduce callspec.c/h which encapsulate the call specification
functionality.

* [profiler] add "callspec" option to log profiler

The callspec option define which methods get instrumented
at JIT time with entry/exit calls to the profiler. The
syntax is same as the tracer (--trace=) syntax. To
distinguish between profiler options and callspec, wrap the
callspec with double quotes, as in:
--profile=log:callspec="all-mscorlib",calls

Since this typically runs from a shell, the double quotes have to be
escaped or wrapped in single quotes, as in:
mono '--profile=log:callspec="all-mscorlib",calls' prog.exe

* [metadata] add an error return string to callspec parsing

Instead of printing parsing error messages to standard error,
return an error string. That allows the client to do the right thing
with the error.

12 files changed:
mono/metadata/Makefile.am
mono/metadata/callspec.c [new file with mode: 0644]
mono/metadata/callspec.h [new file with mode: 0644]
mono/mini/driver.c
mono/mini/mini.c
mono/mini/mini.h
mono/mini/trace.c
mono/mini/trace.h
mono/profiler/log-args.c
mono/profiler/log.c
mono/profiler/log.h
msvc/libmonoruntime.vcxproj

index 2fbf826ceece5a6338eadd77b1deb9d5267801e0..8a9494054d94eaf9124c9c2731160131de14af16 100644 (file)
@@ -283,7 +283,9 @@ common_sources = \
        sre-save.c      \
        custom-attrs.c  \
        fdhandle.h      \
        sre-save.c      \
        custom-attrs.c  \
        fdhandle.h      \
-       fdhandle.c
+       fdhandle.c      \
+       callspec.h      \
+       callspec.c
 
 # These source files have compile time dependencies on GC code
 gc_dependent_sources = \
 
 # These source files have compile time dependencies on GC code
 gc_dependent_sources = \
diff --git a/mono/metadata/callspec.c b/mono/metadata/callspec.c
new file mode 100644 (file)
index 0000000..bf108d9
--- /dev/null
@@ -0,0 +1,348 @@
+/**
+ * \file
+ * Call specification facilities for the Mono Runtime.
+ *
+ * Author:
+ *   Paolo Molaro (lupus@ximian.com)
+ *   Dietmar Maurer (dietmar@ximian.com)
+ *
+ * (C) 2002 Ximian, Inc.
+ * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
+ * Licensed under the MIT license. See LICENSE file in the project root for full
+ * license information.
+ */
+#include "metadata.h"
+#include "callspec.h"
+#include "assembly.h"
+#include "class-internals.h"
+#include "debug-helpers.h"
+
+static MonoAssembly *prog_assembly;
+
+gboolean
+mono_callspec_eval_exception (MonoClass *klass, MonoCallSpec *spec)
+{
+       int include = 0;
+       int i;
+
+       if (!klass)
+               return FALSE;
+
+       for (i = 0; i < spec->len; i++) {
+               MonoTraceOperation *op = &spec->ops [i];
+               int inc = 0;
+
+               switch (op->op) {
+               case MONO_TRACEOP_EXCEPTION:
+                       if (strcmp ("", op->data) == 0 &&
+                           strcmp ("all", op->data2) == 0)
+                               inc = 1;
+                       else if (strcmp ("", op->data) == 0 ||
+                                strcmp (klass->name_space, op->data) == 0)
+                               if (strcmp (klass->name, op->data2) == 0)
+                                       inc = 1;
+                       break;
+               default:
+                       break;
+               }
+               if (op->exclude) {
+                       if (inc)
+                               include = 0;
+               } else if (inc)
+                       include = 1;
+       }
+
+       return include;
+}
+
+gboolean mono_callspec_eval (MonoMethod *method, const MonoCallSpec *spec)
+{
+       int include = 0;
+       int i;
+
+       for (i = 0; i < spec->len; i++) {
+               MonoTraceOperation *op = &spec->ops[i];
+               int inc = 0;
+
+               switch (op->op) {
+               case MONO_TRACEOP_ALL:
+                       inc = 1;
+                       break;
+               case MONO_TRACEOP_PROGRAM:
+                       if (prog_assembly &&
+                           (method->klass->image ==
+                            mono_assembly_get_image (prog_assembly)))
+                               inc = 1;
+                       break;
+               case MONO_TRACEOP_WRAPPER:
+                       if ((method->wrapper_type ==
+                            MONO_WRAPPER_NATIVE_TO_MANAGED) ||
+                           (method->wrapper_type ==
+                            MONO_WRAPPER_MANAGED_TO_NATIVE))
+                               inc = 1;
+                       break;
+               case MONO_TRACEOP_METHOD:
+                       if (mono_method_desc_full_match (
+                               (MonoMethodDesc *)op->data, method))
+                               inc = 1;
+                       break;
+               case MONO_TRACEOP_CLASS:
+                       if (strcmp (method->klass->name_space, op->data) == 0)
+                               if (strcmp (method->klass->name, op->data2) ==
+                                   0)
+                                       inc = 1;
+                       break;
+               case MONO_TRACEOP_ASSEMBLY:
+                       if (strcmp (mono_image_get_name (method->klass->image),
+                                   op->data) == 0)
+                               inc = 1;
+                       break;
+               case MONO_TRACEOP_NAMESPACE:
+                       if (strcmp (method->klass->name_space, op->data) == 0)
+                               inc = 1;
+                       break;
+               case MONO_TRACEOP_EXCEPTION:
+                       break;
+               }
+               if (op->exclude) {
+                       if (inc)
+                               include = 0;
+               } else if (inc) {
+                       include = 1;
+               }
+       }
+       return include;
+}
+
+static int is_filenamechar (char p)
+{
+       if (p >= 'A' && p <= 'Z')
+               return TRUE;
+       if (p >= 'a' && p <= 'z')
+               return TRUE;
+       if (p >= '0' && p <= '9')
+               return TRUE;
+       if (p == '.' || p == ':' || p == '_' || p == '-' || p == '`')
+               return TRUE;
+       return FALSE;
+}
+
+static char *get_string (char **in)
+{
+       char *start = *in;
+       char *p = *in;
+       while (is_filenamechar (*p)) {
+               p++;
+       }
+       size_t len = p - start;
+       char *ret = (char *)g_malloc (len + 1);
+       memcpy (ret, start, len);
+       ret [len] = 0;
+       *in = p;
+       return ret;
+}
+
+enum Token {
+       TOKEN_METHOD,
+       TOKEN_CLASS,
+       TOKEN_ALL,
+       TOKEN_PROGRAM,
+       TOKEN_EXCEPTION,
+       TOKEN_NAMESPACE,
+       TOKEN_WRAPPER,
+       TOKEN_STRING,
+       TOKEN_EXCLUDE,
+       TOKEN_DISABLED,
+       TOKEN_SEPARATOR,
+       TOKEN_END,
+       TOKEN_ERROR
+};
+
+static int get_token (char **in, char **extra, char **errstr)
+{
+       char *p = *in;
+       while (p[0] == '+')
+               p++;
+
+       *extra = NULL;
+
+       if (p[0] == '\0') {
+               *in = p;
+               return TOKEN_END;
+       }
+       if (p[0] == 'M' && p[1] == ':') {
+               p += 2;
+               *extra = get_string (&p);
+               *in = p;
+               return TOKEN_METHOD;
+       }
+       if (p[0] == 'N' && p[1] == ':') {
+               p += 2;
+               *extra = get_string (&p);
+               *in = p;
+               return TOKEN_NAMESPACE;
+       }
+       if (p[0] == 'T' && p[1] == ':') {
+               p += 2;
+               *extra = get_string (&p);
+               *in = p;
+               return TOKEN_CLASS;
+       }
+       if (p[0] == 'E' && p[1] == ':') {
+               p += 2;
+               *extra = get_string (&p);
+               *in = p;
+               return TOKEN_EXCEPTION;
+       }
+       if (*p == '-') {
+               p++;
+               *in = p;
+               return TOKEN_EXCLUDE;
+       }
+       if (is_filenamechar (*p)) {
+               *extra = get_string (&p);
+               *in = p;
+               if (strcmp (*extra, "all") == 0)
+                       return TOKEN_ALL;
+               if (strcmp (*extra, "program") == 0)
+                       return TOKEN_PROGRAM;
+               if (strcmp (*extra, "wrapper") == 0)
+                       return TOKEN_WRAPPER;
+               if (strcmp (*extra, "disabled") == 0)
+                       return TOKEN_DISABLED;
+               return TOKEN_STRING;
+       }
+       if (*p == ',') {
+               p++;
+               *in = p;
+               return TOKEN_SEPARATOR;
+       }
+
+       *errstr = g_strdup_printf ("Syntax error at or around '%s'", p);
+       return TOKEN_ERROR;
+}
+
+static int get_spec (char **in, MonoCallSpec *spec, char **errstr)
+{
+       int n = spec->len;
+       char *extra = NULL;
+
+       int token = get_token (in, &extra, errstr);
+       gboolean exclude = FALSE;
+       if (token == TOKEN_EXCLUDE) {
+               exclude = TRUE;
+               token = get_token (in, &extra, errstr);
+               if (token == TOKEN_EXCLUDE || token == TOKEN_DISABLED) {
+                       *errstr = g_strdup_printf ("Expecting an expression");
+                       token = TOKEN_ERROR;
+                       goto out;
+               }
+       }
+       if (token == TOKEN_END || token == TOKEN_SEPARATOR ||
+           token == TOKEN_ERROR)
+               goto out;
+
+       if (token == TOKEN_DISABLED) {
+               spec->enabled = FALSE;
+               goto out;
+       }
+
+       if (token == TOKEN_METHOD) {
+               MonoMethodDesc *desc = mono_method_desc_new (extra, TRUE);
+               if (desc == NULL) {
+                       *errstr =
+                           g_strdup_printf ("Invalid method name: %s", extra);
+                       token = TOKEN_ERROR;
+                       goto out;
+               }
+               spec->ops[n].op = MONO_TRACEOP_METHOD;
+               spec->ops[n].data = desc;
+       } else if (token == TOKEN_ALL)
+               spec->ops[n].op = MONO_TRACEOP_ALL;
+       else if (token == TOKEN_PROGRAM)
+               spec->ops[n].op = MONO_TRACEOP_PROGRAM;
+       else if (token == TOKEN_WRAPPER)
+               spec->ops[n].op = MONO_TRACEOP_WRAPPER;
+       else if (token == TOKEN_NAMESPACE) {
+               spec->ops[n].op = MONO_TRACEOP_NAMESPACE;
+               spec->ops[n].data = g_strdup (extra);
+       } else if (token == TOKEN_CLASS || token == TOKEN_EXCEPTION) {
+               char *p = strrchr (extra, '.');
+               if (p) {
+                       *p++ = 0;
+                       spec->ops[n].data = g_strdup (extra);
+                       spec->ops[n].data2 = g_strdup (p);
+               } else {
+                       spec->ops[n].data = g_strdup ("");
+                       spec->ops[n].data2 = g_strdup (extra);
+               }
+               spec->ops[n].op = token == TOKEN_CLASS ? MONO_TRACEOP_CLASS
+                                                      : MONO_TRACEOP_EXCEPTION;
+       } else if (token == TOKEN_STRING) {
+               spec->ops[n].op = MONO_TRACEOP_ASSEMBLY;
+               spec->ops[n].data = g_strdup (extra);
+       } else {
+               *errstr =
+                   g_strdup_printf ("Syntax error in method specification");
+               token = TOKEN_ERROR;
+               goto out;
+       }
+
+       if (exclude)
+               spec->ops[n].exclude = 1;
+
+       spec->len = n + 1;
+       token = TOKEN_SEPARATOR;
+out:
+       if (extra != NULL) {
+               g_free (extra);
+       }
+       return token;
+}
+
+gboolean
+mono_callspec_parse (const char *options, MonoCallSpec *spec, char **errstr)
+{
+       char *p = (char *)options;
+       int size = 1;
+       int token;
+
+       memset (spec, 0, sizeof (*spec));
+       *errstr = NULL;
+
+       spec->enabled = TRUE;
+       if (*p == 0) {
+               spec->len = 1;
+               spec->ops = g_new0 (MonoTraceOperation, 1);
+               spec->ops[0].op = MONO_TRACEOP_ALL;
+               return TRUE;
+       }
+
+       for (p = (char *)options; *p != 0; p++)
+               if (*p == ',')
+                       size++;
+
+       spec->ops = g_new0 (MonoTraceOperation, size);
+
+       p = (char *)options;
+
+       while ((token = (get_spec (&p, spec, errstr))) != TOKEN_END) {
+               if (token == TOKEN_ERROR)
+                       return FALSE;
+       }
+       return TRUE;
+}
+
+void mono_callspec_cleanup (MonoCallSpec *spec)
+{
+       if (spec->ops != NULL) {
+               g_free (spec->ops);
+       }
+       memset (spec, 0, sizeof (*spec));
+}
+
+void
+mono_callspec_set_assembly (MonoAssembly *assembly)
+{
+       prog_assembly = assembly;
+}
diff --git a/mono/metadata/callspec.h b/mono/metadata/callspec.h
new file mode 100644 (file)
index 0000000..c9f6998
--- /dev/null
@@ -0,0 +1,47 @@
+/**
+ * \file
+ */
+
+#ifndef __MONO_CALLSPEC_H__
+#define __MONO_CALLSPEC_H__
+#include <glib.h>
+#include <mono/utils/mono-compiler.h>
+
+typedef enum {
+       MONO_TRACEOP_ALL,
+       MONO_TRACEOP_PROGRAM,
+       MONO_TRACEOP_METHOD,
+       MONO_TRACEOP_ASSEMBLY,
+       MONO_TRACEOP_CLASS,
+       MONO_TRACEOP_NAMESPACE,
+       MONO_TRACEOP_EXCEPTION,
+       MONO_TRACEOP_WRAPPER,
+} MonoTraceOpcode;
+
+typedef struct {
+       MonoTraceOpcode op;
+       int exclude;
+       void *data, *data2;
+} MonoTraceOperation;
+
+typedef struct {
+       int len;
+       gboolean enabled;
+       MonoTraceOperation *ops;
+} MonoCallSpec;
+
+G_BEGIN_DECLS
+
+MONO_PROFILER_API gboolean mono_callspec_parse         (const char *options,
+                                                        MonoCallSpec *spec,
+                                                        char **errstr);
+MONO_PROFILER_API void     mono_callspec_cleanup       (MonoCallSpec *spec);
+MONO_PROFILER_API gboolean mono_callspec_eval_exception        (MonoClass *klass,
+                                                        MonoCallSpec *spec);
+MONO_PROFILER_API gboolean mono_callspec_eval          (MonoMethod *method,
+                                                        const MonoCallSpec *spec);
+void                      mono_callspec_set_assembly   (MonoAssembly *assembly);
+
+G_END_DECLS
+
+#endif /* __MONO_CALLSPEC_H__ */
index a364415e2575b91e95966579e838fdb30c633f2f..dbbb646297d7850c260d5732a98a05be25753627 100644 (file)
@@ -52,6 +52,7 @@
 #include "mono/utils/mono-hwcap.h"
 #include "mono/utils/mono-logger-internals.h"
 #include "mono/metadata/w32handle.h"
 #include "mono/utils/mono-hwcap.h"
 #include "mono/utils/mono-logger-internals.h"
 #include "mono/metadata/w32handle.h"
+#include "mono/metadata/callspec.h"
 
 #include "mini.h"
 #include "jit.h"
 
 #include "mini.h"
 #include "jit.h"
@@ -1454,7 +1455,7 @@ mono_jit_parse_options (int argc, char * argv[])
                 * Need to call this before mini_init () so we can trace methods 
                 * compiled there too.
                 */
                 * Need to call this before mini_init () so we can trace methods 
                 * compiled there too.
                 */
-               mono_jit_trace_calls = mono_trace_parse_options (trace_options);
+               mono_jit_trace_calls = mono_trace_set_options (trace_options);
                if (mono_jit_trace_calls == NULL)
                        exit (1);
        }
                if (mono_jit_trace_calls == NULL)
                        exit (1);
        }
@@ -2019,7 +2020,7 @@ mono_main (int argc, char* argv[])
                 * Need to call this before mini_init () so we can trace methods 
                 * compiled there too.
                 */
                 * Need to call this before mini_init () so we can trace methods 
                 * compiled there too.
                 */
-               mono_jit_trace_calls = mono_trace_parse_options (trace_options);
+               mono_jit_trace_calls = mono_trace_set_options (trace_options);
                if (mono_jit_trace_calls == NULL)
                        exit (1);
        }
                if (mono_jit_trace_calls == NULL)
                        exit (1);
        }
@@ -2140,8 +2141,7 @@ mono_main (int argc, char* argv[])
                return 2;
        }
 
                return 2;
        }
 
-       if (trace_options != NULL)
-               mono_trace_set_assembly (assembly);
+       mono_callspec_set_assembly (assembly);
 
        if (mono_compile_aot || action == DO_EXEC) {
                const char *error;
 
        if (mono_compile_aot || action == DO_EXEC) {
                const char *error;
@@ -2400,7 +2400,7 @@ mono_jit_aot_compiling (void)
 gboolean
 mono_jit_set_trace_options (const char* options)
 {
 gboolean
 mono_jit_set_trace_options (const char* options)
 {
-       MonoTraceSpec *trace_opt = mono_trace_parse_options (options);
+       MonoCallSpec *trace_opt = mono_trace_set_options (options);
        if (trace_opt == NULL)
                return FALSE;
        mono_jit_trace_calls = trace_opt;
        if (trace_opt == NULL)
                return FALSE;
        mono_jit_trace_calls = trace_opt;
index af04f0bb7966ac94eb40a7e290d3ca0d2b072549..dd2e12aa9512080c4494af31cf29b4c537535ce4 100644 (file)
@@ -79,7 +79,7 @@
 #include "mini-llvm.h"
 #include "lldb.h"
 
 #include "mini-llvm.h"
 #include "lldb.h"
 
-MonoTraceSpec *mono_jit_trace_calls;
+MonoCallSpec *mono_jit_trace_calls;
 MonoMethodDesc *mono_inject_async_exc_method;
 int mono_inject_async_exc_pos;
 MonoMethodDesc *mono_break_at_bb_method;
 MonoMethodDesc *mono_inject_async_exc_method;
 int mono_inject_async_exc_pos;
 MonoMethodDesc *mono_break_at_bb_method;
index f8d052627cd72c45b10e5563471039bbb43eb0b9..42a0356ef839cfc17c5bf341d1b088f247898055 100644 (file)
@@ -48,6 +48,7 @@
 #include "mono/metadata/marshal.h"
 #include "mono/metadata/security-manager.h"
 #include "mono/metadata/exception.h"
 #include "mono/metadata/marshal.h"
 #include "mono/metadata/security-manager.h"
 #include "mono/metadata/exception.h"
+#include "mono/metadata/callspec.h"
 
 /*
  * The mini code should not have any compile time dependencies on the GC being used, so the same object file from mini/
 
 /*
  * The mini code should not have any compile time dependencies on the GC being used, so the same object file from mini/
@@ -537,9 +538,8 @@ typedef struct MonoMethodVar MonoMethodVar;
 typedef struct MonoBasicBlock MonoBasicBlock;
 typedef struct MonoLMF MonoLMF;
 typedef struct MonoSpillInfo MonoSpillInfo;
 typedef struct MonoBasicBlock MonoBasicBlock;
 typedef struct MonoLMF MonoLMF;
 typedef struct MonoSpillInfo MonoSpillInfo;
-typedef struct MonoTraceSpec MonoTraceSpec;
 
 
-extern MonoTraceSpec *mono_jit_trace_calls;
+extern MonoCallSpec *mono_jit_trace_calls;
 extern gboolean mono_break_on_exc;
 extern int mono_exc_esp_offset;
 extern gboolean mono_compile_aot;
 extern gboolean mono_break_on_exc;
 extern int mono_exc_esp_offset;
 extern gboolean mono_compile_aot;
@@ -2992,8 +2992,7 @@ MONO_API void      mono_debugger_run_finally             (MonoContext *start_ctx
 MONO_API gboolean mono_breakpoint_clean_code (guint8 *method_start, guint8 *code, int offset, guint8 *buf, int size);
 
 /* Tracing */
 MONO_API gboolean mono_breakpoint_clean_code (guint8 *method_start, guint8 *code, int offset, guint8 *buf, int size);
 
 /* Tracing */
-MonoTraceSpec *mono_trace_parse_options         (const char *options);
-void           mono_trace_set_assembly          (MonoAssembly *assembly);
+MonoCallSpec *mono_trace_set_options           (const char *options);
 gboolean       mono_trace_eval                  (MonoMethod *method);
 
 extern void
 gboolean       mono_trace_eval                  (MonoMethod *method);
 
 extern void
index 3c916a941b3ddf62af09f0ed8db444584e76c84e..0d6b19e857eb2c75b717b1d90e0ca8126c19a81f 100644 (file)
 #include <string.h>
 #include "mini.h"
 #include <mono/metadata/debug-helpers.h>
 #include <string.h>
 #include "mini.h"
 #include <mono/metadata/debug-helpers.h>
-#include <mono/metadata/assembly.h>
 #include <mono/utils/mono-time.h>
 #include <mono/utils/mono-memory-model.h>
 #include "trace.h"
 #include <mono/utils/mono-time.h>
 #include <mono/utils/mono-memory-model.h>
 #include "trace.h"
+#include <mono/metadata/callspec.h>
 
 #if defined (HOST_ANDROID) || (defined (TARGET_IOS) && defined (TARGET_IOS))
 #  undef printf
 
 #if defined (HOST_ANDROID) || (defined (TARGET_IOS) && defined (TARGET_IOS))
 #  undef printf
 #  define fprintf(__ignore, ...) g_log ("mono-gc", G_LOG_LEVEL_MESSAGE, __VA_ARGS__)
 #endif
 
 #  define fprintf(__ignore, ...) g_log ("mono-gc", G_LOG_LEVEL_MESSAGE, __VA_ARGS__)
 #endif
 
-static MonoTraceSpec trace_spec;
+static MonoCallSpec trace_spec;
 
 static volatile gint32 output_lock = 0;
 
 
 static volatile gint32 output_lock = 0;
 
-gboolean
-mono_trace_eval_exception (MonoClass *klass)
-{
-       int include = 0;
-       int i;
-
-       if (!klass)
-               return FALSE;
-
-       for (i = 0; i < trace_spec.len; i++) {
-               MonoTraceOperation *op = &trace_spec.ops [i];
-               int inc = 0;
-               
-               switch (op->op){
-               case MONO_TRACEOP_EXCEPTION:
-                       if (strcmp ("", op->data) == 0 && strcmp ("all", op->data2) == 0)
-                               inc = 1;
-                       else if (strcmp ("", op->data) == 0 || strcmp (klass->name_space, op->data) == 0)
-                               if (strcmp (klass->name, op->data2) == 0)
-                                       inc = 1;
-                       break;
-               default:
-                       break;
-               }
-               if (op->exclude){
-                       if (inc)
-                               include = 0;
-               } else if (inc)
-                       include = 1;
-       }
-
-       return include;
-}
-
-gboolean
-mono_trace_eval (MonoMethod *method)
-{
-       int include = 0;
-       int i;
-
-       for (i = 0; i < trace_spec.len; i++){
-               MonoTraceOperation *op = &trace_spec.ops [i];
-               int inc = 0;
-               
-               switch (op->op){
-               case MONO_TRACEOP_ALL:
-                       inc = 1;
-                       break;
-               case MONO_TRACEOP_PROGRAM:
-                       if (trace_spec.assembly && (method->klass->image == mono_assembly_get_image (trace_spec.assembly)))
-                               inc = 1;
-                       break;
-               case MONO_TRACEOP_WRAPPER:
-                       if ((method->wrapper_type == MONO_WRAPPER_NATIVE_TO_MANAGED) ||
-                               (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE))
-                               inc = 1;
-                       break;
-               case MONO_TRACEOP_METHOD:
-                       if (mono_method_desc_full_match ((MonoMethodDesc *) op->data, method))
-                               inc = 1;
-                       break;
-               case MONO_TRACEOP_CLASS:
-                       if (strcmp (method->klass->name_space, op->data) == 0)
-                               if (strcmp (method->klass->name, op->data2) == 0)
-                                       inc = 1;
-                       break;
-               case MONO_TRACEOP_ASSEMBLY:
-                       if (strcmp (mono_image_get_name (method->klass->image), op->data) == 0)
-                               inc = 1;
-                       break;
-               case MONO_TRACEOP_NAMESPACE:
-                       if (strcmp (method->klass->name_space, op->data) == 0)
-                               inc = 1;
-                       break;
-               case MONO_TRACEOP_EXCEPTION:
-                       break;
-               }
-               if (op->exclude) {
-                       if (inc)
-                               include = 0;
-               } else if (inc) {
-                       include = 1;
-               }
-       }
-       return include;
-}
-
-static int is_filenamechar (char p)
-{
-       if (p >= 'A' && p <= 'Z')
-               return TRUE;
-       if (p >= 'a' && p <= 'z')
-               return TRUE;
-       if (p >= '0' && p <= '9')
-               return TRUE;
-       if (p == '.' || p == ':' || p == '_' || p == '-' || p == '`')
-               return TRUE;
-       return FALSE;
-}
-
-static char *input;
-static char *value;
-
-static void get_string (void)
+gboolean mono_trace_eval_exception (MonoClass *klass)
 {
 {
-       char *start = input;
-       while (is_filenamechar (*input)){
-               input++;
-       }
-       if (value != NULL)
-               g_free (value);
-       size_t len = input - start;
-       value = (char *)g_malloc (len + 1);
-       memcpy (value, start, len);
-       value [len] = 0;
+       return mono_callspec_eval_exception (klass, &trace_spec);
 }
 
 }
 
-enum Token {
-       TOKEN_METHOD,
-       TOKEN_CLASS,
-       TOKEN_ALL,
-       TOKEN_PROGRAM,
-       TOKEN_EXCEPTION,
-       TOKEN_NAMESPACE,
-       TOKEN_WRAPPER,
-       TOKEN_STRING,
-       TOKEN_EXCLUDE,
-       TOKEN_DISABLED,
-       TOKEN_SEPARATOR,
-       TOKEN_END,
-       TOKEN_ERROR
-};
-
-static int
-get_token (void)
+gboolean mono_trace_eval (MonoMethod *method)
 {
 {
-       while (input [0] == '+')
-               input++;
-
-       if (input [0] == '\0') {
-               return TOKEN_END;
-       }
-       if (input [0] == 'M' && input [1] == ':'){
-               input += 2;
-               get_string ();
-               return TOKEN_METHOD;
-       }
-       if (input [0] == 'N' && input [1] == ':'){
-               input += 2;
-               get_string ();
-               return TOKEN_NAMESPACE;
-       }
-       if (input [0] == 'T' && input [1] == ':'){
-               input += 2;
-               get_string ();
-               return TOKEN_CLASS;
-       }
-       if (input [0] == 'E' && input [1] == ':'){
-               input += 2;
-               get_string ();
-               return TOKEN_EXCEPTION;
-       }
-       if (*input == '-'){
-               input++;
-               return TOKEN_EXCLUDE;
-       }
-       if (is_filenamechar (*input)){
-               get_string ();
-               if (strcmp (value, "all") == 0)
-                       return TOKEN_ALL;
-               if (strcmp (value, "program") == 0)
-                       return TOKEN_PROGRAM;
-               if (strcmp (value, "wrapper") == 0)
-                       return TOKEN_WRAPPER;
-               if (strcmp (value, "disabled") == 0)
-                       return TOKEN_DISABLED;
-               return TOKEN_STRING;
-       }
-       if (*input == ','){
-               input++;
-               return TOKEN_SEPARATOR;
-       }
-
-       fprintf (stderr, "Syntax error at or around '%s'\n", input);    
-       return TOKEN_ERROR;
+       return mono_callspec_eval (method, &trace_spec);
 }
 
 }
 
-static void
-cleanup (void)
+MonoCallSpec *mono_trace_set_options (const char *options)
 {
 {
-       if (value != NULL)
-               g_free (value);
-}
-
-static int
-get_spec (int *last)
-{
-       int token = get_token ();
-       if (token == TOKEN_EXCLUDE){
-               token = get_spec (last);
-               if (token == TOKEN_EXCLUDE){
-                       fprintf (stderr, "Expecting an expression");
-                       return TOKEN_ERROR;
-               }
-               if (token == TOKEN_ERROR)
-                       return token;
-               trace_spec.ops [(*last)-1].exclude = 1;
-               return TOKEN_SEPARATOR;
-       }
-       if (token == TOKEN_END || token == TOKEN_SEPARATOR || token == TOKEN_ERROR)
-               return token;
-       
-       if (token == TOKEN_METHOD){
-               MonoMethodDesc *desc = mono_method_desc_new (value, TRUE);
-               if (desc == NULL){
-                       fprintf (stderr, "Invalid method name: %s\n", value);
-                       return TOKEN_ERROR;
-               }
-               trace_spec.ops [*last].op = MONO_TRACEOP_METHOD;
-               trace_spec.ops [*last].data = desc;
-       } else if (token == TOKEN_ALL)
-               trace_spec.ops [*last].op = MONO_TRACEOP_ALL;
-       else if (token == TOKEN_PROGRAM)
-               trace_spec.ops [*last].op = MONO_TRACEOP_PROGRAM;
-       else if (token == TOKEN_WRAPPER)
-               trace_spec.ops [*last].op = MONO_TRACEOP_WRAPPER;
-       else if (token == TOKEN_NAMESPACE){
-               trace_spec.ops [*last].op = MONO_TRACEOP_NAMESPACE;
-               trace_spec.ops [*last].data = g_strdup (value);
-       } else if (token == TOKEN_CLASS || token == TOKEN_EXCEPTION){
-               char *p = strrchr (value, '.');
-               if (p) {
-                       *p++ = 0;
-                       trace_spec.ops [*last].data = g_strdup (value);
-                       trace_spec.ops [*last].data2 = g_strdup (p);
-               }
-               else {
-                       trace_spec.ops [*last].data = g_strdup ("");
-                       trace_spec.ops [*last].data2 = g_strdup (value);
-               }
-               trace_spec.ops [*last].op = token == TOKEN_CLASS ? MONO_TRACEOP_CLASS : MONO_TRACEOP_EXCEPTION;
-       } else if (token == TOKEN_STRING){
-               trace_spec.ops [*last].op = MONO_TRACEOP_ASSEMBLY;
-               trace_spec.ops [*last].data = g_strdup (value);
-       } else if (token == TOKEN_DISABLED) {
-               trace_spec.enabled = FALSE;
-       } else {
-               fprintf (stderr, "Syntax error in trace option specification\n");
-               return TOKEN_ERROR;
+       char *errstr;
+       if (!mono_callspec_parse (options, &trace_spec, &errstr)) {
+               fprintf (stderr, "%s\n", errstr);
+               g_free (errstr);
+               return NULL;
        }
        }
-       (*last)++;
-       return TOKEN_SEPARATOR;
-}
 
 
-MonoTraceSpec *
-mono_trace_parse_options (const char *options)
-{
-       char *p = (char*)options;
-       int size = 1;
-       int last_used;
-       int token;
-
-       trace_spec.enabled = TRUE;
-       if (*p == 0){
-               trace_spec.len = 1;
-               trace_spec.ops = g_new0 (MonoTraceOperation, 1);
-               trace_spec.ops [0].op = MONO_TRACEOP_ALL;
-               return &trace_spec;
-       }
-               
-       for (p = (char*)options; *p != 0; p++)
-               if (*p == ',')
-                       size++;
-       
-       trace_spec.ops = g_new0 (MonoTraceOperation, size);
-
-       input = (char*)options;
-       last_used = 0;
-       
-       while ((token = (get_spec (&last_used))) != TOKEN_END){
-               if (token == TOKEN_ERROR)
-                       return NULL;
-               if (token == TOKEN_SEPARATOR)
-                       continue;
-       }
-       trace_spec.len = last_used;
-       cleanup ();
        return &trace_spec;
 }
 
        return &trace_spec;
 }
 
-void
-mono_trace_set_assembly (MonoAssembly *assembly)
-{
-       trace_spec.assembly = assembly;
-}
-
 static
 #ifdef HAVE_KW_THREAD
 __thread 
 static
 #ifdef HAVE_KW_THREAD
 __thread 
index 7f6d4562f7d4a1cbcddd01128fa5981618ff1fae..516fd8c60f712170ec1808f32239231107dd1f84 100644 (file)
@@ -7,31 +7,6 @@
 #include <glib.h>
 #include "mono/utils/mono-compiler.h"
 
 #include <glib.h>
 #include "mono/utils/mono-compiler.h"
 
-typedef enum {
-       MONO_TRACEOP_ALL,
-       MONO_TRACEOP_PROGRAM,
-       MONO_TRACEOP_METHOD,
-       MONO_TRACEOP_ASSEMBLY,
-       MONO_TRACEOP_CLASS,
-       MONO_TRACEOP_NAMESPACE,
-       MONO_TRACEOP_EXCEPTION,
-       MONO_TRACEOP_WRAPPER,
-} MonoTraceOpcode;
-
-typedef struct {
-       MonoTraceOpcode op;
-       int   exclude;
-       void *data, *data2;
-} MonoTraceOperation;
-
-struct MonoTraceSpec {
-       int len;
-       gboolean enabled;
-       MonoTraceOperation *ops;
-
-       MonoAssembly *assembly;
-};
-
 G_BEGIN_DECLS
 
 void
 G_BEGIN_DECLS
 
 void
index 3cfadd1e5138ba73a8edc41cd0d618dceb975a53..b20444f31db4fc324d731d8c6a3d15301627bca8 100644 (file)
@@ -104,6 +104,24 @@ parse_arg (const char *arg, ProfilerConfig *config)
        } else if (match_option (arg, "calldepth", &val)) {
                char *end;
                config->max_call_depth = strtoul (val, &end, 10);
        } else if (match_option (arg, "calldepth", &val)) {
                char *end;
                config->max_call_depth = strtoul (val, &end, 10);
+       } else if (match_option (arg, "callspec", &val)) {
+               if (!val)
+                       val = "";
+               if (val[0] == '\"')
+                       ++val;
+               char *spec = g_strdup (val);
+               size_t speclen = strlen (val);
+               if (speclen > 0 && spec[speclen - 1] == '\"')
+                       spec[speclen - 1] = '\0';
+               char *errstr;
+               if (!mono_callspec_parse (spec, &config->callspec, &errstr)) {
+                       mono_profiler_printf_err (
+                           "Could not parse callspec: '%s': %s", spec,
+                           errstr);
+                       g_free (errstr);
+                       mono_callspec_cleanup (&config->callspec);
+               }
+               g_free (spec);
        } else if (match_option (arg, "covfilter-file", &val)) {
                if (config->cov_filter_files == NULL)
                        config->cov_filter_files = g_ptr_array_new ();
        } else if (match_option (arg, "covfilter-file", &val)) {
                if (config->cov_filter_files == NULL)
                        config->cov_filter_files = g_ptr_array_new ();
index f3dbc56cbad61ad5382d149c07877bdd0359877b..8a677c2720d48bba530592351a5f6283d87f970b 100644 (file)
@@ -1816,6 +1816,10 @@ method_exc_leave (MonoProfiler *prof, MonoMethod *method, MonoObject *exc)
 static MonoProfilerCallInstrumentationFlags
 method_filter (MonoProfiler *prof, MonoMethod *method)
 {
 static MonoProfilerCallInstrumentationFlags
 method_filter (MonoProfiler *prof, MonoMethod *method)
 {
+       if (log_config.callspec.len > 0 &&
+           !mono_callspec_eval (method, &log_config.callspec))
+               return MONO_PROFILER_CALL_INSTRUMENTATION_NONE;
+
        return MONO_PROFILER_CALL_INSTRUMENTATION_ENTER |
               MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE |
               MONO_PROFILER_CALL_INSTRUMENTATION_TAIL_CALL |
        return MONO_PROFILER_CALL_INSTRUMENTATION_ENTER |
               MONO_PROFILER_CALL_INSTRUMENTATION_LEAVE |
               MONO_PROFILER_CALL_INSTRUMENTATION_TAIL_CALL |
index f0503e4a4e423f7c9bc43c131d9ba5a3d3537642..e381d8975471a34d1895b98bb0f05b07a54db502 100644 (file)
@@ -4,6 +4,7 @@
 #include <glib.h>
 #define MONO_PROFILER_UNSTABLE_GC_ROOTS
 #include <mono/metadata/profiler.h>
 #include <glib.h>
 #define MONO_PROFILER_UNSTABLE_GC_ROOTS
 #include <mono/metadata/profiler.h>
+#include <mono/metadata/callspec.h>
 
 #define BUF_ID 0x4D504C01
 #define LOG_HEADER_ID 0x4D505A01
 
 #define BUF_ID 0x4D504C01
 #define LOG_HEADER_ID 0x4D505A01
@@ -537,6 +538,9 @@ typedef struct {
 
        // Sample mode. Only used at startup.
        MonoProfilerSampleMode sampling_mode;
 
        // Sample mode. Only used at startup.
        MonoProfilerSampleMode sampling_mode;
+
+       // Callspec config - which methods are to be instrumented
+       MonoCallSpec callspec;
 } ProfilerConfig;
 
 void proflog_parse_args (ProfilerConfig *config, const char *desc);
 } ProfilerConfig;
 
 void proflog_parse_args (ProfilerConfig *config, const char *desc);
index d2e5ad512cd78a353af0e1e05eb4308b9bd287b6..84c3083b3a1a631d0557de98411a83a1334366d9 100644 (file)
@@ -23,6 +23,7 @@
     <ClCompile Include="..\mono\metadata\assembly.c" />\r
     <ClCompile Include="..\mono\metadata\attach.c" />\r
     <ClCompile Include="..\mono\metadata\boehm-gc.c" />\r
     <ClCompile Include="..\mono\metadata\assembly.c" />\r
     <ClCompile Include="..\mono\metadata\attach.c" />\r
     <ClCompile Include="..\mono\metadata\boehm-gc.c" />\r
+    <ClCompile Include="..\mono\metadata\callspec.c" />\r
     <ClCompile Include="..\mono\metadata\class-accessors.c" />\r
     <ClCompile Include="..\mono\metadata\class.c" />\r
     <ClCompile Include="..\mono\metadata\cominterop.c" />\r
     <ClCompile Include="..\mono\metadata\class-accessors.c" />\r
     <ClCompile Include="..\mono\metadata\class.c" />\r
     <ClCompile Include="..\mono\metadata\cominterop.c" />\r
     <ClInclude Include="..\mono\metadata\appdomain-icalls.h" />\r
     <ClInclude Include="..\mono\metadata\assembly.h" />\r
     <ClInclude Include="..\mono\metadata\attach.h" />\r
     <ClInclude Include="..\mono\metadata\appdomain-icalls.h" />\r
     <ClInclude Include="..\mono\metadata\assembly.h" />\r
     <ClInclude Include="..\mono\metadata\attach.h" />\r
+    <ClInclude Include="..\mono\metadata\callspec.h" />\r
     <ClInclude Include="..\mono\metadata\cil-coff.h" />\r
     <ClInclude Include="..\mono\metadata\class-internals.h" />\r
     <ClInclude Include="..\mono\metadata\class.h" />\r
     <ClInclude Include="..\mono\metadata\cil-coff.h" />\r
     <ClInclude Include="..\mono\metadata\class-internals.h" />\r
     <ClInclude Include="..\mono\metadata\class.h" />\r