From: Uri Simchoni Date: Mon, 2 Oct 2017 14:29:49 +0000 (+0300) Subject: [profiler] log profiler: limit method instrumentation to selected methods (#5517) X-Git-Url: http://wien.tomnetworks.com/gitweb/?p=mono.git;a=commitdiff_plain;h=bfc76bafbf72f8e10c4d2cc7b12e779715c527fa [profiler] log profiler: limit method instrumentation to selected methods (#5517) * [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. --- diff --git a/mono/metadata/Makefile.am b/mono/metadata/Makefile.am index 2fbf826ceec..8a9494054d9 100644 --- a/mono/metadata/Makefile.am +++ b/mono/metadata/Makefile.am @@ -283,7 +283,9 @@ common_sources = \ 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 = \ diff --git a/mono/metadata/callspec.c b/mono/metadata/callspec.c new file mode 100644 index 00000000000..bf108d93cb2 --- /dev/null +++ b/mono/metadata/callspec.c @@ -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 index 00000000000..c9f69981991 --- /dev/null +++ b/mono/metadata/callspec.h @@ -0,0 +1,47 @@ +/** + * \file + */ + +#ifndef __MONO_CALLSPEC_H__ +#define __MONO_CALLSPEC_H__ +#include +#include + +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__ */ diff --git a/mono/mini/driver.c b/mono/mini/driver.c index a364415e257..dbbb646297d 100644 --- a/mono/mini/driver.c +++ b/mono/mini/driver.c @@ -52,6 +52,7 @@ #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" @@ -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. */ - 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); } @@ -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. */ - 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); } @@ -2140,8 +2141,7 @@ mono_main (int argc, char* argv[]) 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; @@ -2400,7 +2400,7 @@ mono_jit_aot_compiling (void) 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; diff --git a/mono/mini/mini.c b/mono/mini/mini.c index af04f0bb796..dd2e12aa951 100644 --- a/mono/mini/mini.c +++ b/mono/mini/mini.c @@ -79,7 +79,7 @@ #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; diff --git a/mono/mini/mini.h b/mono/mini/mini.h index f8d052627cd..42a0356ef83 100644 --- a/mono/mini/mini.h +++ b/mono/mini/mini.h @@ -48,6 +48,7 @@ #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/ @@ -537,9 +538,8 @@ typedef struct MonoMethodVar MonoMethodVar; 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; @@ -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 */ -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 diff --git a/mono/mini/trace.c b/mono/mini/trace.c index 3c916a941b3..0d6b19e857e 100644 --- a/mono/mini/trace.c +++ b/mono/mini/trace.c @@ -21,10 +21,10 @@ #include #include "mini.h" #include -#include #include #include #include "trace.h" +#include #if defined (HOST_ANDROID) || (defined (TARGET_IOS) && defined (TARGET_IOS)) # undef printf @@ -33,306 +33,32 @@ # 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; -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; } -void -mono_trace_set_assembly (MonoAssembly *assembly) -{ - trace_spec.assembly = assembly; -} - static #ifdef HAVE_KW_THREAD __thread diff --git a/mono/mini/trace.h b/mono/mini/trace.h index 7f6d4562f7d..516fd8c60f7 100644 --- a/mono/mini/trace.h +++ b/mono/mini/trace.h @@ -7,31 +7,6 @@ #include #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 diff --git a/mono/profiler/log-args.c b/mono/profiler/log-args.c index 3cfadd1e513..b20444f31db 100644 --- a/mono/profiler/log-args.c +++ b/mono/profiler/log-args.c @@ -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, "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 (); diff --git a/mono/profiler/log.c b/mono/profiler/log.c index f3dbc56cbad..8a677c2720d 100644 --- a/mono/profiler/log.c +++ b/mono/profiler/log.c @@ -1816,6 +1816,10 @@ method_exc_leave (MonoProfiler *prof, MonoMethod *method, MonoObject *exc) 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 | diff --git a/mono/profiler/log.h b/mono/profiler/log.h index f0503e4a4e4..e381d897547 100644 --- a/mono/profiler/log.h +++ b/mono/profiler/log.h @@ -4,6 +4,7 @@ #include #define MONO_PROFILER_UNSTABLE_GC_ROOTS #include +#include #define BUF_ID 0x4D504C01 #define LOG_HEADER_ID 0x4D505A01 @@ -537,6 +538,9 @@ typedef struct { // 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); diff --git a/msvc/libmonoruntime.vcxproj b/msvc/libmonoruntime.vcxproj index d2e5ad512cd..84c3083b3a1 100644 --- a/msvc/libmonoruntime.vcxproj +++ b/msvc/libmonoruntime.vcxproj @@ -23,6 +23,7 @@ + @@ -115,6 +116,7 @@ +