strftime(%F) and strftime(%T) are non-portable shorthands that produce
[mono-project.git] / mono / profiler / aot.c
blob4685277144d9fcb807dab4d3f08b19871f0a6e30
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/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>
22 #include <string.h>
23 #include <errno.h>
24 #include <stdlib.h>
25 #include <glib.h>
26 #include <mono/utils/mono-error-internals.h>
28 struct _MonoProfiler {
29 GHashTable *classes;
30 GHashTable *images;
31 GPtrArray *methods;
32 FILE *outfile;
33 int id;
34 char *outfile_name;
35 mono_mutex_t mutex;
36 gboolean verbose;
39 static MonoProfiler aot_profiler;
41 static void
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)
47 return;
49 mono_os_mutex_lock (&prof->mutex);
50 g_ptr_array_add (prof->methods, method);
51 mono_os_mutex_unlock (&prof->mutex);
54 static void
55 prof_shutdown (MonoProfiler *prof);
57 static void
58 usage (void)
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");
67 exit (0);
70 static gboolean
71 match_option (const char *arg, const char *opt_name, const char **rval)
73 if (rval) {
74 const char *end = strchr (arg, '=');
76 *rval = NULL;
77 if (!end)
78 return !strcmp (arg, opt_name);
80 if (strncmp (arg, opt_name, strlen (opt_name)) || (end - arg) > strlen (opt_name) + 1)
81 return FALSE;
82 *rval = end + 1;
83 return TRUE;
84 } else {
85 //FIXME how should we handle passing a value to an arg that doesn't expect it?
86 return !strcmp (arg, opt_name);
90 static void
91 parse_arg (const char *arg)
93 const char *val;
95 if (match_option (arg, "help", NULL)) {
96 usage ();
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;
101 } else {
102 mono_profiler_printf_err ("Could not parse argument: %s", arg);
106 static void
107 parse_args (const char *desc)
109 const char *p;
110 gboolean in_quotes = FALSE;
111 char quote_char = '\0';
112 char *buffer = g_malloc (strlen (desc) + 1);
113 int buffer_pos = 0;
115 for (p = desc; *p; p++){
116 switch (*p){
117 case ',':
118 if (!in_quotes) {
119 if (buffer_pos != 0){
120 buffer [buffer_pos] = 0;
121 parse_arg (buffer);
122 buffer_pos = 0;
124 } else {
125 buffer [buffer_pos++] = *p;
127 break;
129 case '\\':
130 if (p [1]) {
131 buffer [buffer_pos++] = p[1];
132 p++;
134 break;
135 case '\'':
136 case '"':
137 if (in_quotes) {
138 if (quote_char == *p)
139 in_quotes = FALSE;
140 else
141 buffer [buffer_pos++] = *p;
142 } else {
143 in_quotes = TRUE;
144 quote_char = *p;
146 break;
147 default:
148 buffer [buffer_pos++] = *p;
149 break;
153 if (buffer_pos != 0) {
154 buffer [buffer_pos] = 0;
155 parse_arg (buffer);
158 g_free (buffer);
161 void
162 mono_profiler_init_aot (const char *desc);
165 * mono_profiler_init_aot:
166 * the entry point
168 void
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.");
173 exit (1);
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");
187 else
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));
192 exit (1);
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);
206 static void
207 emit_byte (MonoProfiler *prof, guint8 value)
209 fwrite (&value, sizeof (guint8), 1, prof->outfile);
212 static void
213 emit_int32 (MonoProfiler *prof, gint32 value)
215 for (int i = 0; i < sizeof (gint32); ++i) {
216 guint8 b = value;
217 fwrite (&b, sizeof (guint8), 1, prof->outfile);
218 value >>= 8;
222 static void
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);
231 static void
232 emit_record (MonoProfiler *prof, AotProfRecordType type, int id)
234 emit_byte (prof, type);
235 emit_int32 (prof, id);
238 static int
239 add_image (MonoProfiler *prof, MonoImage *image)
241 int id = GPOINTER_TO_INT (g_hash_table_lookup (prof->images, image));
242 if (id)
243 return id - 1;
245 id = prof->id ++;
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));
250 return id;
253 static int
254 add_class (MonoProfiler *prof, MonoClass *klass);
256 static int
257 add_type (MonoProfiler *prof, MonoType *type)
259 switch (type->type) {
260 #if 0
261 case MONO_TYPE_SZARRAY: {
262 int eid = add_type (prof, m_class_get_byval_arg (type->data.klass));
263 if (eid == -1)
264 return -1;
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);
269 return id;
271 #endif
272 case MONO_TYPE_BOOLEAN:
273 case MONO_TYPE_CHAR:
274 case MONO_TYPE_I1:
275 case MONO_TYPE_U1:
276 case MONO_TYPE_I2:
277 case MONO_TYPE_U2:
278 case MONO_TYPE_I4:
279 case MONO_TYPE_U4:
280 case MONO_TYPE_I8:
281 case MONO_TYPE_U8:
282 case MONO_TYPE_R4:
283 case MONO_TYPE_R8:
284 case MONO_TYPE_I:
285 case MONO_TYPE_U:
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));
292 default:
293 return -1;
297 static int
298 add_ginst (MonoProfiler *prof, MonoGenericInst *inst)
300 int i, id;
301 int *ids;
303 // FIXME: Cache
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);
308 if (ids [i] == -1) {
309 g_free (ids);
310 return -1;
313 id = prof->id ++;
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]);
318 g_free (ids);
320 return id;
323 static int
324 add_class (MonoProfiler *prof, MonoClass *klass)
326 int id, inst_id = -1, image_id;
327 char *name;
329 id = GPOINTER_TO_INT (g_hash_table_lookup (prof->classes, klass));
330 if (id)
331 return id - 1;
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);
338 if (inst_id == -1)
339 return -1;
342 MonoClass *klass_nested_in = mono_class_get_nesting_type (klass);
343 if (klass_nested_in)
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));
345 else
346 name = g_strdup_printf ("%s.%s", m_class_get_name_space (klass), m_class_get_name (klass));
348 id = prof->id ++;
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);
354 g_free (name);
355 g_hash_table_insert (prof->classes, klass, GINT_TO_POINTER (id + 1));
356 return id;
359 static void
360 add_method (MonoProfiler *prof, MonoMethod *m)
362 ERROR_DECL (error);
363 MonoMethodSignature *sig;
364 char *s;
366 sig = mono_method_signature_checked (m, error);
367 g_assert (mono_error_ok (error));
369 int class_id = add_class (prof, m->klass);
370 if (class_id == -1)
371 return;
372 int inst_id = -1;
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);
387 g_free (s);
389 if (prof->verbose)
390 mono_profiler_printf ("%s %d\n", mono_method_full_name (m, 1), id);
393 /* called at the end of the program */
394 static void
395 prof_shutdown (MonoProfiler *prof)
397 int mindex;
398 char magic [32];
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))
410 continue;
412 if (g_hash_table_lookup (all_methods, m))
413 continue;
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);