b5c2d47fb5149cc5b842b4c443fd85f47e74d115
[mono.git] / mono / profiler / aot.c
1 /*
2  * mono-profiler-aot.c: Ahead of Time Compiler Profiler for Mono.
3  *
4  *
5  * Copyright 2008-2009 Novell, Inc (http://www.novell.com)
6  *
7  * This profiler collects profiling information usable by the Mono AOT compiler
8  * to generate better code. It saves the information into files under ~/.mono. 
9  * The AOT compiler can load these files during compilation.
10  * Currently, only the order in which methods were compiled is saved, 
11  * allowing more efficient function ordering in the AOT files.
12  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
13  */
14
15 #include <config.h>
16
17 #include "aot.h"
18
19 #include <mono/metadata/profiler.h>
20 #include <mono/metadata/tokentype.h>
21 #include <mono/metadata/tabledefs.h>
22 #include <mono/metadata/debug-helpers.h>
23 #include <mono/metadata/assembly.h>
24 #include <mono/metadata/class-internals.h>
25 #include <mono/utils/mono-os-mutex.h>
26 #include <string.h>
27 #include <errno.h>
28 #include <stdlib.h>
29 #include <glib.h>
30 #include <sys/stat.h>
31
32 #ifdef HOST_WIN32
33 #include <direct.h>
34 #endif
35
36 struct _MonoProfiler {
37         GHashTable *classes;
38         GHashTable *images;
39         GPtrArray *methods;
40         FILE *outfile;
41         int id;
42         char *outfile_name;
43 };
44
45 static mono_mutex_t mutex;
46 static gboolean verbose;
47
48 static void
49 prof_jit_enter (MonoProfiler *prof, MonoMethod *method)
50 {
51 }
52
53 static void
54 prof_jit_leave (MonoProfiler *prof, MonoMethod *method, int result)
55 {
56         MonoImage *image = mono_class_get_image (mono_method_get_class (method));
57
58         if (!image->assembly || method->wrapper_type)
59                 return;
60
61         mono_os_mutex_lock (&mutex);
62         g_ptr_array_add (prof->methods, method);
63         mono_os_mutex_unlock (&mutex);
64 }
65
66 static void
67 prof_shutdown (MonoProfiler *prof);
68
69 static void
70 usage (int do_exit)
71 {
72         printf ("AOT profiler.\n");
73         printf ("Usage: mono --profile=aot[:OPTION1[,OPTION2...]] program.exe\n");
74         printf ("Options:\n");
75         printf ("\thelp                 show this usage info\n");
76         printf ("\toutput=FILENAME      write the data to file FILENAME (required)\n");
77         printf ("\tverbose              print diagnostic info\n");
78         if (do_exit)
79                 exit (1);
80 }
81
82 static const char*
83 match_option (const char* p, const char *opt, char **rval)
84 {
85         int len = strlen (opt);
86         if (strncmp (p, opt, len) == 0) {
87                 if (rval) {
88                         if (p [len] == '=' && p [len + 1]) {
89                                 const char *opt = p + len + 1;
90                                 const char *end = strchr (opt, ',');
91                                 char *val;
92                                 int l;
93                                 if (end == NULL) {
94                                         l = strlen (opt);
95                                 } else {
96                                         l = end - opt;
97                                 }
98                                 val = (char *) g_malloc (l + 1);
99                                 memcpy (val, opt, l);
100                                 val [l] = 0;
101                                 *rval = val;
102                                 return opt + l;
103                         }
104                         if (p [len] == 0 || p [len] == ',') {
105                                 *rval = NULL;
106                                 return p + len + (p [len] == ',');
107                         }
108                         usage (1);
109                 } else {
110                         if (p [len] == 0)
111                                 return p + len;
112                         if (p [len] == ',')
113                                 return p + len + 1;
114                 }
115         }
116         return p;
117 }
118
119 void
120 mono_profiler_startup (const char *desc);
121
122 /**
123  * mono_profiler_startup:
124  * the entry point
125  */
126 void
127 mono_profiler_startup (const char *desc)
128 {
129         MonoProfiler *prof;
130         const char *p;
131         const char *opt;
132         char *outfile_name;
133
134         p = desc;
135         if (strncmp (p, "aot", 3))
136                 usage (1);
137         p += 3;
138         if (*p == ':')
139                 p++;
140         for (; *p; p = opt) {
141                 char *val;
142                 if (*p == ',') {
143                         opt = p + 1;
144                         continue;
145                 }
146                 if ((opt = match_option (p, "help", NULL)) != p) {
147                         usage (0);
148                         continue;
149                 }
150                 if ((opt = match_option (p, "verbose", NULL)) != p) {
151                         verbose = TRUE;
152                         continue;
153                 }
154                 if ((opt = match_option (p, "output", &val)) != p) {
155                         outfile_name = val;
156                         continue;
157                 }
158                 fprintf (stderr, "mono-profiler-aot: Unknown option: '%s'.\n", p);
159                 exit (1);
160         }
161
162         if (!outfile_name) {
163                 fprintf (stderr, "mono-profiler-aot: The 'output' argument is required.\n");
164                 exit (1);
165         }
166
167         prof = g_new0 (MonoProfiler, 1);
168         prof->images = g_hash_table_new (NULL, NULL);
169         prof->classes = g_hash_table_new (NULL, NULL);
170         prof->methods = g_ptr_array_new ();
171         prof->outfile_name = outfile_name;
172
173         mono_os_mutex_init (&mutex);
174
175         mono_profiler_install (prof, prof_shutdown);
176
177         mono_profiler_install_jit_compile (prof_jit_enter, prof_jit_leave);
178
179         mono_profiler_set_events (MONO_PROFILE_JIT_COMPILATION);
180 }
181
182 static void
183 emit_byte (MonoProfiler *prof, guint8 value)
184 {
185         fwrite (&value, 1, 1, prof->outfile);
186 }
187
188 static void
189 emit_int32 (MonoProfiler *prof, int value)
190 {
191         // FIXME: Endianness
192         fwrite (&value, 4, 1, prof->outfile);
193 }
194
195 static void
196 emit_string (MonoProfiler *prof, const char *str)
197 {
198         int len = strlen (str);
199
200         emit_int32 (prof, len);
201         fwrite (str, len, 1, prof->outfile);
202 }
203
204 static void
205 emit_record (MonoProfiler *prof, AotProfRecordType type, int id)
206 {
207         emit_byte (prof, type);
208         emit_int32 (prof, id);
209 }
210
211 static int
212 add_image (MonoProfiler *prof, MonoImage *image)
213 {
214         int id = GPOINTER_TO_INT (g_hash_table_lookup (prof->images, image));
215         if (id)
216                 return id - 1;
217
218         id = prof->id ++;
219         emit_record (prof, AOTPROF_RECORD_IMAGE, id);
220         emit_string (prof, image->assembly->aname.name);
221         emit_string (prof, image->guid);
222         g_hash_table_insert (prof->images, image, GINT_TO_POINTER (id + 1));
223         return id;
224 }
225
226 static int
227 add_class (MonoProfiler *prof, MonoClass *klass);
228
229 static int
230 add_type (MonoProfiler *prof, MonoType *type)
231 {
232         switch (type->type) {
233 #if 0
234         case MONO_TYPE_SZARRAY: {
235                 int eid = add_type (prof, &type->data.klass->byval_arg);
236                 if (eid == -1)
237                         return -1;
238                 int id = prof->id ++;
239                 emit_record (prof, AOTPROF_RECORD_TYPE, id);
240                 emit_byte (prof, MONO_TYPE_SZARRAY);
241                 emit_int32 (prof, id);
242                 return id;
243         }
244 #endif
245         case MONO_TYPE_BOOLEAN:
246         case MONO_TYPE_CHAR:
247         case MONO_TYPE_I1:
248         case MONO_TYPE_U1:
249         case MONO_TYPE_I2:
250         case MONO_TYPE_U2:
251         case MONO_TYPE_I4:
252         case MONO_TYPE_U4:
253         case MONO_TYPE_I8:
254         case MONO_TYPE_U8:
255         case MONO_TYPE_R4:
256         case MONO_TYPE_R8:
257         case MONO_TYPE_I:
258         case MONO_TYPE_U:
259         case MONO_TYPE_OBJECT:
260         case MONO_TYPE_STRING:
261         case MONO_TYPE_CLASS:
262         case MONO_TYPE_VALUETYPE:
263         case MONO_TYPE_GENERICINST:
264                 return add_class (prof, mono_class_from_mono_type (type));
265         default:
266                 return -1;
267         }
268 }
269
270 static int
271 add_ginst (MonoProfiler *prof, MonoGenericInst *inst)
272 {
273         int i, id;
274         int *ids;
275
276         // FIXME: Cache
277         ids = g_malloc0 (inst->type_argc * sizeof (int));
278         for (i = 0; i < inst->type_argc; ++i) {
279                 MonoType *t = inst->type_argv [i];
280                 ids [i] = add_type (prof, t);
281                 if (ids [i] == -1) {
282                         g_free (ids);
283                         return -1;
284                 }
285         }
286         id = prof->id ++;
287         emit_record (prof, AOTPROF_RECORD_GINST, id);
288         emit_int32 (prof, inst->type_argc);
289         for (i = 0; i < inst->type_argc; ++i)
290                 emit_int32 (prof, ids [i]);
291         g_free (ids);
292
293         return id;
294 }
295
296 static int
297 add_class (MonoProfiler *prof, MonoClass *klass)
298 {
299         int id, inst_id = -1, image_id;
300         char *name;
301
302         id = GPOINTER_TO_INT (g_hash_table_lookup (prof->classes, klass));
303         if (id)
304                 return id - 1;
305
306         image_id = add_image (prof, klass->image);
307
308         if (mono_class_is_ginst (klass)) {
309                 MonoGenericContext *ctx = mono_class_get_context (klass);
310                 inst_id = add_ginst (prof, ctx->class_inst);
311                 if (inst_id == -1)
312                         return -1;
313         }
314
315         if (klass->nested_in)
316                 name = g_strdup_printf ("%s.%s/%s", klass->nested_in->name_space, klass->nested_in->name, klass->name);
317         else
318                 name = g_strdup_printf ("%s.%s", klass->name_space, klass->name);
319
320         id = prof->id ++;
321         emit_record (prof, AOTPROF_RECORD_TYPE, id);
322         emit_byte (prof, MONO_TYPE_CLASS);
323         emit_int32 (prof, image_id);
324         emit_int32 (prof, inst_id);
325         emit_string (prof, name);
326         g_free (name);
327         g_hash_table_insert (prof->classes, klass, GINT_TO_POINTER (id + 1));
328         return id;
329 }
330
331 static void
332 add_method (MonoProfiler *prof, MonoMethod *m)
333 {
334         MonoError error;
335         MonoMethodSignature *sig;
336         char *s;
337
338         sig = mono_method_signature_checked (m, &error);
339         g_assert (mono_error_ok (&error));
340
341         int class_id = add_class (prof, m->klass);
342         if (class_id == -1)
343                 return;
344         int inst_id = -1;
345
346         if (m->is_inflated) {
347                 MonoGenericContext *ctx = mono_method_get_context (m);
348                 if (ctx->method_inst)
349                         inst_id = add_ginst (prof, ctx->method_inst);
350         }
351         int id = prof->id ++;
352         emit_record (prof, AOTPROF_RECORD_METHOD, id);
353         emit_int32 (prof, class_id);
354         emit_int32 (prof, inst_id);
355         emit_int32 (prof, sig->param_count);
356         emit_string (prof, m->name);
357         s = mono_signature_full_name (sig);
358         emit_string (prof, s);
359         g_free (s);
360         if (verbose)
361                 printf ("%s %d\n", mono_method_full_name (m, 1), id);
362 }
363
364 /* called at the end of the program */
365 static void
366 prof_shutdown (MonoProfiler *prof)
367 {
368         FILE *outfile;
369         int mindex;
370         char magic [32];
371
372         printf ("Creating output file: %s\n", prof->outfile_name);
373
374         if (prof->outfile_name [0] == '#') {
375                 int fd = strtol (prof->outfile_name + 1, NULL, 10);
376                 outfile = fdopen (fd, "a");
377         } else {
378                 outfile = fopen (prof->outfile_name, "w+");
379         }
380         if (!outfile) {
381                 fprintf (stderr, "Unable to create output file '%s': %s.\n", prof->outfile_name, strerror (errno));
382                 return;
383         }
384         prof->outfile = outfile;
385
386         gint32 version = (AOT_PROFILER_MAJOR_VERSION << 16) | AOT_PROFILER_MINOR_VERSION;
387         sprintf (magic, AOT_PROFILER_MAGIC);
388         fwrite (magic, strlen (magic), 1, outfile);
389         emit_int32 (prof, version);
390
391         GHashTable *all_methods = g_hash_table_new (NULL, NULL);
392         for (mindex = 0; mindex < prof->methods->len; ++mindex) {
393             MonoMethod *m = (MonoMethod*)g_ptr_array_index (prof->methods, mindex);
394
395                 if (!mono_method_get_token (m))
396                         continue;
397
398                 if (g_hash_table_lookup (all_methods, m))
399                         continue;
400                 g_hash_table_insert (all_methods, m, m);
401
402                 add_method (prof, m);
403         }
404         emit_record (prof, AOTPROF_RECORD_NONE, 0);
405
406         fclose (outfile);
407
408         g_hash_table_destroy (all_methods);
409         g_hash_table_destroy (prof->classes);
410         g_hash_table_destroy (prof->images);
411         g_ptr_array_free (prof->methods, TRUE);
412         g_free (prof->outfile_name);
413 }