[profiler] log profiler: limit method instrumentation to selected methods (#5517)
[mono.git] / mono / profiler / log-args.c
1 #include <config.h>
2 #include <mono/utils/mono-logger-internals.h>
3 #include <mono/utils/mono-proclib.h>
4 #include "log.h"
5
6 #ifdef HAVE_UNISTD_H
7 #include <unistd.h>
8 #endif
9
10 typedef struct {
11         const char *event_name;
12         const int mask;
13 } NameAndMask;
14
15 static NameAndMask event_list[] = {
16         { "exception", PROFLOG_EXCEPTION_EVENTS },
17         { "monitor", PROFLOG_MONITOR_EVENTS },
18         { "gc", PROFLOG_GC_EVENTS },
19         { "gcalloc", PROFLOG_GC_ALLOCATION_EVENTS },
20         { "gcmove", PROFLOG_GC_MOVE_EVENTS },
21         { "gcroot", PROFLOG_GC_ROOT_EVENTS },
22         { "gchandle", PROFLOG_GC_HANDLE_EVENTS },
23         { "finalization", PROFLOG_GC_FINALIZATION_EVENTS },
24         { "counter", PROFLOG_COUNTER_EVENTS },
25         { "jit", PROFLOG_JIT_EVENTS },
26
27         { "alloc", PROFLOG_ALLOC_ALIAS },
28         { "legacy", PROFLOG_LEGACY_ALIAS },
29 };
30
31 static void usage (void);
32 static void set_hsmode (ProfilerConfig *config, const char* val);
33 static void set_sample_freq (ProfilerConfig *config, const char *val);
34
35 static gboolean
36 match_option (const char *arg, const char *opt_name, const char **rval)
37 {
38         if (rval) {
39                 const char *end = strchr (arg, '=');
40
41                 *rval = NULL;
42                 if (!end)
43                         return !strcmp (arg, opt_name);
44
45                 if (strncmp (arg, opt_name, strlen (opt_name)) || (end - arg) > strlen (opt_name) + 1)
46                         return FALSE;
47                 *rval = end + 1;
48                 return TRUE;
49         } else {
50                 //FIXME how should we handle passing a value to an arg that doesn't expect it?
51                 return !strcmp (arg, opt_name);
52         }
53 }
54
55 static void
56 parse_arg (const char *arg, ProfilerConfig *config)
57 {
58         const char *val;
59
60         if (match_option (arg, "help", NULL)) {
61                 usage ();
62         } else if (match_option (arg, "report", NULL)) {
63                 config->do_report = TRUE;
64         } else if (match_option (arg, "debug", NULL)) {
65                 config->do_debug = TRUE;
66         } else if (match_option (arg, "heapshot", &val)) {
67                 set_hsmode (config, val);
68                 if (config->hs_mode != MONO_PROFILER_HEAPSHOT_NONE)
69                         config->enable_mask |= PROFLOG_HEAPSHOT_ALIAS;
70         } else if (match_option (arg, "heapshot-on-shutdown", NULL)) {
71                 config->hs_on_shutdown = TRUE;
72                 config->enable_mask |= PROFLOG_HEAPSHOT_ALIAS;
73         } else if (match_option (arg, "sample", &val)) {
74                 set_sample_freq (config, val);
75                 config->sampling_mode = MONO_PROFILER_SAMPLE_MODE_PROCESS;
76                 config->enable_mask |= PROFLOG_SAMPLE_EVENTS;
77         } else if (match_option (arg, "sample-real", &val)) {
78                 set_sample_freq (config, val);
79                 config->sampling_mode = MONO_PROFILER_SAMPLE_MODE_REAL;
80                 config->enable_mask |= PROFLOG_SAMPLE_EVENTS;
81         } else if (match_option (arg, "calls", NULL)) {
82                 config->enter_leave = TRUE;
83         } else if (match_option (arg, "coverage", NULL)) {
84                 g_warning ("the log profiler support for code coverage is obsolete, use the \"coverage\" profiler");
85                 config->collect_coverage = TRUE;
86         } else if (match_option (arg, "zip", NULL)) {
87                 config->use_zip = TRUE;
88         } else if (match_option (arg, "output", &val)) {
89                 config->output_filename = g_strdup (val);
90         } else if (match_option (arg, "port", &val)) {
91                 char *end;
92                 config->command_port = strtoul (val, &end, 10);
93         } else if (match_option (arg, "maxframes", &val)) {
94                 char *end;
95                 int num_frames = strtoul (val, &end, 10);
96                 if (num_frames > MAX_FRAMES)
97                         num_frames = MAX_FRAMES;
98                 config->num_frames = num_frames;
99         } else if (match_option (arg, "maxsamples", &val)) {
100                 char *end;
101                 int max_samples = strtoul (val, &end, 10);
102                 if (max_samples)
103                         config->max_allocated_sample_hits = max_samples;
104         } else if (match_option (arg, "calldepth", &val)) {
105                 char *end;
106                 config->max_call_depth = strtoul (val, &end, 10);
107         } else if (match_option (arg, "callspec", &val)) {
108                 if (!val)
109                         val = "";
110                 if (val[0] == '\"')
111                         ++val;
112                 char *spec = g_strdup (val);
113                 size_t speclen = strlen (val);
114                 if (speclen > 0 && spec[speclen - 1] == '\"')
115                         spec[speclen - 1] = '\0';
116                 char *errstr;
117                 if (!mono_callspec_parse (spec, &config->callspec, &errstr)) {
118                         mono_profiler_printf_err (
119                             "Could not parse callspec: '%s': %s", spec,
120                             errstr);
121                         g_free (errstr);
122                         mono_callspec_cleanup (&config->callspec);
123                 }
124                 g_free (spec);
125         } else if (match_option (arg, "covfilter-file", &val)) {
126                 if (config->cov_filter_files == NULL)
127                         config->cov_filter_files = g_ptr_array_new ();
128                 g_ptr_array_add (config->cov_filter_files, g_strdup (val));
129         } else {
130                 int i;
131
132                 for (i = 0; i < G_N_ELEMENTS (event_list); ++i){
133                         if (!strcmp (arg, event_list [i].event_name)) {
134                                 config->enable_mask |= event_list [i].mask;
135                                 break;
136                         } else if (!strncmp (arg, "no", 2) && !strcmp (arg + 2, event_list [i].event_name)) {
137                                 config->disable_mask |= event_list [i].mask;
138                                 break;
139                         }
140                 }
141
142                 if (i == G_N_ELEMENTS (event_list))
143                         mono_profiler_printf_err ("Could not parse argument: %s", arg);
144         }
145 }
146
147 static void
148 load_args_from_env_or_default (ProfilerConfig *config)
149 {
150         //XXX change this to header constants
151
152         config->max_allocated_sample_hits = mono_cpu_count () * 1000;
153         config->sampling_mode = MONO_PROFILER_SAMPLE_MODE_NONE;
154         config->sample_freq = 100;
155         config->max_call_depth = 100;
156         config->num_frames = MAX_FRAMES;
157 }
158
159
160 void
161 proflog_parse_args (ProfilerConfig *config, const char *desc)
162 {
163         const char *p;
164         gboolean in_quotes = FALSE;
165         char quote_char = '\0';
166         char *buffer = malloc (strlen (desc));
167         int buffer_pos = 0;
168
169         load_args_from_env_or_default (config);
170
171         for (p = desc; *p; p++){
172                 switch (*p){
173                 case ',':
174                         if (!in_quotes) {
175                                 if (buffer_pos != 0){
176                                         buffer [buffer_pos] = 0;
177                                         parse_arg (buffer, config);
178                                         buffer_pos = 0;
179                                 }
180                         } else {
181                                 buffer [buffer_pos++] = *p;
182                         }
183                         break;
184
185                 case '\\':
186                         if (p [1]) {
187                                 buffer [buffer_pos++] = p[1];
188                                 p++;
189                         }
190                         break;
191                 case '\'':
192                 case '"':
193                         if (in_quotes) {
194                                 if (quote_char == *p)
195                                         in_quotes = FALSE;
196                                 else
197                                         buffer [buffer_pos++] = *p;
198                         } else {
199                                 in_quotes = TRUE;
200                                 quote_char = *p;
201                         }
202                         break;
203                 default:
204                         buffer [buffer_pos++] = *p;
205                         break;
206                 }
207         }
208                 
209         if (buffer_pos != 0) {
210                 buffer [buffer_pos] = 0;
211                 parse_arg (buffer, config);
212         }
213
214         g_free (buffer);
215
216         //Compure config effective mask
217         config->effective_mask = config->enable_mask & ~config->disable_mask;
218 }
219
220 static void
221 set_hsmode (ProfilerConfig *config, const char* val)
222 {
223         if (!val) {
224                 config->hs_mode = MONO_PROFILER_HEAPSHOT_MAJOR;
225                 return;
226         }
227
228         if (strcmp (val, "ondemand") == 0) {
229                 config->hs_mode = MONO_PROFILER_HEAPSHOT_ON_DEMAND;
230                 return;
231         }
232
233         char *end;
234
235         unsigned int count = strtoul (val, &end, 10);
236
237         if (val == end) {
238                 usage ();
239                 return;
240         }
241
242         if (strcmp (end, "ms") == 0) {
243                 config->hs_mode = MONO_PROFILER_HEAPSHOT_X_MS;
244                 config->hs_freq_ms = count;
245         } else if (strcmp (end, "gc") == 0) {
246                 config->hs_mode = MONO_PROFILER_HEAPSHOT_X_GC;
247                 config->hs_freq_gc = count;
248         } else
249                 usage ();
250 }
251
252 static void
253 set_sample_freq (ProfilerConfig *config, const char *val)
254 {
255         if (!val)
256                 return;
257
258         char *end;
259
260         int freq = strtoul (val, &end, 10);
261
262         if (val == end) {
263                 usage ();
264                 return;
265         }
266
267         config->sample_freq = freq;
268 }
269
270 static void
271 usage (void)
272 {
273         mono_profiler_printf ("Mono log profiler version %d.%d (format: %d)", LOG_VERSION_MAJOR, LOG_VERSION_MINOR, LOG_DATA_VERSION);
274         mono_profiler_printf ("Usage: mono --profile=log[:OPTION1[,OPTION2...]] program.exe\n");
275         mono_profiler_printf ("Options:");
276         mono_profiler_printf ("\thelp                 show this usage info");
277         mono_profiler_printf ("\t[no]'EVENT'          enable/disable an individual profiling event");
278         mono_profiler_printf ("\t                     valid EVENT values:");
279
280         for (int i = 0; i < G_N_ELEMENTS (event_list); i++)
281                 mono_profiler_printf ("\t                         %s", event_list [i].event_name);
282
283         mono_profiler_printf ("\t[no]alloc            enable/disable recording allocation info");
284         mono_profiler_printf ("\t[no]legacy           enable/disable pre Mono 5.6 default profiler events");
285         mono_profiler_printf ("\tsample[-real][=FREQ] enable/disable statistical sampling of threads");
286         mono_profiler_printf ("\t                     FREQ in Hz, 100 by default");
287         mono_profiler_printf ("\t                     the -real variant uses wall clock time instead of process time");
288         mono_profiler_printf ("\theapshot[=MODE]      record heapshot info (by default at each major collection)");
289         mono_profiler_printf ("\t                     MODE: every XXms milliseconds, every YYgc collections, ondemand");
290         mono_profiler_printf ("\theapshot-on-shutdown do a heapshot on runtime shutdown");
291         mono_profiler_printf ("\t                     this option is independent of the above option");
292         mono_profiler_printf ("\tcalls                enable recording enter/leave method events (very heavy)");
293         mono_profiler_printf ("\tcoverage             enable collection of code coverage data");
294         mono_profiler_printf ("\tcovfilter=ASSEMBLY   add ASSEMBLY to the code coverage filters");
295         mono_profiler_printf ("\t                     prefix a + to include the assembly or a - to exclude it");
296         mono_profiler_printf ("\t                     e.g. covfilter=-mscorlib");
297         mono_profiler_printf ("\tcovfilter-file=FILE  use FILE to generate the list of assemblies to be filtered");
298         mono_profiler_printf ("\tmaxframes=NUM        collect up to NUM stack frames");
299         mono_profiler_printf ("\tcalldepth=NUM        ignore method events for call chain depth bigger than NUM");
300         mono_profiler_printf ("\toutput=FILENAME      write the data to file FILENAME (the file is always overwritten)");
301         mono_profiler_printf ("\toutput=+FILENAME     write the data to file FILENAME.pid (the file is always overwritten)");
302         mono_profiler_printf ("\toutput=|PROGRAM      write the data to the stdin of PROGRAM");
303         mono_profiler_printf ("\t                     %%t is substituted with date and time, %%p with the pid");
304         mono_profiler_printf ("\treport               create a report instead of writing the raw data to a file");
305         mono_profiler_printf ("\tzip                  compress the output data");
306         mono_profiler_printf ("\tport=PORTNUM         use PORTNUM for the listening command server");
307 }