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.
13 #include <mono/metadata/profiler.h>
14 #include <mono/metadata/tokentype.h>
15 #include <mono/metadata/tabledefs.h>
16 #include <mono/metadata/debug-helpers.h>
17 #include <mono/metadata/assembly.h>
18 #include <mono/metadata/class-internals.h>
19 #include <mono/mini/jit.h>
20 #include <mono/utils/mono-logger-internals.h>
21 #include <mono/utils/mono-os-mutex.h>
26 #include <mono/utils/mono-error-internals.h>
28 struct _MonoProfiler
{
39 static MonoProfiler aot_profiler
;
42 prof_jit_done (MonoProfiler
*prof
, MonoMethod
*method
, MonoJitInfo
*jinfo
)
44 MonoImage
*image
= mono_class_get_image (mono_method_get_class (method
));
46 if (!image
->assembly
|| method
->wrapper_type
)
49 mono_os_mutex_lock (&prof
->mutex
);
50 g_ptr_array_add (prof
->methods
, method
);
51 mono_os_mutex_unlock (&prof
->mutex
);
55 prof_shutdown (MonoProfiler
*prof
);
60 mono_profiler_printf ("AOT profiler.\n");
61 mono_profiler_printf ("Usage: mono --profile=aot[:OPTION1[,OPTION2...]] program.exe\n");
62 mono_profiler_printf ("Options:\n");
63 mono_profiler_printf ("\thelp show this usage info\n");
64 mono_profiler_printf ("\toutput=FILENAME write the data to file FILENAME\n");
65 mono_profiler_printf ("\tverbose print diagnostic info\n");
71 match_option (const char *arg
, const char *opt_name
, const char **rval
)
74 const char *end
= strchr (arg
, '=');
78 return !strcmp (arg
, opt_name
);
80 if (strncmp (arg
, opt_name
, strlen (opt_name
)) || (end
- arg
) > strlen (opt_name
) + 1)
85 //FIXME how should we handle passing a value to an arg that doesn't expect it?
86 return !strcmp (arg
, opt_name
);
91 parse_arg (const char *arg
)
95 if (match_option (arg
, "help", NULL
)) {
97 } else if (match_option (arg
, "output", &val
)) {
98 aot_profiler
.outfile_name
= g_strdup (val
);
99 } else if (match_option (arg
, "verbose", NULL
)) {
100 aot_profiler
.verbose
= TRUE
;
102 mono_profiler_printf_err ("Could not parse argument: %s", arg
);
107 parse_args (const char *desc
)
110 gboolean in_quotes
= FALSE
;
111 char quote_char
= '\0';
112 char *buffer
= g_malloc (strlen (desc
) + 1);
115 for (p
= desc
; *p
; p
++){
119 if (buffer_pos
!= 0){
120 buffer
[buffer_pos
] = 0;
125 buffer
[buffer_pos
++] = *p
;
131 buffer
[buffer_pos
++] = p
[1];
138 if (quote_char
== *p
)
141 buffer
[buffer_pos
++] = *p
;
148 buffer
[buffer_pos
++] = *p
;
153 if (buffer_pos
!= 0) {
154 buffer
[buffer_pos
] = 0;
162 mono_profiler_init_aot (const char *desc
);
165 * mono_profiler_init_aot:
169 mono_profiler_init_aot (const char *desc
)
171 if (mono_jit_aot_compiling ()) {
172 mono_profiler_printf_err ("The AOT profiler is not meant to be run during AOT compilation.");
176 parse_args (desc
[strlen ("aot")] == ':' ? desc
+ strlen ("aot") + 1 : "");
178 if (!aot_profiler
.outfile_name
)
179 aot_profiler
.outfile_name
= g_strdup ("output.aotprofile");
180 else if (*aot_profiler
.outfile_name
== '+')
181 aot_profiler
.outfile_name
= g_strdup_printf ("%s.%d", aot_profiler
.outfile_name
+ 1, getpid ());
183 if (*aot_profiler
.outfile_name
== '|')
184 aot_profiler
.outfile
= popen (aot_profiler
.outfile_name
+ 1, "w");
185 else if (*aot_profiler
.outfile_name
== '#')
186 aot_profiler
.outfile
= fdopen (strtol (aot_profiler
.outfile_name
+ 1, NULL
, 10), "a");
188 aot_profiler
.outfile
= fopen (aot_profiler
.outfile_name
, "w");
190 if (!aot_profiler
.outfile
) {
191 mono_profiler_printf_err ("Could not create AOT profiler output file '%s': %s", aot_profiler
.outfile_name
, g_strerror (errno
));
195 aot_profiler
.images
= g_hash_table_new (NULL
, NULL
);
196 aot_profiler
.classes
= g_hash_table_new (NULL
, NULL
);
197 aot_profiler
.methods
= g_ptr_array_new ();
199 mono_os_mutex_init (&aot_profiler
.mutex
);
201 MonoProfilerHandle handle
= mono_profiler_create (&aot_profiler
);
202 mono_profiler_set_runtime_shutdown_end_callback (handle
, prof_shutdown
);
203 mono_profiler_set_jit_done_callback (handle
, prof_jit_done
);
207 emit_byte (MonoProfiler
*prof
, guint8 value
)
209 fwrite (&value
, sizeof (guint8
), 1, prof
->outfile
);
213 emit_int32 (MonoProfiler
*prof
, gint32 value
)
215 for (int i
= 0; i
< sizeof (gint32
); ++i
) {
217 fwrite (&b
, sizeof (guint8
), 1, prof
->outfile
);
223 emit_string (MonoProfiler
*prof
, const char *str
)
225 int len
= strlen (str
);
227 emit_int32 (prof
, len
);
228 fwrite (str
, len
, 1, prof
->outfile
);
232 emit_record (MonoProfiler
*prof
, AotProfRecordType type
, int id
)
234 emit_byte (prof
, type
);
235 emit_int32 (prof
, id
);
239 add_image (MonoProfiler
*prof
, MonoImage
*image
)
241 int id
= GPOINTER_TO_INT (g_hash_table_lookup (prof
->images
, image
));
246 emit_record (prof
, AOTPROF_RECORD_IMAGE
, id
);
247 emit_string (prof
, image
->assembly
->aname
.name
);
248 emit_string (prof
, image
->guid
);
249 g_hash_table_insert (prof
->images
, image
, GINT_TO_POINTER (id
+ 1));
254 add_class (MonoProfiler
*prof
, MonoClass
*klass
);
257 add_type (MonoProfiler
*prof
, MonoType
*type
)
259 switch (type
->type
) {
261 case MONO_TYPE_SZARRAY
: {
262 int eid
= add_type (prof
, m_class_get_byval_arg (type
->data
.klass
));
265 int id
= prof
->id
++;
266 emit_record (prof
, AOTPROF_RECORD_TYPE
, id
);
267 emit_byte (prof
, MONO_TYPE_SZARRAY
);
268 emit_int32 (prof
, id
);
272 case MONO_TYPE_BOOLEAN
:
286 case MONO_TYPE_OBJECT
:
287 case MONO_TYPE_STRING
:
288 case MONO_TYPE_CLASS
:
289 case MONO_TYPE_VALUETYPE
:
290 case MONO_TYPE_GENERICINST
:
291 return add_class (prof
, mono_class_from_mono_type_internal (type
));
298 add_ginst (MonoProfiler
*prof
, MonoGenericInst
*inst
)
304 ids
= g_malloc0 (inst
->type_argc
* sizeof (int));
305 for (i
= 0; i
< inst
->type_argc
; ++i
) {
306 MonoType
*t
= inst
->type_argv
[i
];
307 ids
[i
] = add_type (prof
, t
);
314 emit_record (prof
, AOTPROF_RECORD_GINST
, id
);
315 emit_int32 (prof
, inst
->type_argc
);
316 for (i
= 0; i
< inst
->type_argc
; ++i
)
317 emit_int32 (prof
, ids
[i
]);
324 add_class (MonoProfiler
*prof
, MonoClass
*klass
)
326 int id
, inst_id
= -1, image_id
;
329 id
= GPOINTER_TO_INT (g_hash_table_lookup (prof
->classes
, klass
));
333 image_id
= add_image (prof
, mono_class_get_image (klass
));
335 if (mono_class_is_ginst (klass
)) {
336 MonoGenericContext
*ctx
= mono_class_get_context (klass
);
337 inst_id
= add_ginst (prof
, ctx
->class_inst
);
342 MonoClass
*klass_nested_in
= mono_class_get_nesting_type (klass
);
344 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
));
346 name
= g_strdup_printf ("%s.%s", m_class_get_name_space (klass
), m_class_get_name (klass
));
349 emit_record (prof
, AOTPROF_RECORD_TYPE
, id
);
350 emit_byte (prof
, MONO_TYPE_CLASS
);
351 emit_int32 (prof
, image_id
);
352 emit_int32 (prof
, inst_id
);
353 emit_string (prof
, name
);
355 g_hash_table_insert (prof
->classes
, klass
, GINT_TO_POINTER (id
+ 1));
360 add_method (MonoProfiler
*prof
, MonoMethod
*m
)
363 MonoMethodSignature
*sig
;
366 sig
= mono_method_signature_checked (m
, error
);
367 g_assert (mono_error_ok (error
));
369 int class_id
= add_class (prof
, m
->klass
);
374 if (m
->is_inflated
) {
375 MonoGenericContext
*ctx
= mono_method_get_context (m
);
376 if (ctx
->method_inst
)
377 inst_id
= add_ginst (prof
, ctx
->method_inst
);
379 int id
= prof
->id
++;
380 emit_record (prof
, AOTPROF_RECORD_METHOD
, id
);
381 emit_int32 (prof
, class_id
);
382 emit_int32 (prof
, inst_id
);
383 emit_int32 (prof
, sig
->param_count
);
384 emit_string (prof
, m
->name
);
385 s
= mono_signature_full_name (sig
);
386 emit_string (prof
, s
);
390 mono_profiler_printf ("%s %d\n", mono_method_full_name (m
, 1), id
);
393 /* called at the end of the program */
395 prof_shutdown (MonoProfiler
*prof
)
400 gint32 version
= (AOT_PROFILER_MAJOR_VERSION
<< 16) | AOT_PROFILER_MINOR_VERSION
;
401 sprintf (magic
, AOT_PROFILER_MAGIC
);
402 fwrite (magic
, strlen (magic
), 1, prof
->outfile
);
403 emit_int32 (prof
, version
);
405 GHashTable
*all_methods
= g_hash_table_new (NULL
, NULL
);
406 for (mindex
= 0; mindex
< prof
->methods
->len
; ++mindex
) {
407 MonoMethod
*m
= (MonoMethod
*)g_ptr_array_index (prof
->methods
, mindex
);
409 if (!mono_method_get_token (m
))
412 if (g_hash_table_lookup (all_methods
, m
))
414 g_hash_table_insert (all_methods
, m
, m
);
416 add_method (prof
, m
);
418 emit_record (prof
, AOTPROF_RECORD_NONE
, 0);
420 fclose (prof
->outfile
);
422 g_hash_table_destroy (all_methods
);
423 g_hash_table_destroy (prof
->classes
);
424 g_hash_table_destroy (prof
->images
);
425 g_ptr_array_free (prof
->methods
, TRUE
);
426 g_free (prof
->outfile_name
);