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 (arg
[0] == 'n' && arg
[1] == 'o' && !strcmp (arg
+ 2, event_list
[i
].event_name
)) {
145 config
->disable_mask
|= event_list
[i
].mask
;
148 if (i
== G_N_ELEMENTS (event_list
)) {
149 printf ("Could not parse argument %s\n", arg
);
155 load_args_from_env_or_default (ProfilerConfig
*config
)
157 //XXX change this to header constants
159 config
->max_allocated_sample_hits
= mono_cpu_count () * 1000;
160 config
->sample_freq
= 100;
161 config
->max_call_depth
= 100;
162 config
->num_frames
= MAX_FRAMES
;
167 proflog_parse_args (ProfilerConfig
*config
, const char *desc
)
170 gboolean in_quotes
= FALSE
;
171 char quote_char
= '\0';
172 char *buffer
= malloc (strlen (desc
));
175 load_args_from_env_or_default (config
);
177 for (p
= desc
; *p
; p
++){
181 if (buffer_pos
!= 0){
182 buffer
[buffer_pos
] = 0;
183 parse_arg (buffer
, config
);
187 buffer
[buffer_pos
++] = *p
;
193 buffer
[buffer_pos
++] = p
[1];
200 if (quote_char
== *p
)
203 buffer
[buffer_pos
++] = *p
;
210 buffer
[buffer_pos
++] = *p
;
215 if (buffer_pos
!= 0) {
216 buffer
[buffer_pos
] = 0;
217 parse_arg (buffer
, config
);
222 //Compure config effective mask
223 config
->effective_mask
= config
->enable_mask
& ~config
->disable_mask
;
227 set_hsmode (ProfilerConfig
*config
, const char* val
)
233 if (strcmp (val
, "ondemand") == 0) {
234 config
->hs_mode_ondemand
= TRUE
;
238 count
= strtoul (val
, &end
, 10);
244 if (strcmp (end
, "ms") == 0)
245 config
->hs_mode_ms
= count
;
246 else if (strcmp (end
, "gc") == 0)
247 config
->hs_mode_gc
= count
;
253 Sampling frequency allows for one undocumented, hidden and ignored argument. The sampling kind.
254 Back in the day when this was done using perf, we could specify one of: cycles,instr,cacherefs,cachemiss,branches,branchmiss
255 With us moving ot userland sampling, those options are now meaningless.
258 set_sample_freq (ProfilerConfig
*config
, const char *val
)
265 // Is it only the frequency (new option style)?
269 // Skip the sample type for backwards compatibility.
273 // Skip the forward slash only if we got a sample type.
274 if (p
!= val
&& *p
== '/') {
280 config
->sample_freq
= strtoul (p
, &end
, 10);
297 printf ("Log profiler version %d.%d (format: %d)\n", LOG_VERSION_MAJOR
, LOG_VERSION_MINOR
, LOG_DATA_VERSION
);
298 printf ("Usage: mono --profile=log[:OPTION1[,OPTION2...]] program.exe\n");
299 printf ("Options:\n");
300 printf ("\thelp show this usage info\n");
301 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");
302 printf ("\t[no]typesystem enable/disable typesystem related events such as class and assembly loading\n");
303 printf ("\t[no]alloc enable/disable recording allocation info\n");
304 printf ("\t[no]calls enable/disable recording enter/leave method events\n");
305 printf ("\t[no]legacy enable/disable pre mono 5.4 default profiler events\n");
306 printf ("\tsample[=frequency] enable/disable statistical sampling of threads (frequency in Hz, 100 by default)\n");
307 printf ("\theapshot[=MODE] record heap shot info (by default at each major collection)\n");
308 printf ("\t MODE: every XXms milliseconds, every YYgc collections, ondemand\n");
309 printf ("\t[no]coverage enable collection of code coverage data\n");
310 printf ("\tcovfilter=ASSEMBLY add an assembly to the code coverage filters\n");
311 printf ("\t add a + to include the assembly or a - to exclude it\n");
312 printf ("\t covfilter=-mscorlib\n");
313 printf ("\tcovfilter-file=FILE use FILE to generate the list of assemblies to be filtered\n");
314 printf ("\tmaxframes=NUM collect up to NUM stack frames\n");
315 printf ("\tcalldepth=NUM ignore method events for call chain depth bigger than NUM\n");
316 printf ("\toutput=FILENAME write the data to file FILENAME (The file is always overwriten)\n");
317 printf ("\toutput=+FILENAME write the data to file FILENAME.pid (The file is always overwriten)\n");
318 printf ("\toutput=|PROGRAM write the data to the stdin of PROGRAM\n");
319 printf ("\t %%t is subtituted with date and time, %%p with the pid\n");
320 printf ("\treport create a report instead of writing the raw data to a file\n");
321 printf ("\tzip compress the output data\n");
322 printf ("\tport=PORTNUM use PORTNUM for the listening command server\n");
326 mono_cpu_count (void)
328 #ifdef PLATFORM_ANDROID
329 /* Android tries really hard to save power by powering off CPUs on SMP phones which
330 * means the normal way to query cpu count returns a wrong value with userspace API.
331 * Instead we use /sys entries to query the actual hardware CPU count.
334 char buffer
[8] = {'\0'};
335 int present
= open ("/sys/devices/system/cpu/present", O_RDONLY
);
336 /* Format of the /sys entry is a cpulist of indexes which in the case
337 * of present is always of the form "0-(n-1)" when there is more than
338 * 1 core, n being the number of CPU cores in the system. Otherwise
339 * the value is simply 0
341 if (present
!= -1 && read (present
, (char*)buffer
, sizeof (buffer
)) > 3)
342 count
= strtol (((char*)buffer
) + 2, NULL
, 10);
349 #if defined(HOST_ARM) || defined (HOST_ARM64)
351 /* ARM platforms tries really hard to save power by powering off CPUs on SMP phones which
352 * means the normal way to query cpu count returns a wrong value with userspace API. */
354 #ifdef _SC_NPROCESSORS_CONF
356 int count
= sysconf (_SC_NPROCESSORS_CONF
);
364 #ifdef HAVE_SCHED_GETAFFINITY
367 if (sched_getaffinity (getpid (), sizeof (set
), &set
) == 0)
368 return CPU_COUNT (&set
);
371 #ifdef _SC_NPROCESSORS_ONLN
373 int count
= sysconf (_SC_NPROCESSORS_ONLN
);
379 #endif /* defined(HOST_ARM) || defined (HOST_ARM64) */
385 size_t len
= sizeof (int);
388 if (sysctl (mib
, 2, &count
, &len
, NULL
, 0) == 0)
395 GetSystemInfo (&info
);
396 return info
.dwNumberOfProcessors
;
400 static gboolean warned
;
403 g_warning ("Don't know how to determine CPU count on this platform; assuming 1");