2 #include <mono/utils/mono-logger-internals.h>
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
, "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
)) {
114 config
->command_port
= strtoul (val
, &end
, 10);
115 } else if (match_option (arg
, "maxframes", &val
)) {
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
)) {
124 int max_samples
= strtoul (val
, &end
, 10);
126 config
->max_allocated_sample_hits
= max_samples
;
127 } else if (match_option (arg
, "calldepth", &val
)) {
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
));
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
;
141 } else if (!strncmp (arg
, "no", 2) && !strcmp (arg
+ 2, event_list
[i
].event_name
)) {
142 config
->disable_mask
|= event_list
[i
].mask
;
147 if (i
== G_N_ELEMENTS (event_list
))
148 mono_profiler_printf_err ("Could not parse argument: %s", arg
);
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
;
166 proflog_parse_args (ProfilerConfig
*config
, const char *desc
)
169 gboolean in_quotes
= FALSE
;
170 char quote_char
= '\0';
171 char *buffer
= malloc (strlen (desc
));
174 load_args_from_env_or_default (config
);
176 for (p
= desc
; *p
; p
++){
180 if (buffer_pos
!= 0){
181 buffer
[buffer_pos
] = 0;
182 parse_arg (buffer
, config
);
186 buffer
[buffer_pos
++] = *p
;
192 buffer
[buffer_pos
++] = p
[1];
199 if (quote_char
== *p
)
202 buffer
[buffer_pos
++] = *p
;
209 buffer
[buffer_pos
++] = *p
;
214 if (buffer_pos
!= 0) {
215 buffer
[buffer_pos
] = 0;
216 parse_arg (buffer
, config
);
221 //Compure config effective mask
222 config
->effective_mask
= config
->enable_mask
& ~config
->disable_mask
;
226 set_hsmode (ProfilerConfig
*config
, const char* val
)
232 if (strcmp (val
, "ondemand") == 0) {
233 config
->hs_mode_ondemand
= TRUE
;
237 count
= strtoul (val
, &end
, 10);
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
;
252 set_sample_freq (ProfilerConfig
*config
, const char *val
)
259 int freq
= strtoul (val
, &end
, 10);
266 config
->sample_freq
= freq
;
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");
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.
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);
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
);
344 #ifdef HAVE_SCHED_GETAFFINITY
347 if (sched_getaffinity (getpid (), sizeof (set
), &set
) == 0)
348 return CPU_COUNT (&set
);
351 #ifdef _SC_NPROCESSORS_ONLN
353 int count
= sysconf (_SC_NPROCESSORS_ONLN
);
359 #endif /* defined(HOST_ARM) || defined (HOST_ARM64) */
365 size_t len
= sizeof (int);
368 if (sysctl (mib
, 2, &count
, &len
, NULL
, 0) == 0)
375 GetSystemInfo (&info
);
376 return info
.dwNumberOfProcessors
;
380 static gboolean warned
;
383 g_warning ("Don't know how to determine CPU count on this platform; assuming 1");