Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / profiler / aot.c
1 /*
2  * 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_leave (MonoProfiler *prof, MonoMethod *method, MonoJitInfo *jinfo)
50 {
51         MonoImage *image = mono_class_get_image (mono_method_get_class (method));
52
53         if (!image->assembly || method->wrapper_type)
54                 return;
55
56         mono_os_mutex_lock (&mutex);
57         g_ptr_array_add (prof->methods, method);
58         mono_os_mutex_unlock (&mutex);
59 }
60
61 static void
62 prof_shutdown (MonoProfiler *prof);
63
64 static void
65 usage (int do_exit)
66 {
67         printf ("AOT profiler.\n");
68         printf ("Usage: mono --profile=aot[:OPTION1[,OPTION2...]] program.exe\n");
69         printf ("Options:\n");
70         printf ("\thelp                 show this usage info\n");
71         printf ("\toutput=FILENAME      write the data to file FILENAME (required)\n");
72         printf ("\tverbose              print diagnostic info\n");
73         if (do_exit)
74                 exit (1);
75 }
76
77 static const char*
78 match_option (const char* p, const char *opt, char **rval)
79 {
80         int len = strlen (opt);
81         if (strncmp (p, opt, len) == 0) {
82                 if (rval) {
83                         if (p [len] == '=' && p [len + 1]) {
84                                 const char *opt = p + len + 1;
85                                 const char *end = strchr (opt, ',');
86                                 char *val;
87                                 int l;
88                                 if (end == NULL) {
89                                         l = strlen (opt);
90                                 } else {
91                                         l = end - opt;
92                                 }
93                                 val = (char *) g_malloc (l + 1);
94                                 memcpy (val, opt, l);
95                                 val [l] = 0;
96                                 *rval = val;
97                                 return opt + l;
98                         }
99                         if (p [len] == 0 || p [len] == ',') {
100                                 *rval = NULL;
101                                 return p + len + (p [len] == ',');
102                         }
103                         usage (1);
104                 } else {
105                         if (p [len] == 0)
106                                 return p + len;
107                         if (p [len] == ',')
108                                 return p + len + 1;
109                 }
110         }
111         return p;
112 }
113
114 void
115 mono_profiler_init_aot (const char *desc);
116
117 /**
118  * mono_profiler_init_aot:
119  * the entry point
120  */
121 void
122 mono_profiler_init_aot (const char *desc)
123 {
124         MonoProfiler *prof;
125         const char *p;
126         const char *opt;
127         char *outfile_name = NULL;
128
129         p = desc;
130         if (strncmp (p, "aot", 3))
131                 usage (1);
132         p += 3;
133         if (*p == ':')
134                 p++;
135         for (; *p; p = opt) {
136                 char *val;
137                 if (*p == ',') {
138                         opt = p + 1;
139                         continue;
140                 }
141                 if ((opt = match_option (p, "help", NULL)) != p) {
142                         usage (0);
143                         continue;
144                 }
145                 if ((opt = match_option (p, "verbose", NULL)) != p) {
146                         verbose = TRUE;
147                         continue;
148                 }
149                 if ((opt = match_option (p, "output", &val)) != p) {
150                         outfile_name = val;
151                         continue;
152                 }
153                 fprintf (stderr, "mono-profiler-aot: Unknown option: '%s'.\n", p);
154                 exit (1);
155         }
156
157         if (!outfile_name) {
158                 fprintf (stderr, "mono-profiler-aot: The 'output' argument is required.\n");
159                 exit (1);
160         }
161
162         prof = g_new0 (MonoProfiler, 1);
163         prof->images = g_hash_table_new (NULL, NULL);
164         prof->classes = g_hash_table_new (NULL, NULL);
165         prof->methods = g_ptr_array_new ();
166         prof->outfile_name = outfile_name;
167
168         mono_os_mutex_init (&mutex);
169
170         MonoProfilerHandle handle = mono_profiler_create (prof);
171         mono_profiler_set_runtime_shutdown_end_callback (handle, prof_shutdown);
172         mono_profiler_set_jit_done_callback (handle, prof_jit_leave);
173 }
174
175 static void
176 emit_byte (MonoProfiler *prof, guint8 value)
177 {
178         fwrite (&value, 1, 1, prof->outfile);
179 }
180
181 static void
182 emit_int32 (MonoProfiler *prof, int value)
183 {
184         // FIXME: Endianness
185         fwrite (&value, 4, 1, prof->outfile);
186 }
187
188 static void
189 emit_string (MonoProfiler *prof, const char *str)
190 {
191         int len = strlen (str);
192
193         emit_int32 (prof, len);
194         fwrite (str, len, 1, prof->outfile);
195 }
196
197 static void
198 emit_record (MonoProfiler *prof, AotProfRecordType type, int id)
199 {
200         emit_byte (prof, type);
201         emit_int32 (prof, id);
202 }
203
204 static int
205 add_image (MonoProfiler *prof, MonoImage *image)
206 {
207         int id = GPOINTER_TO_INT (g_hash_table_lookup (prof->images, image));
208         if (id)
209                 return id - 1;
210
211         id = prof->id ++;
212         emit_record (prof, AOTPROF_RECORD_IMAGE, id);
213         emit_string (prof, image->assembly->aname.name);
214         emit_string (prof, image->guid);
215         g_hash_table_insert (prof->images, image, GINT_TO_POINTER (id + 1));
216         return id;
217 }
218
219 static int
220 add_class (MonoProfiler *prof, MonoClass *klass);
221
222 static int
223 add_type (MonoProfiler *prof, MonoType *type)
224 {
225         switch (type->type) {
226 #if 0
227         case MONO_TYPE_SZARRAY: {
228                 int eid = add_type (prof, &type->data.klass->byval_arg);
229                 if (eid == -1)
230                         return -1;
231                 int id = prof->id ++;
232                 emit_record (prof, AOTPROF_RECORD_TYPE, id);
233                 emit_byte (prof, MONO_TYPE_SZARRAY);
234                 emit_int32 (prof, id);
235                 return id;
236         }
237 #endif
238         case MONO_TYPE_BOOLEAN:
239         case MONO_TYPE_CHAR:
240         case MONO_TYPE_I1:
241         case MONO_TYPE_U1:
242         case MONO_TYPE_I2:
243         case MONO_TYPE_U2:
244         case MONO_TYPE_I4:
245         case MONO_TYPE_U4:
246         case MONO_TYPE_I8:
247         case MONO_TYPE_U8:
248         case MONO_TYPE_R4:
249         case MONO_TYPE_R8:
250         case MONO_TYPE_I:
251         case MONO_TYPE_U:
252         case MONO_TYPE_OBJECT:
253         case MONO_TYPE_STRING:
254         case MONO_TYPE_CLASS:
255         case MONO_TYPE_VALUETYPE:
256         case MONO_TYPE_GENERICINST:
257                 return add_class (prof, mono_class_from_mono_type (type));
258         default:
259                 return -1;
260         }
261 }
262
263 static int
264 add_ginst (MonoProfiler *prof, MonoGenericInst *inst)
265 {
266         int i, id;
267         int *ids;
268
269         // FIXME: Cache
270         ids = g_malloc0 (inst->type_argc * sizeof (int));
271         for (i = 0; i < inst->type_argc; ++i) {
272                 MonoType *t = inst->type_argv [i];
273                 ids [i] = add_type (prof, t);
274                 if (ids [i] == -1) {
275                         g_free (ids);
276                         return -1;
277                 }
278         }
279         id = prof->id ++;
280         emit_record (prof, AOTPROF_RECORD_GINST, id);
281         emit_int32 (prof, inst->type_argc);
282         for (i = 0; i < inst->type_argc; ++i)
283                 emit_int32 (prof, ids [i]);
284         g_free (ids);
285
286         return id;
287 }
288
289 static int
290 add_class (MonoProfiler *prof, MonoClass *klass)
291 {
292         int id, inst_id = -1, image_id;
293         char *name;
294
295         id = GPOINTER_TO_INT (g_hash_table_lookup (prof->classes, klass));
296         if (id)
297                 return id - 1;
298
299         image_id = add_image (prof, klass->image);
300
301         if (mono_class_is_ginst (klass)) {
302                 MonoGenericContext *ctx = mono_class_get_context (klass);
303                 inst_id = add_ginst (prof, ctx->class_inst);
304                 if (inst_id == -1)
305                         return -1;
306         }
307
308         if (klass->nested_in)
309                 name = g_strdup_printf ("%s.%s/%s", klass->nested_in->name_space, klass->nested_in->name, klass->name);
310         else
311                 name = g_strdup_printf ("%s.%s", klass->name_space, klass->name);
312
313         id = prof->id ++;
314         emit_record (prof, AOTPROF_RECORD_TYPE, id);
315         emit_byte (prof, MONO_TYPE_CLASS);
316         emit_int32 (prof, image_id);
317         emit_int32 (prof, inst_id);
318         emit_string (prof, name);
319         g_free (name);
320         g_hash_table_insert (prof->classes, klass, GINT_TO_POINTER (id + 1));
321         return id;
322 }
323
324 static void
325 add_method (MonoProfiler *prof, MonoMethod *m)
326 {
327         MonoError error;
328         MonoMethodSignature *sig;
329         char *s;
330
331         sig = mono_method_signature_checked (m, &error);
332         g_assert (mono_error_ok (&error));
333
334         int class_id = add_class (prof, m->klass);
335         if (class_id == -1)
336                 return;
337         int inst_id = -1;
338
339         if (m->is_inflated) {
340                 MonoGenericContext *ctx = mono_method_get_context (m);
341                 if (ctx->method_inst)
342                         inst_id = add_ginst (prof, ctx->method_inst);
343         }
344         int id = prof->id ++;
345         emit_record (prof, AOTPROF_RECORD_METHOD, id);
346         emit_int32 (prof, class_id);
347         emit_int32 (prof, inst_id);
348         emit_int32 (prof, sig->param_count);
349         emit_string (prof, m->name);
350         s = mono_signature_full_name (sig);
351         emit_string (prof, s);
352         g_free (s);
353         if (verbose)
354                 printf ("%s %d\n", mono_method_full_name (m, 1), id);
355 }
356
357 /* called at the end of the program */
358 static void
359 prof_shutdown (MonoProfiler *prof)
360 {
361         FILE *outfile;
362         int mindex;
363         char magic [32];
364
365         printf ("Creating output file: %s\n", prof->outfile_name);
366
367         if (prof->outfile_name [0] == '#') {
368                 int fd = strtol (prof->outfile_name + 1, NULL, 10);
369                 outfile = fdopen (fd, "a");
370         } else {
371                 outfile = fopen (prof->outfile_name, "w+");
372         }
373         if (!outfile) {
374                 fprintf (stderr, "Unable to create output file '%s': %s.\n", prof->outfile_name, strerror (errno));
375                 return;
376         }
377         prof->outfile = outfile;
378
379         gint32 version = (AOT_PROFILER_MAJOR_VERSION << 16) | AOT_PROFILER_MINOR_VERSION;
380         sprintf (magic, AOT_PROFILER_MAGIC);
381         fwrite (magic, strlen (magic), 1, outfile);
382         emit_int32 (prof, version);
383
384         GHashTable *all_methods = g_hash_table_new (NULL, NULL);
385         for (mindex = 0; mindex < prof->methods->len; ++mindex) {
386             MonoMethod *m = (MonoMethod*)g_ptr_array_index (prof->methods, mindex);
387
388                 if (!mono_method_get_token (m))
389                         continue;
390
391                 if (g_hash_table_lookup (all_methods, m))
392                         continue;
393                 g_hash_table_insert (all_methods, m, m);
394
395                 add_method (prof, m);
396         }
397         emit_record (prof, AOTPROF_RECORD_NONE, 0);
398
399         fclose (outfile);
400
401         g_hash_table_destroy (all_methods);
402         g_hash_table_destroy (prof->classes);
403         g_hash_table_destroy (prof->images);
404         g_ptr_array_free (prof->methods, TRUE);
405         g_free (prof->outfile_name);
406 }