9 #ifdef HAVE_SCHED_GETAFFINITY
12 # ifndef GLIBC_HAS_CPU_COUNT
14 CPU_COUNT(cpu_set_t *set)
18 for (int i = 0; i < CPU_SETSIZE; i++)
19 if (CPU_ISSET(i, set))
27 const char *event_name;
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);
67 match_option (const char *arg, const char *opt_name, const char **rval)
70 const char *end = strchr (arg, '=');
74 return !strcmp (arg, opt_name);
76 if (strncmp (arg, opt_name, strlen (opt_name)) || (end - arg) > strlen (opt_name) + 1)
81 //FIXME how should we handle passing a value to an arg that doesn't expect it?
82 return !strcmp (arg, opt_name);
87 parse_arg (const char *arg, ProfilerConfig *config)
91 if (match_option (arg, "help", NULL)) {
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, "debug-coverage", NULL)) {
98 config->debug_coverage = TRUE;
99 } else if (match_option (arg, "sampling-real", NULL)) {
100 config->sampling_mode = MONO_PROFILER_STAT_MODE_REAL;
101 } else if (match_option (arg, "sampling-process", NULL)) {
102 config->sampling_mode = MONO_PROFILER_STAT_MODE_PROCESS;
103 } else if (match_option (arg, "heapshot", &val)) {
104 config->enable_mask |= PROFLOG_HEAPSHOT_ALIAS;
105 set_hsmode (config, val);
106 } else if (match_option (arg, "sample", &val)) {
107 config->enable_mask |= PROFLOG_PERF_SAMPLING_ALIAS;
108 set_sample_freq (config, val);
109 } else if (match_option (arg, "zip", NULL)) {
110 config->use_zip = TRUE;
111 } else if (match_option (arg, "output", &val)) {
112 config->output_filename = g_strdup (val);
113 } else if (match_option (arg, "port", &val)) {
115 config->command_port = strtoul (val, &end, 10);
116 } else if (match_option (arg, "maxframes", &val)) {
118 int num_frames = strtoul (val, &end, 10);
119 if (num_frames > MAX_FRAMES)
120 num_frames = MAX_FRAMES;
121 config->notraces = num_frames == 0;
122 config->num_frames = num_frames;
123 } else if (match_option (arg, "maxsamples", &val)) {
125 int max_samples = strtoul (val, &end, 10);
127 config->max_allocated_sample_hits = max_samples;
128 } else if (match_option (arg, "calldepth", &val)) {
130 config->max_call_depth = strtoul (val, &end, 10);
131 } else if (match_option (arg, "covfilter-file", &val)) {
132 if (config->cov_filter_files == NULL)
133 config->cov_filter_files = g_ptr_array_new ();
134 g_ptr_array_add (config->cov_filter_files, g_strdup (val));
135 } else if (match_option (arg, "onlycoverage", NULL)) {
136 config->only_coverage = TRUE;
140 for (i = 0; i < G_N_ELEMENTS (event_list); ++i){
141 if (!strcmp (arg, event_list [i].event_name)) {
142 config->enable_mask |= event_list [i].mask;
144 } else if (!strncmp (arg, "no", 2) && !strcmp (arg + 2, event_list [i].event_name)) {
145 config->disable_mask |= event_list [i].mask;
149 if (i == G_N_ELEMENTS (event_list)) {
150 printf ("Could not parse argument %s\n", arg);
156 load_args_from_env_or_default (ProfilerConfig *config)
158 //XXX change this to header constants
160 config->max_allocated_sample_hits = mono_cpu_count () * 1000;
161 config->sample_freq = 100;
162 config->max_call_depth = 100;
163 config->num_frames = MAX_FRAMES;
168 proflog_parse_args (ProfilerConfig *config, const char *desc)
171 gboolean in_quotes = FALSE;
172 char quote_char = '\0';
173 char *buffer = malloc (strlen (desc));
176 load_args_from_env_or_default (config);
178 for (p = desc; *p; p++){
182 if (buffer_pos != 0){
183 buffer [buffer_pos] = 0;
184 parse_arg (buffer, config);
188 buffer [buffer_pos++] = *p;
194 buffer [buffer_pos++] = p[1];
201 if (quote_char == *p)
204 buffer [buffer_pos++] = *p;
211 buffer [buffer_pos++] = *p;
216 if (buffer_pos != 0) {
217 buffer [buffer_pos] = 0;
218 parse_arg (buffer, config);
223 //Compure config effective mask
224 config->effective_mask = config->enable_mask & ~config->disable_mask;
228 set_hsmode (ProfilerConfig *config, const char* val)
234 if (strcmp (val, "ondemand") == 0) {
235 config->hs_mode_ondemand = TRUE;
239 count = strtoul (val, &end, 10);
245 if (strcmp (end, "ms") == 0)
246 config->hs_mode_ms = count;
247 else if (strcmp (end, "gc") == 0)
248 config->hs_mode_gc = count;
254 Sampling frequency allows for one undocumented, hidden and ignored argument. The sampling kind.
255 Back in the day when this was done using perf, we could specify one of: cycles,instr,cacherefs,cachemiss,branches,branchmiss
256 With us moving ot userland sampling, those options are now meaningless.
259 set_sample_freq (ProfilerConfig *config, const char *val)
266 // Is it only the frequency (new option style)?
270 // Skip the sample type for backwards compatibility.
274 // Skip the forward slash only if we got a sample type.
275 if (p != val && *p == '/') {
281 config->sample_freq = strtoul (p, &end, 10);
298 printf ("Log profiler version %d.%d (format: %d)\n", LOG_VERSION_MAJOR, LOG_VERSION_MINOR, LOG_DATA_VERSION);
299 printf ("Usage: mono --profile=log[:OPTION1[,OPTION2...]] program.exe\n");
300 printf ("Options:\n");
301 printf ("\thelp show this usage info\n");
302 printf ("\t[no]'event' enable/disable a profiling event. Valid values: domain, assembly, module, class, jit, exception, gcalloc, gc, thread, monitor, gcmove, gcroot, context, finalization, counter, gchandle\n");
303 printf ("\t[no]typesystem enable/disable typesystem related events such as class and assembly loading\n");
304 printf ("\t[no]alloc enable/disable recording allocation info\n");
305 printf ("\t[no]calls enable/disable recording enter/leave method events\n");
306 printf ("\t[no]legacy enable/disable pre mono 5.4 default profiler events\n");
307 printf ("\tsample[=frequency] enable/disable statistical sampling of threads (frequency in Hz, 100 by default)\n");
308 printf ("\theapshot[=MODE] record heap shot info (by default at each major collection)\n");
309 printf ("\t MODE: every XXms milliseconds, every YYgc collections, ondemand\n");
310 printf ("\t[no]coverage enable collection of code coverage data\n");
311 printf ("\tcovfilter=ASSEMBLY add an assembly to the code coverage filters\n");
312 printf ("\t add a + to include the assembly or a - to exclude it\n");
313 printf ("\t covfilter=-mscorlib\n");
314 printf ("\tcovfilter-file=FILE use FILE to generate the list of assemblies to be filtered\n");
315 printf ("\tmaxframes=NUM collect up to NUM stack frames\n");
316 printf ("\tcalldepth=NUM ignore method events for call chain depth bigger than NUM\n");
317 printf ("\toutput=FILENAME write the data to file FILENAME (The file is always overwriten)\n");
318 printf ("\toutput=+FILENAME write the data to file FILENAME.pid (The file is always overwriten)\n");
319 printf ("\toutput=|PROGRAM write the data to the stdin of PROGRAM\n");
320 printf ("\t %%t is subtituted with date and time, %%p with the pid\n");
321 printf ("\treport create a report instead of writing the raw data to a file\n");
322 printf ("\tzip compress the output data\n");
323 printf ("\tport=PORTNUM use PORTNUM for the listening command server\n");
327 mono_cpu_count (void)
329 #ifdef PLATFORM_ANDROID
330 /* Android tries really hard to save power by powering off CPUs on SMP phones which
331 * means the normal way to query cpu count returns a wrong value with userspace API.
332 * Instead we use /sys entries to query the actual hardware CPU count.
335 char buffer[8] = {'\0'};
336 int present = open ("/sys/devices/system/cpu/present", O_RDONLY);
337 /* Format of the /sys entry is a cpulist of indexes which in the case
338 * of present is always of the form "0-(n-1)" when there is more than
339 * 1 core, n being the number of CPU cores in the system. Otherwise
340 * the value is simply 0
342 if (present != -1 && read (present, (char*)buffer, sizeof (buffer)) > 3)
343 count = strtol (((char*)buffer) + 2, NULL, 10);
350 #if defined(HOST_ARM) || defined (HOST_ARM64)
352 /* ARM platforms tries really hard to save power by powering off CPUs on SMP phones which
353 * means the normal way to query cpu count returns a wrong value with userspace API. */
355 #ifdef _SC_NPROCESSORS_CONF
357 int count = sysconf (_SC_NPROCESSORS_CONF);
365 #ifdef HAVE_SCHED_GETAFFINITY
368 if (sched_getaffinity (getpid (), sizeof (set), &set) == 0)
369 return CPU_COUNT (&set);
372 #ifdef _SC_NPROCESSORS_ONLN
374 int count = sysconf (_SC_NPROCESSORS_ONLN);
380 #endif /* defined(HOST_ARM) || defined (HOST_ARM64) */
386 size_t len = sizeof (int);
389 if (sysctl (mib, 2, &count, &len, NULL, 0) == 0)
396 GetSystemInfo (&info);
397 return info.dwNumberOfProcessors;
401 static gboolean warned;
404 g_warning ("Don't know how to determine CPU count on this platform; assuming 1");