[profiler] Clean up logging to use the new logging functions.
[mono.git] / mono / profiler / log-args.c
1 #include <config.h>
2 #include <mono/utils/mono-logger-internals.h>
3 #include "log.h"
4
5 #ifdef HAVE_UNISTD_H
6 #include <unistd.h>
7 #endif
8
9 #ifdef HAVE_SCHED_GETAFFINITY
10 #include <sched.h>
11
12 #  ifndef GLIBC_HAS_CPU_COUNT
13 static int
14 CPU_COUNT(cpu_set_t *set)
15 {
16         int i, count = 0;
17
18         for (int i = 0; i < CPU_SETSIZE; i++)
19                 if (CPU_ISSET(i, set))
20                         count++;
21         return count;
22 }
23 #  endif
24 #endif
25
26 typedef struct {
27         const char *event_name;
28         const int mask;
29 } NameAndMask;
30
31 static NameAndMask event_list[] = {
32         { "domain", PROFLOG_DOMAIN_EVENTS },
33         { "assembly", PROFLOG_ASSEMBLY_EVENTS },
34         { "module", PROFLOG_MODULE_EVENTS },
35         { "class", PROFLOG_CLASS_EVENTS },
36         { "jit", PROFLOG_JIT_COMPILATION_EVENTS },
37         { "exception", PROFLOG_EXCEPTION_EVENTS },
38         { "gcalloc", PROFLOG_ALLOCATION_EVENTS },
39         { "gc", PROFLOG_GC_EVENTS },
40         { "thread", PROFLOG_THREAD_EVENTS },
41         { "calls", PROFLOG_CALL_EVENTS },
42         //{ "inscov", PROFLOG_INS_COVERAGE_EVENTS }, //this is a profiler API event, but there's no actual event for us to emit here
43         //{ "sampling", PROFLOG_SAMPLING_EVENTS }, //it makes no sense to enable/disable this event by itself
44         { "monitor", PROFLOG_MONITOR_EVENTS },
45         { "gcmove", PROFLOG_GC_MOVES_EVENTS },
46         { "gcroot", PROFLOG_GC_ROOT_EVENTS },
47         { "context", PROFLOG_CONTEXT_EVENTS },
48         { "finalization", PROFLOG_FINALIZATION_EVENTS },
49         { "counter", PROFLOG_COUNTER_EVENTS },
50         { "gchandle", PROFLOG_GC_HANDLE_EVENTS },
51
52         { "typesystem", PROFLOG_TYPELOADING_ALIAS },
53         { "coverage", PROFLOG_CODECOV_ALIAS },
54         //{ "sample", PROFLOG_PERF_SAMPLING_ALIAS }, //takes args, explicitly handles
55         { "alloc", PROFLOG_GC_ALLOC_ALIAS },
56         //{ "heapshot", PROFLOG_HEAPSHOT_ALIAS }, //takes args, explicitly handled
57         { "legacy", PROFLOG_LEGACY_ALIAS },
58 };
59
60 static void usage (void);
61 static void set_hsmode (ProfilerConfig *config, const char* val);
62 static void set_sample_freq (ProfilerConfig *config, const char *val);
63 static int mono_cpu_count (void);
64
65
66 static gboolean
67 match_option (const char *arg, const char *opt_name, const char **rval)
68 {
69         if (rval) {
70                 const char *end = strchr (arg, '=');
71
72                 *rval = NULL;
73                 if (!end)
74                         return !strcmp (arg, opt_name);
75
76                 if (strncmp (arg, opt_name, strlen (opt_name)) || (end - arg) > strlen (opt_name) + 1)
77                         return FALSE;
78                 *rval = end + 1;
79                 return TRUE;
80         } else {
81                 //FIXME how should we handle passing a value to an arg that doesn't expect it?
82                 return !strcmp (arg, opt_name);
83         }
84 }
85
86 static void
87 parse_arg (const char *arg, ProfilerConfig *config)
88 {
89         const char *val;
90
91         if (match_option (arg, "help", NULL)) {
92                 usage ();
93         } else if (match_option (arg, "report", NULL)) {
94                 config->do_report = TRUE;
95         } else if (match_option (arg, "debug", NULL)) {
96                 config->do_debug = TRUE;
97         } else if (match_option (arg, "sampling-real", NULL)) {
98                 config->sampling_mode = MONO_PROFILER_SAMPLE_MODE_REAL;
99         } else if (match_option (arg, "sampling-process", NULL)) {
100                 config->sampling_mode = MONO_PROFILER_SAMPLE_MODE_PROCESS;
101         } else if (match_option (arg, "heapshot", &val)) {
102                 config->enable_mask |= PROFLOG_HEAPSHOT_ALIAS;
103                 set_hsmode (config, val);
104         } else if (match_option (arg, "sample", &val)) {
105                 set_sample_freq (config, val);
106                 if (config->sample_freq)
107                         config->enable_mask |= PROFLOG_PERF_SAMPLING_ALIAS;
108         } else if (match_option (arg, "zip", NULL)) {
109                 config->use_zip = TRUE;
110         } else if (match_option (arg, "output", &val)) {
111                 config->output_filename = g_strdup (val);
112         } else if (match_option (arg, "port", &val)) {
113                 char *end;
114                 config->command_port = strtoul (val, &end, 10);
115         } else if (match_option (arg, "maxframes", &val)) {
116                 char *end;
117                 int num_frames = strtoul (val, &end, 10);
118                 if (num_frames > MAX_FRAMES)
119                         num_frames = MAX_FRAMES;
120                 config->notraces = num_frames == 0;
121                 config->num_frames = num_frames;
122         } else if (match_option (arg, "maxsamples", &val)) {
123                 char *end;
124                 int max_samples = strtoul (val, &end, 10);
125                 if (max_samples)
126                         config->max_allocated_sample_hits = max_samples;
127         } else if (match_option (arg, "calldepth", &val)) {
128                 char *end;
129                 config->max_call_depth = strtoul (val, &end, 10);
130         } else if (match_option (arg, "covfilter-file", &val)) {
131                 if (config->cov_filter_files == NULL)
132                         config->cov_filter_files = g_ptr_array_new ();
133                 g_ptr_array_add (config->cov_filter_files, g_strdup (val));
134         } else {
135                 int i;
136
137                 for (i = 0; i < G_N_ELEMENTS (event_list); ++i){
138                         if (!strcmp (arg, event_list [i].event_name)) {
139                                 config->enable_mask |= event_list [i].mask;
140                                 break;
141                         } else if (!strncmp (arg, "no", 2) && !strcmp (arg + 2, event_list [i].event_name)) {
142                                 config->disable_mask |= event_list [i].mask;
143                                 break;
144                         }
145                 }
146
147                 if (i == G_N_ELEMENTS (event_list))
148                         mono_profiler_printf_err ("Could not parse argument: %s", arg);
149         }
150 }
151
152 static void
153 load_args_from_env_or_default (ProfilerConfig *config)
154 {
155         //XXX change this to header constants
156
157         config->max_allocated_sample_hits = mono_cpu_count () * 1000;
158         config->sampling_mode = MONO_PROFILER_SAMPLE_MODE_PROCESS;
159         config->sample_freq = 100;
160         config->max_call_depth = 100;
161         config->num_frames = MAX_FRAMES;
162 }
163
164
165 void
166 proflog_parse_args (ProfilerConfig *config, const char *desc)
167 {
168         const char *p;
169         gboolean in_quotes = FALSE;
170         char quote_char = '\0';
171         char *buffer = malloc (strlen (desc));
172         int buffer_pos = 0;
173
174         load_args_from_env_or_default (config);
175
176         for (p = desc; *p; p++){
177                 switch (*p){
178                 case ',':
179                         if (!in_quotes) {
180                                 if (buffer_pos != 0){
181                                         buffer [buffer_pos] = 0;
182                                         parse_arg (buffer, config);
183                                         buffer_pos = 0;
184                                 }
185                         } else {
186                                 buffer [buffer_pos++] = *p;
187                         }
188                         break;
189
190                 case '\\':
191                         if (p [1]) {
192                                 buffer [buffer_pos++] = p[1];
193                                 p++;
194                         }
195                         break;
196                 case '\'':
197                 case '"':
198                         if (in_quotes) {
199                                 if (quote_char == *p)
200                                         in_quotes = FALSE;
201                                 else
202                                         buffer [buffer_pos++] = *p;
203                         } else {
204                                 in_quotes = TRUE;
205                                 quote_char = *p;
206                         }
207                         break;
208                 default:
209                         buffer [buffer_pos++] = *p;
210                         break;
211                 }
212         }
213                 
214         if (buffer_pos != 0) {
215                 buffer [buffer_pos] = 0;
216                 parse_arg (buffer, config);
217         }
218
219         g_free (buffer);
220
221         //Compure config effective mask
222         config->effective_mask = config->enable_mask & ~config->disable_mask;
223 }
224
225 static void
226 set_hsmode (ProfilerConfig *config, const char* val)
227 {
228         char *end;
229         unsigned int count;
230         if (!val)
231                 return;
232         if (strcmp (val, "ondemand") == 0) {
233                 config->hs_mode_ondemand = TRUE;
234                 return;
235         }
236
237         count = strtoul (val, &end, 10);
238         if (val == end) {
239                 usage ();
240                 return;
241         }
242
243         if (strcmp (end, "ms") == 0)
244                 config->hs_mode_ms = count;
245         else if (strcmp (end, "gc") == 0)
246                 config->hs_mode_gc = count;
247         else
248                 usage ();
249 }
250
251 /*
252 Sampling frequency allows for one undocumented, hidden and ignored argument. The sampling kind.
253 Back in the day when this was done using perf, we could specify one of: cycles,instr,cacherefs,cachemiss,branches,branchmiss
254 With us moving ot userland sampling, those options are now meaningless.
255 */
256 static void
257 set_sample_freq (ProfilerConfig *config, const char *val)
258 {
259         if (!val)
260                 return;
261
262         const char *p = val;
263
264         // Is it only the frequency (new option style)?
265         if (isdigit (*p))
266                 goto parse;
267
268         // Skip the sample type for backwards compatibility.
269         while (isalpha (*p))
270                 p++;
271
272         // Skip the forward slash only if we got a sample type.
273         if (p != val && *p == '/') {
274                 p++;
275
276                 char *end;
277
278         parse:
279                 config->sample_freq = strtoul (p, &end, 10);
280
281                 if (p == end) {
282                         usage ();
283                         return; 
284                 }
285
286                 p = end;
287         }
288
289         if (*p)
290                 usage ();
291 }
292
293 static void
294 usage (void)
295 {
296         mono_profiler_printf ("Mono log profiler version %d.%d (format: %d)", LOG_VERSION_MAJOR, LOG_VERSION_MINOR, LOG_DATA_VERSION);
297         mono_profiler_printf ("Usage: mono --profile=log[:OPTION1[,OPTION2...]] program.exe\n");
298         mono_profiler_printf ("Options:");
299         mono_profiler_printf ("\thelp                 show this usage info");
300         mono_profiler_printf ("\t[no]'EVENT'          enable/disable an individual profiling event");
301         mono_profiler_printf ("\t                     valid EVENT values:");
302
303         for (int i = 0; i < G_N_ELEMENTS (event_list); i++)
304                 mono_profiler_printf ("\t                         %s", event_list [i].event_name);
305
306         mono_profiler_printf ("\t[no]typesystem       enable/disable type system related events such as class and assembly loading");
307         mono_profiler_printf ("\t[no]alloc            enable/disable recording allocation info");
308         mono_profiler_printf ("\t[no]calls            enable/disable recording enter/leave method events (very heavy)");
309         mono_profiler_printf ("\t[no]legacy           enable/disable pre mono 5.4 default profiler events");
310         mono_profiler_printf ("\tsample[=FREQ]        enable/disable statistical sampling of threads (FREQ in Hz, 100 by default)");
311         mono_profiler_printf ("\theapshot[=MODE]      record heapshot info (by default at each major collection)");
312         mono_profiler_printf ("\t                     MODE: every XXms milliseconds, every YYgc collections, ondemand");
313         mono_profiler_printf ("\t[no]coverage         enable/disable collection of code coverage data");
314         mono_profiler_printf ("\tcovfilter=ASSEMBLY   add ASSEMBLY to the code coverage filters");
315         mono_profiler_printf ("\t                     prefix a + to include the assembly or a - to exclude it");
316         mono_profiler_printf ("\t                     e.g. covfilter=-mscorlib");
317         mono_profiler_printf ("\tcovfilter-file=FILE  use FILE to generate the list of assemblies to be filtered");
318         mono_profiler_printf ("\tmaxframes=NUM        collect up to NUM stack frames");
319         mono_profiler_printf ("\tcalldepth=NUM        ignore method events for call chain depth bigger than NUM");
320         mono_profiler_printf ("\toutput=FILENAME      write the data to file FILENAME (the file is always overwritten)");
321         mono_profiler_printf ("\toutput=+FILENAME     write the data to file FILENAME.pid (the file is always overwritten)");
322         mono_profiler_printf ("\toutput=|PROGRAM      write the data to the stdin of PROGRAM");
323         mono_profiler_printf ("\t                     %%t is substituted with date and time, %%p with the pid");
324         mono_profiler_printf ("\treport               create a report instead of writing the raw data to a file");
325         mono_profiler_printf ("\tzip                  compress the output data");
326         mono_profiler_printf ("\tport=PORTNUM         use PORTNUM for the listening command server");
327 }
328
329 static int
330 mono_cpu_count (void)
331 {
332 #ifdef PLATFORM_ANDROID
333         /* Android tries really hard to save power by powering off CPUs on SMP phones which
334          * means the normal way to query cpu count returns a wrong value with userspace API.
335          * Instead we use /sys entries to query the actual hardware CPU count.
336          */
337         int count = 0;
338         char buffer[8] = {'\0'};
339         int present = open ("/sys/devices/system/cpu/present", O_RDONLY);
340         /* Format of the /sys entry is a cpulist of indexes which in the case
341          * of present is always of the form "0-(n-1)" when there is more than
342          * 1 core, n being the number of CPU cores in the system. Otherwise
343          * the value is simply 0
344          */
345         if (present != -1 && read (present, (char*)buffer, sizeof (buffer)) > 3)
346                 count = strtol (((char*)buffer) + 2, NULL, 10);
347         if (present != -1)
348                 close (present);
349         if (count > 0)
350                 return count + 1;
351 #endif
352
353 #if defined(HOST_ARM) || defined (HOST_ARM64)
354
355         /* ARM platforms tries really hard to save power by powering off CPUs on SMP phones which
356          * means the normal way to query cpu count returns a wrong value with userspace API. */
357
358 #ifdef _SC_NPROCESSORS_CONF
359         {
360                 int count = sysconf (_SC_NPROCESSORS_CONF);
361                 if (count > 0)
362                         return count;
363         }
364 #endif
365
366 #else
367
368 #ifdef HAVE_SCHED_GETAFFINITY
369         {
370                 cpu_set_t set;
371                 if (sched_getaffinity (getpid (), sizeof (set), &set) == 0)
372                         return CPU_COUNT (&set);
373         }
374 #endif
375 #ifdef _SC_NPROCESSORS_ONLN
376         {
377                 int count = sysconf (_SC_NPROCESSORS_ONLN);
378                 if (count > 0)
379                         return count;
380         }
381 #endif
382
383 #endif /* defined(HOST_ARM) || defined (HOST_ARM64) */
384
385 #ifdef USE_SYSCTL
386         {
387                 int count;
388                 int mib [2];
389                 size_t len = sizeof (int);
390                 mib [0] = CTL_HW;
391                 mib [1] = HW_NCPU;
392                 if (sysctl (mib, 2, &count, &len, NULL, 0) == 0)
393                         return count;
394         }
395 #endif
396 #ifdef HOST_WIN32
397         {
398                 SYSTEM_INFO info;
399                 GetSystemInfo (&info);
400                 return info.dwNumberOfProcessors;
401         }
402 #endif
403
404         static gboolean warned;
405
406         if (!warned) {
407                 g_warning ("Don't know how to determine CPU count on this platform; assuming 1");
408                 warned = TRUE;
409         }
410
411         return 1;
412 }