[profiler] Use mono_cpu_count () from utils/proclib.
[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         { "domain", PROFLOG_DOMAIN_EVENTS },
17         { "assembly", PROFLOG_ASSEMBLY_EVENTS },
18         { "module", PROFLOG_MODULE_EVENTS },
19         { "class", PROFLOG_CLASS_EVENTS },
20         { "jit", PROFLOG_JIT_COMPILATION_EVENTS },
21         { "exception", PROFLOG_EXCEPTION_EVENTS },
22         { "gcalloc", PROFLOG_ALLOCATION_EVENTS },
23         { "gc", PROFLOG_GC_EVENTS },
24         { "thread", PROFLOG_THREAD_EVENTS },
25         { "calls", PROFLOG_CALL_EVENTS },
26         //{ "inscov", PROFLOG_INS_COVERAGE_EVENTS }, //this is a profiler API event, but there's no actual event for us to emit here
27         //{ "sampling", PROFLOG_SAMPLING_EVENTS }, //it makes no sense to enable/disable this event by itself
28         { "monitor", PROFLOG_MONITOR_EVENTS },
29         { "gcmove", PROFLOG_GC_MOVES_EVENTS },
30         { "gcroot", PROFLOG_GC_ROOT_EVENTS },
31         { "context", PROFLOG_CONTEXT_EVENTS },
32         { "finalization", PROFLOG_FINALIZATION_EVENTS },
33         { "counter", PROFLOG_COUNTER_EVENTS },
34         { "gchandle", PROFLOG_GC_HANDLE_EVENTS },
35
36         { "typesystem", PROFLOG_TYPELOADING_ALIAS },
37         { "coverage", PROFLOG_CODECOV_ALIAS },
38         //{ "sample", PROFLOG_PERF_SAMPLING_ALIAS }, //takes args, explicitly handles
39         { "alloc", PROFLOG_GC_ALLOC_ALIAS },
40         //{ "heapshot", PROFLOG_HEAPSHOT_ALIAS }, //takes args, explicitly handled
41         { "legacy", PROFLOG_LEGACY_ALIAS },
42 };
43
44 static void usage (void);
45 static void set_hsmode (ProfilerConfig *config, const char* val);
46 static void set_sample_freq (ProfilerConfig *config, const char *val);
47
48 static gboolean
49 match_option (const char *arg, const char *opt_name, const char **rval)
50 {
51         if (rval) {
52                 const char *end = strchr (arg, '=');
53
54                 *rval = NULL;
55                 if (!end)
56                         return !strcmp (arg, opt_name);
57
58                 if (strncmp (arg, opt_name, strlen (opt_name)) || (end - arg) > strlen (opt_name) + 1)
59                         return FALSE;
60                 *rval = end + 1;
61                 return TRUE;
62         } else {
63                 //FIXME how should we handle passing a value to an arg that doesn't expect it?
64                 return !strcmp (arg, opt_name);
65         }
66 }
67
68 static void
69 parse_arg (const char *arg, ProfilerConfig *config)
70 {
71         const char *val;
72
73         if (match_option (arg, "help", NULL)) {
74                 usage ();
75         } else if (match_option (arg, "report", NULL)) {
76                 config->do_report = TRUE;
77         } else if (match_option (arg, "debug", NULL)) {
78                 config->do_debug = TRUE;
79         } else if (match_option (arg, "sampling-real", NULL)) {
80                 config->sampling_mode = MONO_PROFILER_SAMPLE_MODE_REAL;
81         } else if (match_option (arg, "sampling-process", NULL)) {
82                 config->sampling_mode = MONO_PROFILER_SAMPLE_MODE_PROCESS;
83         } else if (match_option (arg, "heapshot", &val)) {
84                 config->enable_mask |= PROFLOG_HEAPSHOT_ALIAS;
85                 set_hsmode (config, val);
86         } else if (match_option (arg, "sample", &val)) {
87                 set_sample_freq (config, val);
88                 if (config->sample_freq)
89                         config->enable_mask |= PROFLOG_PERF_SAMPLING_ALIAS;
90         } else if (match_option (arg, "zip", NULL)) {
91                 config->use_zip = TRUE;
92         } else if (match_option (arg, "output", &val)) {
93                 config->output_filename = g_strdup (val);
94         } else if (match_option (arg, "port", &val)) {
95                 char *end;
96                 config->command_port = strtoul (val, &end, 10);
97         } else if (match_option (arg, "maxframes", &val)) {
98                 char *end;
99                 int num_frames = strtoul (val, &end, 10);
100                 if (num_frames > MAX_FRAMES)
101                         num_frames = MAX_FRAMES;
102                 config->notraces = num_frames == 0;
103                 config->num_frames = num_frames;
104         } else if (match_option (arg, "maxsamples", &val)) {
105                 char *end;
106                 int max_samples = strtoul (val, &end, 10);
107                 if (max_samples)
108                         config->max_allocated_sample_hits = max_samples;
109         } else if (match_option (arg, "calldepth", &val)) {
110                 char *end;
111                 config->max_call_depth = strtoul (val, &end, 10);
112         } else if (match_option (arg, "covfilter-file", &val)) {
113                 if (config->cov_filter_files == NULL)
114                         config->cov_filter_files = g_ptr_array_new ();
115                 g_ptr_array_add (config->cov_filter_files, g_strdup (val));
116         } else {
117                 int i;
118
119                 for (i = 0; i < G_N_ELEMENTS (event_list); ++i){
120                         if (!strcmp (arg, event_list [i].event_name)) {
121                                 config->enable_mask |= event_list [i].mask;
122                                 break;
123                         } else if (!strncmp (arg, "no", 2) && !strcmp (arg + 2, event_list [i].event_name)) {
124                                 config->disable_mask |= event_list [i].mask;
125                                 break;
126                         }
127                 }
128
129                 if (i == G_N_ELEMENTS (event_list))
130                         mono_profiler_printf_err ("Could not parse argument: %s", arg);
131         }
132 }
133
134 static void
135 load_args_from_env_or_default (ProfilerConfig *config)
136 {
137         //XXX change this to header constants
138
139         config->max_allocated_sample_hits = mono_cpu_count () * 1000;
140         config->sampling_mode = MONO_PROFILER_SAMPLE_MODE_PROCESS;
141         config->sample_freq = 100;
142         config->max_call_depth = 100;
143         config->num_frames = MAX_FRAMES;
144 }
145
146
147 void
148 proflog_parse_args (ProfilerConfig *config, const char *desc)
149 {
150         const char *p;
151         gboolean in_quotes = FALSE;
152         char quote_char = '\0';
153         char *buffer = malloc (strlen (desc));
154         int buffer_pos = 0;
155
156         load_args_from_env_or_default (config);
157
158         for (p = desc; *p; p++){
159                 switch (*p){
160                 case ',':
161                         if (!in_quotes) {
162                                 if (buffer_pos != 0){
163                                         buffer [buffer_pos] = 0;
164                                         parse_arg (buffer, config);
165                                         buffer_pos = 0;
166                                 }
167                         } else {
168                                 buffer [buffer_pos++] = *p;
169                         }
170                         break;
171
172                 case '\\':
173                         if (p [1]) {
174                                 buffer [buffer_pos++] = p[1];
175                                 p++;
176                         }
177                         break;
178                 case '\'':
179                 case '"':
180                         if (in_quotes) {
181                                 if (quote_char == *p)
182                                         in_quotes = FALSE;
183                                 else
184                                         buffer [buffer_pos++] = *p;
185                         } else {
186                                 in_quotes = TRUE;
187                                 quote_char = *p;
188                         }
189                         break;
190                 default:
191                         buffer [buffer_pos++] = *p;
192                         break;
193                 }
194         }
195                 
196         if (buffer_pos != 0) {
197                 buffer [buffer_pos] = 0;
198                 parse_arg (buffer, config);
199         }
200
201         g_free (buffer);
202
203         //Compure config effective mask
204         config->effective_mask = config->enable_mask & ~config->disable_mask;
205 }
206
207 static void
208 set_hsmode (ProfilerConfig *config, const char* val)
209 {
210         char *end;
211         unsigned int count;
212         if (!val)
213                 return;
214         if (strcmp (val, "ondemand") == 0) {
215                 config->hs_mode_ondemand = TRUE;
216                 return;
217         }
218
219         count = strtoul (val, &end, 10);
220         if (val == end) {
221                 usage ();
222                 return;
223         }
224
225         if (strcmp (end, "ms") == 0)
226                 config->hs_mode_ms = count;
227         else if (strcmp (end, "gc") == 0)
228                 config->hs_mode_gc = count;
229         else
230                 usage ();
231 }
232
233 static void
234 set_sample_freq (ProfilerConfig *config, const char *val)
235 {
236         if (!val)
237                 return;
238
239         char *end;
240
241         int freq = strtoul (val, &end, 10);
242
243         if (val == end) {
244                 usage ();
245                 return;
246         }
247
248         config->sample_freq = freq;
249 }
250
251 static void
252 usage (void)
253 {
254         mono_profiler_printf ("Mono log profiler version %d.%d (format: %d)", LOG_VERSION_MAJOR, LOG_VERSION_MINOR, LOG_DATA_VERSION);
255         mono_profiler_printf ("Usage: mono --profile=log[:OPTION1[,OPTION2...]] program.exe\n");
256         mono_profiler_printf ("Options:");
257         mono_profiler_printf ("\thelp                 show this usage info");
258         mono_profiler_printf ("\t[no]'EVENT'          enable/disable an individual profiling event");
259         mono_profiler_printf ("\t                     valid EVENT values:");
260
261         for (int i = 0; i < G_N_ELEMENTS (event_list); i++)
262                 mono_profiler_printf ("\t                         %s", event_list [i].event_name);
263
264         mono_profiler_printf ("\t[no]typesystem       enable/disable type system related events such as class and assembly loading");
265         mono_profiler_printf ("\t[no]alloc            enable/disable recording allocation info");
266         mono_profiler_printf ("\t[no]calls            enable/disable recording enter/leave method events (very heavy)");
267         mono_profiler_printf ("\t[no]legacy           enable/disable pre mono 5.4 default profiler events");
268         mono_profiler_printf ("\tsample[=FREQ]        enable/disable statistical sampling of threads (FREQ in Hz, 100 by default)");
269         mono_profiler_printf ("\theapshot[=MODE]      record heapshot info (by default at each major collection)");
270         mono_profiler_printf ("\t                     MODE: every XXms milliseconds, every YYgc collections, ondemand");
271         mono_profiler_printf ("\t[no]coverage         enable/disable collection of code coverage data");
272         mono_profiler_printf ("\tcovfilter=ASSEMBLY   add ASSEMBLY to the code coverage filters");
273         mono_profiler_printf ("\t                     prefix a + to include the assembly or a - to exclude it");
274         mono_profiler_printf ("\t                     e.g. covfilter=-mscorlib");
275         mono_profiler_printf ("\tcovfilter-file=FILE  use FILE to generate the list of assemblies to be filtered");
276         mono_profiler_printf ("\tmaxframes=NUM        collect up to NUM stack frames");
277         mono_profiler_printf ("\tcalldepth=NUM        ignore method events for call chain depth bigger than NUM");
278         mono_profiler_printf ("\toutput=FILENAME      write the data to file FILENAME (the file is always overwritten)");
279         mono_profiler_printf ("\toutput=+FILENAME     write the data to file FILENAME.pid (the file is always overwritten)");
280         mono_profiler_printf ("\toutput=|PROGRAM      write the data to the stdin of PROGRAM");
281         mono_profiler_printf ("\t                     %%t is substituted with date and time, %%p with the pid");
282         mono_profiler_printf ("\treport               create a report instead of writing the raw data to a file");
283         mono_profiler_printf ("\tzip                  compress the output data");
284         mono_profiler_printf ("\tport=PORTNUM         use PORTNUM for the listening command server");
285 }