Revert some changes which don't have proper dependencies.
[mono-project.git] / mono / profiler / aot.c
blob58e50752df8f6ad9f7e4f6aeec01671a8fa800ee
1 /*
2 * aot.c: Ahead of Time Compiler Profiler for Mono.
5 * Copyright 2008-2009 Novell, Inc (http://www.novell.com)
6 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
7 */
9 #include <config.h>
11 #include "aot.h"
13 #include <mono/metadata/object-internals.h>
14 #include <mono/metadata/profiler.h>
15 #include <mono/metadata/tokentype.h>
16 #include <mono/metadata/tabledefs.h>
17 #include <mono/metadata/debug-helpers.h>
18 #include <mono/metadata/assembly.h>
19 #include <mono/metadata/class-internals.h>
20 #include <mono/metadata/debug-helpers.h>
21 #include <mono/mini/jit.h>
22 #include <mono/utils/mono-logger-internals.h>
23 #include <mono/utils/mono-os-mutex.h>
24 #include <mono/utils/mono-threads.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <stdlib.h>
28 #include <glib.h>
29 #include <mono/utils/mono-error-internals.h>
31 struct _MonoProfiler {
32 GHashTable *classes;
33 GHashTable *images;
34 GPtrArray *methods;
35 FILE *outfile;
36 int id;
37 char *outfile_name;
38 mono_mutex_t mutex;
39 gboolean verbose;
40 int duration;
41 MonoMethodDesc *write_at;
42 MonoMethodDesc *send_to;
43 char *send_to_arg;
44 char *send_to_str;
45 guint8 *buf;
46 gboolean disable;
47 int buf_pos, buf_len;
50 static MonoProfiler aot_profiler;
52 static void
53 prof_shutdown (MonoProfiler *prof);
55 static void
56 prof_jit_done (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo)
58 MonoImage *image = mono_class_get_image (mono_method_get_class (method));
60 if (!image->assembly || method->wrapper_type || !prof->methods || prof->disable)
61 return;
63 if (prof->write_at && mono_method_desc_match (prof->write_at, method)) {
64 printf ("aot-profiler | Writing data at: '%s'.\n", mono_method_full_name (method, 1));
65 prof_shutdown (prof);
66 return;
69 mono_os_mutex_lock (&prof->mutex);
70 if (prof->methods)
71 g_ptr_array_add (prof->methods, method);
72 mono_os_mutex_unlock (&prof->mutex);
75 static void
76 usage (void)
78 mono_profiler_printf ("AOT profiler.");
79 mono_profiler_printf ("Usage: mono --profile=aot[:OPTION1[,OPTION2...]] program.exe\n");
80 mono_profiler_printf ("Options:");
81 mono_profiler_printf ("\tduration=NUM profile only NUM seconds of runtime and write the data");
82 mono_profiler_printf ("\thelp show this usage info");
83 mono_profiler_printf ("\toutput=FILENAME write the data to file FILENAME");
84 mono_profiler_printf ("\twrite-at-method=METHOD write the data when METHOD is compiled.");
85 mono_profiler_printf ("\tsend-to-method=METHOD call METHOD with the collected data.");
86 mono_profiler_printf ("\tsend-to-arg=STR extra argument to pass to METHOD.");
87 mono_profiler_printf ("\tverbose print diagnostic info");
89 exit (0);
92 static gboolean
93 match_option (const char *arg, const char *opt_name, const char **rval)
95 if (rval) {
96 const char *end = strchr (arg, '=');
98 *rval = NULL;
99 if (!end)
100 return !strcmp (arg, opt_name);
102 if (strncmp (arg, opt_name, strlen (opt_name)) || (end - arg) > strlen (opt_name) + 1)
103 return FALSE;
104 *rval = end + 1;
105 return TRUE;
106 } else {
107 //FIXME how should we handle passing a value to an arg that doesn't expect it?
108 return !strcmp (arg, opt_name);
112 static void
113 parse_arg (const char *arg)
115 const char *val;
117 if (match_option (arg, "help", NULL)) {
118 usage ();
119 } else if (match_option (arg, "duration", &val)) {
120 char *end;
121 aot_profiler.duration = strtoul (val, &end, 10);
122 } else if (match_option (arg, "write-at-method", &val)) {
123 aot_profiler.write_at = mono_method_desc_new (val, TRUE);
124 if (!aot_profiler.write_at) {
125 mono_profiler_printf_err ("Could not parse method description: %s", val);
126 exit (1);
128 } else if (match_option (arg, "send-to-method", &val)) {
129 aot_profiler.send_to = mono_method_desc_new (val, TRUE);
130 if (!aot_profiler.send_to) {
131 mono_profiler_printf_err ("Could not parse method description: %s", val);
132 exit (1);
134 aot_profiler.send_to_str = strdup (val);
135 } else if (match_option (arg, "send-to-arg", &val)) {
136 aot_profiler.send_to_arg = strdup (val);
137 } else if (match_option (arg, "output", &val)) {
138 aot_profiler.outfile_name = g_strdup (val);
139 } else if (match_option (arg, "verbose", NULL)) {
140 aot_profiler.verbose = TRUE;
141 } else {
142 mono_profiler_printf_err ("Could not parse argument: %s", arg);
146 static void
147 parse_args (const char *desc)
149 const char *p;
150 gboolean in_quotes = FALSE;
151 char quote_char = '\0';
152 char *buffer = g_malloc (strlen (desc) + 1);
153 int buffer_pos = 0;
155 for (p = desc; *p; p++){
156 switch (*p){
157 case ',':
158 if (!in_quotes) {
159 if (buffer_pos != 0){
160 buffer [buffer_pos] = 0;
161 parse_arg (buffer);
162 buffer_pos = 0;
164 } else {
165 buffer [buffer_pos++] = *p;
167 break;
169 case '\\':
170 if (p [1]) {
171 buffer [buffer_pos++] = p[1];
172 p++;
174 break;
175 case '\'':
176 case '"':
177 if (in_quotes) {
178 if (quote_char == *p)
179 in_quotes = FALSE;
180 else
181 buffer [buffer_pos++] = *p;
182 } else {
183 in_quotes = TRUE;
184 quote_char = *p;
186 break;
187 default:
188 buffer [buffer_pos++] = *p;
189 break;
193 if (buffer_pos != 0) {
194 buffer [buffer_pos] = 0;
195 parse_arg (buffer);
198 g_free (buffer);
201 static void *
202 helper_thread (void *arg)
204 mono_thread_attach (mono_get_root_domain ());
206 MonoInternalThread *internal = mono_thread_internal_current ();
208 ERROR_DECL (error);
210 MonoString *name_str = mono_string_new_checked (mono_get_root_domain (), "AOT Profiler Helper", error);
211 mono_error_assert_ok (error);
212 mono_thread_set_name_internal (internal, name_str, FALSE, FALSE, error);
213 mono_error_assert_ok (error);
215 mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NO_GC | MONO_THREAD_INFO_FLAGS_NO_SAMPLE);
217 sleep (aot_profiler.duration);
219 prof_shutdown (&aot_profiler);
221 mono_thread_info_set_flags (MONO_THREAD_INFO_FLAGS_NONE);
222 mono_thread_detach (mono_thread_current ());
224 return NULL;
227 static void
228 start_helper_thread (void)
230 MonoNativeThreadId thread_id;
232 if (!mono_native_thread_create (&thread_id, helper_thread, NULL)) {
233 mono_profiler_printf_err ("Could not start aot profiler helper thread");
234 exit (1);
238 static void
239 runtime_initialized (MonoProfiler *profiler)
241 if (profiler->duration >= 0)
242 start_helper_thread ();
245 void
246 mono_profiler_init_aot (const char *desc);
249 * mono_profiler_init_aot:
250 * the entry point
252 void
253 mono_profiler_init_aot (const char *desc)
255 if (mono_jit_aot_compiling ()) {
256 mono_profiler_printf_err ("The AOT profiler is not meant to be run during AOT compilation.");
257 exit (1);
260 aot_profiler.duration = -1;
262 parse_args (desc [strlen ("aot")] == ':' ? desc + strlen ("aot") + 1 : "");
264 if (!aot_profiler.send_to) {
265 if (!aot_profiler.outfile_name)
266 aot_profiler.outfile_name = g_strdup ("output.aotprofile");
267 else if (*aot_profiler.outfile_name == '+')
268 aot_profiler.outfile_name = g_strdup_printf ("%s.%d", aot_profiler.outfile_name + 1, getpid ());
270 if (*aot_profiler.outfile_name == '|') {
271 #ifdef HAVE_POPEN
272 aot_profiler.outfile = popen (aot_profiler.outfile_name + 1, "w");
273 #else
274 g_assert_not_reached ();
275 #endif
276 } else if (*aot_profiler.outfile_name == '#') {
277 aot_profiler.outfile = fdopen (strtol (aot_profiler.outfile_name + 1, NULL, 10), "a");
278 } else {
279 aot_profiler.outfile = fopen (aot_profiler.outfile_name, "w");
282 if (!aot_profiler.outfile) {
283 mono_profiler_printf_err ("Could not create AOT profiler output file '%s': %s", aot_profiler.outfile_name, g_strerror (errno));
284 exit (1);
288 aot_profiler.images = g_hash_table_new (NULL, NULL);
289 aot_profiler.classes = g_hash_table_new (NULL, NULL);
290 aot_profiler.methods = g_ptr_array_new ();
292 mono_os_mutex_init (&aot_profiler.mutex);
294 MonoProfilerHandle handle = mono_profiler_create (&aot_profiler);
295 mono_profiler_set_runtime_initialized_callback (handle, runtime_initialized);
296 mono_profiler_set_runtime_shutdown_end_callback (handle, prof_shutdown);
297 mono_profiler_set_jit_done_callback (handle, prof_jit_done);
300 static void
301 make_room (MonoProfiler *prof, int n)
303 if (prof->buf_pos + n >= prof->buf_len) {
304 int new_len = prof->buf_len * 2;
305 guint8 *new_buf = g_malloc0 (new_len);
306 memcpy (new_buf, prof->buf, prof->buf_pos);
307 g_free (prof->buf);
308 prof->buf = new_buf;
309 prof->buf_len = new_len;
313 static void
314 emit_bytes (MonoProfiler *prof, guint8 *bytes, int len)
316 make_room (prof, len);
317 memcpy (prof->buf + prof->buf_pos, bytes, len);
318 prof->buf_pos += len;
321 static void
322 emit_byte (MonoProfiler *prof, guint8 value)
324 emit_bytes (prof, &value, 1);
327 static void
328 emit_int32 (MonoProfiler *prof, gint32 value)
330 for (int i = 0; i < sizeof (gint32); ++i) {
331 guint8 b = value;
332 emit_bytes (prof, &b, 1);
333 value >>= 8;
337 static void
338 emit_string (MonoProfiler *prof, const char *str)
340 int len = strlen (str);
342 emit_int32 (prof, len);
343 emit_bytes (prof, (guint8*)str, len);
346 static void
347 emit_record (MonoProfiler *prof, AotProfRecordType type, int id)
349 emit_byte (prof, type);
350 emit_int32 (prof, id);
353 static int
354 add_image (MonoProfiler *prof, MonoImage *image)
356 int id = GPOINTER_TO_INT (g_hash_table_lookup (prof->images, image));
357 if (id)
358 return id - 1;
360 // Dynamic images don't have a GUID set. Moreover, we won't
361 // have a chance to AOT them. (But perhaps they should be
362 // included in the profile, or logged, for diagnostic purposes?)
363 if (!image->guid)
364 return -1;
366 id = prof->id ++;
367 emit_record (prof, AOTPROF_RECORD_IMAGE, id);
368 emit_string (prof, image->assembly->aname.name);
369 emit_string (prof, image->guid);
370 g_hash_table_insert (prof->images, image, GINT_TO_POINTER (id + 1));
371 return id;
374 static int
375 add_class (MonoProfiler *prof, MonoClass *klass);
377 static int
378 add_type (MonoProfiler *prof, MonoType *type)
380 switch (type->type) {
381 #if 0
382 case MONO_TYPE_SZARRAY: {
383 int eid = add_type (prof, m_class_get_byval_arg (type->data.klass));
384 if (eid == -1)
385 return -1;
386 int id = prof->id ++;
387 emit_record (prof, AOTPROF_RECORD_TYPE, id);
388 emit_byte (prof, MONO_TYPE_SZARRAY);
389 emit_int32 (prof, id);
390 return id;
392 #endif
393 case MONO_TYPE_BOOLEAN:
394 case MONO_TYPE_CHAR:
395 case MONO_TYPE_I1:
396 case MONO_TYPE_U1:
397 case MONO_TYPE_I2:
398 case MONO_TYPE_U2:
399 case MONO_TYPE_I4:
400 case MONO_TYPE_U4:
401 case MONO_TYPE_I8:
402 case MONO_TYPE_U8:
403 case MONO_TYPE_R4:
404 case MONO_TYPE_R8:
405 case MONO_TYPE_I:
406 case MONO_TYPE_U:
407 case MONO_TYPE_OBJECT:
408 case MONO_TYPE_STRING:
409 case MONO_TYPE_CLASS:
410 case MONO_TYPE_VALUETYPE:
411 case MONO_TYPE_GENERICINST:
412 return add_class (prof, mono_class_from_mono_type_internal (type));
413 default:
414 return -1;
418 static int
419 add_ginst (MonoProfiler *prof, MonoGenericInst *inst)
421 int i, id;
422 int *ids;
424 // FIXME: Cache
425 ids = g_malloc0 (inst->type_argc * sizeof (int));
426 for (i = 0; i < inst->type_argc; ++i) {
427 MonoType *t = inst->type_argv [i];
428 ids [i] = add_type (prof, t);
429 if (ids [i] == -1) {
430 g_free (ids);
431 return -1;
434 id = prof->id ++;
435 emit_record (prof, AOTPROF_RECORD_GINST, id);
436 emit_int32 (prof, inst->type_argc);
437 for (i = 0; i < inst->type_argc; ++i)
438 emit_int32 (prof, ids [i]);
439 g_free (ids);
441 return id;
444 static int
445 add_class (MonoProfiler *prof, MonoClass *klass)
447 int id, inst_id = -1, image_id;
448 char *name;
450 id = GPOINTER_TO_INT (g_hash_table_lookup (prof->classes, klass));
451 if (id)
452 return id - 1;
454 image_id = add_image (prof, mono_class_get_image (klass));
455 if (image_id == -1)
456 return -1;
458 if (mono_class_is_ginst (klass)) {
459 MonoGenericContext *ctx = mono_class_get_context (klass);
460 inst_id = add_ginst (prof, ctx->class_inst);
461 if (inst_id == -1)
462 return -1;
465 MonoClass *klass_nested_in = mono_class_get_nesting_type (klass);
466 if (klass_nested_in)
467 name = g_strdup_printf ("%s.%s/%s", m_class_get_name_space (klass_nested_in), m_class_get_name (klass_nested_in), m_class_get_name (klass));
468 else
469 name = g_strdup_printf ("%s.%s", m_class_get_name_space (klass), m_class_get_name (klass));
471 id = prof->id ++;
472 emit_record (prof, AOTPROF_RECORD_TYPE, id);
473 emit_byte (prof, MONO_TYPE_CLASS);
474 emit_int32 (prof, image_id);
475 emit_int32 (prof, inst_id);
476 emit_string (prof, name);
477 g_free (name);
478 g_hash_table_insert (prof->classes, klass, GINT_TO_POINTER (id + 1));
479 return id;
482 static void
483 add_method (MonoProfiler *prof, MonoMethod *m)
485 ERROR_DECL (error);
486 MonoMethodSignature *sig;
487 char *s;
489 sig = mono_method_signature_checked (m, error);
490 g_assert (mono_error_ok (error));
492 int class_id = add_class (prof, m->klass);
493 if (class_id == -1)
494 return;
495 int inst_id = -1;
497 if (m->is_inflated) {
498 MonoGenericContext *ctx = mono_method_get_context (m);
499 if (ctx->method_inst)
500 inst_id = add_ginst (prof, ctx->method_inst);
502 int id = prof->id ++;
503 emit_record (prof, AOTPROF_RECORD_METHOD, id);
504 emit_int32 (prof, class_id);
505 emit_int32 (prof, inst_id);
506 emit_int32 (prof, sig->param_count);
507 emit_string (prof, m->name);
508 s = mono_signature_full_name (sig);
509 emit_string (prof, s);
510 g_free (s);
512 if (prof->verbose)
513 mono_profiler_printf ("%s %d", mono_method_full_name (m, 1), id);
516 /* called at the end of the program */
517 static void
518 prof_shutdown (MonoProfiler *prof)
520 mono_os_mutex_lock (&prof->mutex);
521 int already_shutdown = prof->methods == NULL;
522 mono_os_mutex_unlock (&prof->mutex);
524 if (already_shutdown)
525 return;
527 int mindex;
528 char magic [32];
530 prof->buf_len = 4096;
531 prof->buf = g_malloc0 (prof->buf_len);
532 prof->buf_pos = 0;
534 gint32 version = (AOT_PROFILER_MAJOR_VERSION << 16) | AOT_PROFILER_MINOR_VERSION;
535 sprintf (magic, AOT_PROFILER_MAGIC);
536 emit_bytes (prof, (guint8*)magic, strlen (magic));
537 emit_int32 (prof, version);
539 GHashTable *all_methods = g_hash_table_new (NULL, NULL);
540 mono_os_mutex_lock (&prof->mutex);
541 for (mindex = 0; mindex < prof->methods->len; ++mindex) {
542 MonoMethod *m = (MonoMethod*)g_ptr_array_index (prof->methods, mindex);
544 if (!mono_method_get_token (m))
545 continue;
547 if (g_hash_table_lookup (all_methods, m))
548 continue;
549 g_hash_table_insert (all_methods, m, m);
551 add_method (prof, m);
553 emit_record (prof, AOTPROF_RECORD_NONE, 0);
555 if (prof->send_to) {
556 GHashTableIter iter;
557 gpointer id;
558 MonoImage *image;
559 MonoMethod *send_method = NULL;
560 MonoMethodSignature *sig;
561 ERROR_DECL (error);
563 g_hash_table_iter_init (&iter, prof->images);
564 while (g_hash_table_iter_next (&iter, (void**)&image, (void**)&id)) {
565 send_method = mono_method_desc_search_in_image (prof->send_to, image);
566 if (send_method)
567 break;
569 if (!send_method) {
570 mono_profiler_printf_err ("Cannot find method in loaded assemblies: '%s'.", prof->send_to_str);
571 exit (1);
574 sig = mono_method_signature_checked (send_method, error);
575 mono_error_assert_ok (error);
576 if (sig->param_count != 3 || !sig->params [0]->byref || sig->params [0]->type != MONO_TYPE_U1 || sig->params [1]->type != MONO_TYPE_I4 || sig->params [2]->type != MONO_TYPE_STRING) {
577 mono_profiler_printf_err ("Method '%s' should have signature void (byte&,int,string).", prof->send_to_str);
578 exit (1);
581 // Don't collect data from the call
582 prof->disable = TRUE;
584 MonoString *extra_arg = NULL;
585 if (prof->send_to_arg) {
586 extra_arg = mono_string_new_checked (mono_domain_get (), prof->send_to_arg, error);
587 mono_error_assert_ok (error);
590 MonoObject *exc;
591 gpointer args [3];
592 int len = prof->buf_pos;
593 void *ptr = prof->buf;
594 args [0] = ptr;
595 args [1] = &len;
596 args [2] = extra_arg;
598 printf ("aot-profiler | Passing data to '%s': %p %d %s\n", mono_method_full_name (send_method, 1), args [0], len, extra_arg);
599 mono_runtime_try_invoke (send_method, NULL, args, &exc, error);
600 mono_error_assert_ok (error);
601 g_assert (exc == NULL);
602 } else {
603 g_assert (prof->outfile);
604 fwrite (prof->buf, 1, prof->buf_pos, prof->outfile);
605 fclose (prof->outfile);
607 mono_profiler_printf ("AOT profiler data written to '%s'", prof->outfile_name);
610 g_hash_table_destroy (all_methods);
611 g_hash_table_destroy (prof->classes);
612 g_hash_table_destroy (prof->images);
613 g_ptr_array_free (prof->methods, TRUE);
614 g_free (prof->outfile_name);
616 prof->methods = NULL;
617 mono_os_mutex_unlock (&prof->mutex);