[profiler] Drop support for the old-style sample option.
[mono-project.git] / mono / profiler / log-args.c
bloba18572cc08d3af62e507c42ac63ac2730f2adb19
1 #include <config.h>
2 #include <mono/utils/mono-logger-internals.h>
3 #include "log.h"
5 #ifdef HAVE_UNISTD_H
6 #include <unistd.h>
7 #endif
9 #ifdef HAVE_SCHED_GETAFFINITY
10 #include <sched.h>
12 # ifndef GLIBC_HAS_CPU_COUNT
13 static int
14 CPU_COUNT(cpu_set_t *set)
16 int i, count = 0;
18 for (int i = 0; i < CPU_SETSIZE; i++)
19 if (CPU_ISSET(i, set))
20 count++;
21 return count;
23 # endif
24 #endif
26 typedef struct {
27 const char *event_name;
28 const int mask;
29 } NameAndMask;
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 },
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 },
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);
66 static gboolean
67 match_option (const char *arg, const char *opt_name, const char **rval)
69 if (rval) {
70 const char *end = strchr (arg, '=');
72 *rval = NULL;
73 if (!end)
74 return !strcmp (arg, opt_name);
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);
86 static void
87 parse_arg (const char *arg, ProfilerConfig *config)
89 const char *val;
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;
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;
147 if (i == G_N_ELEMENTS (event_list))
148 mono_profiler_printf_err ("Could not parse argument: %s", arg);
152 static void
153 load_args_from_env_or_default (ProfilerConfig *config)
155 //XXX change this to header constants
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;
165 void
166 proflog_parse_args (ProfilerConfig *config, const char *desc)
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;
174 load_args_from_env_or_default (config);
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;
185 } else {
186 buffer [buffer_pos++] = *p;
188 break;
190 case '\\':
191 if (p [1]) {
192 buffer [buffer_pos++] = p[1];
193 p++;
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;
207 break;
208 default:
209 buffer [buffer_pos++] = *p;
210 break;
214 if (buffer_pos != 0) {
215 buffer [buffer_pos] = 0;
216 parse_arg (buffer, config);
219 g_free (buffer);
221 //Compure config effective mask
222 config->effective_mask = config->enable_mask & ~config->disable_mask;
225 static void
226 set_hsmode (ProfilerConfig *config, const char* val)
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;
237 count = strtoul (val, &end, 10);
238 if (val == end) {
239 usage ();
240 return;
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 ();
251 static void
252 set_sample_freq (ProfilerConfig *config, const char *val)
254 if (!val)
255 return;
257 char *end;
259 int freq = strtoul (val, &end, 10);
261 if (val == end) {
262 usage ();
263 return;
266 config->sample_freq = freq;
269 static void
270 usage (void)
272 mono_profiler_printf ("Mono log profiler version %d.%d (format: %d)", LOG_VERSION_MAJOR, LOG_VERSION_MINOR, LOG_DATA_VERSION);
273 mono_profiler_printf ("Usage: mono --profile=log[:OPTION1[,OPTION2...]] program.exe\n");
274 mono_profiler_printf ("Options:");
275 mono_profiler_printf ("\thelp show this usage info");
276 mono_profiler_printf ("\t[no]'EVENT' enable/disable an individual profiling event");
277 mono_profiler_printf ("\t valid EVENT values:");
279 for (int i = 0; i < G_N_ELEMENTS (event_list); i++)
280 mono_profiler_printf ("\t %s", event_list [i].event_name);
282 mono_profiler_printf ("\t[no]typesystem enable/disable type system related events such as class and assembly loading");
283 mono_profiler_printf ("\t[no]alloc enable/disable recording allocation info");
284 mono_profiler_printf ("\t[no]calls enable/disable recording enter/leave method events (very heavy)");
285 mono_profiler_printf ("\t[no]legacy enable/disable pre mono 5.4 default profiler events");
286 mono_profiler_printf ("\tsample[=FREQ] enable/disable statistical sampling of threads (FREQ in Hz, 100 by default)");
287 mono_profiler_printf ("\theapshot[=MODE] record heapshot info (by default at each major collection)");
288 mono_profiler_printf ("\t MODE: every XXms milliseconds, every YYgc collections, ondemand");
289 mono_profiler_printf ("\t[no]coverage enable/disable collection of code coverage data");
290 mono_profiler_printf ("\tcovfilter=ASSEMBLY add ASSEMBLY to the code coverage filters");
291 mono_profiler_printf ("\t prefix a + to include the assembly or a - to exclude it");
292 mono_profiler_printf ("\t e.g. covfilter=-mscorlib");
293 mono_profiler_printf ("\tcovfilter-file=FILE use FILE to generate the list of assemblies to be filtered");
294 mono_profiler_printf ("\tmaxframes=NUM collect up to NUM stack frames");
295 mono_profiler_printf ("\tcalldepth=NUM ignore method events for call chain depth bigger than NUM");
296 mono_profiler_printf ("\toutput=FILENAME write the data to file FILENAME (the file is always overwritten)");
297 mono_profiler_printf ("\toutput=+FILENAME write the data to file FILENAME.pid (the file is always overwritten)");
298 mono_profiler_printf ("\toutput=|PROGRAM write the data to the stdin of PROGRAM");
299 mono_profiler_printf ("\t %%t is substituted with date and time, %%p with the pid");
300 mono_profiler_printf ("\treport create a report instead of writing the raw data to a file");
301 mono_profiler_printf ("\tzip compress the output data");
302 mono_profiler_printf ("\tport=PORTNUM use PORTNUM for the listening command server");
305 static int
306 mono_cpu_count (void)
308 #ifdef PLATFORM_ANDROID
309 /* Android tries really hard to save power by powering off CPUs on SMP phones which
310 * means the normal way to query cpu count returns a wrong value with userspace API.
311 * Instead we use /sys entries to query the actual hardware CPU count.
313 int count = 0;
314 char buffer[8] = {'\0'};
315 int present = open ("/sys/devices/system/cpu/present", O_RDONLY);
316 /* Format of the /sys entry is a cpulist of indexes which in the case
317 * of present is always of the form "0-(n-1)" when there is more than
318 * 1 core, n being the number of CPU cores in the system. Otherwise
319 * the value is simply 0
321 if (present != -1 && read (present, (char*)buffer, sizeof (buffer)) > 3)
322 count = strtol (((char*)buffer) + 2, NULL, 10);
323 if (present != -1)
324 close (present);
325 if (count > 0)
326 return count + 1;
327 #endif
329 #if defined(HOST_ARM) || defined (HOST_ARM64)
331 /* ARM platforms tries really hard to save power by powering off CPUs on SMP phones which
332 * means the normal way to query cpu count returns a wrong value with userspace API. */
334 #ifdef _SC_NPROCESSORS_CONF
336 int count = sysconf (_SC_NPROCESSORS_CONF);
337 if (count > 0)
338 return count;
340 #endif
342 #else
344 #ifdef HAVE_SCHED_GETAFFINITY
346 cpu_set_t set;
347 if (sched_getaffinity (getpid (), sizeof (set), &set) == 0)
348 return CPU_COUNT (&set);
350 #endif
351 #ifdef _SC_NPROCESSORS_ONLN
353 int count = sysconf (_SC_NPROCESSORS_ONLN);
354 if (count > 0)
355 return count;
357 #endif
359 #endif /* defined(HOST_ARM) || defined (HOST_ARM64) */
361 #ifdef USE_SYSCTL
363 int count;
364 int mib [2];
365 size_t len = sizeof (int);
366 mib [0] = CTL_HW;
367 mib [1] = HW_NCPU;
368 if (sysctl (mib, 2, &count, &len, NULL, 0) == 0)
369 return count;
371 #endif
372 #ifdef HOST_WIN32
374 SYSTEM_INFO info;
375 GetSystemInfo (&info);
376 return info.dwNumberOfProcessors;
378 #endif
380 static gboolean warned;
382 if (!warned) {
383 g_warning ("Don't know how to determine CPU count on this platform; assuming 1");
384 warned = TRUE;
387 return 1;