[bcl] Enable tests for the monodroid profile.
[mono.git] / mono / profiler / 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 "mono-profiler-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 /* the entry point */
123 void
124 mono_profiler_startup (const char *desc)
125 {
126         MonoProfiler *prof;
127         const char *p;
128         const char *opt;
129         char *outfile_name;
130
131         p = desc;
132         if (strncmp (p, "aot", 3))
133                 usage (1);
134         p += 3;
135         if (*p == ':')
136                 p++;
137         for (; *p; p = opt) {
138                 char *val;
139                 if (*p == ',') {
140                         opt = p + 1;
141                         continue;
142                 }
143                 if ((opt = match_option (p, "help", NULL)) != p) {
144                         usage (0);
145                         continue;
146                 }
147                 if ((opt = match_option (p, "verbose", NULL)) != p) {
148                         verbose = TRUE;
149                         continue;
150                 }
151                 if ((opt = match_option (p, "output", &val)) != p) {
152                         outfile_name = val;
153                         continue;
154                 }
155                 fprintf (stderr, "mono-profiler-aot: Unknown option: '%s'.\n", p);
156                 exit (1);
157         }
158
159         if (!outfile_name) {
160                 fprintf (stderr, "mono-profiler-aot: The 'output' argument is required.\n");
161                 exit (1);
162         }
163
164         prof = g_new0 (MonoProfiler, 1);
165         prof->images = g_hash_table_new (NULL, NULL);
166         prof->classes = g_hash_table_new (NULL, NULL);
167         prof->methods = g_ptr_array_new ();
168         prof->outfile_name = outfile_name;
169
170         mono_os_mutex_init (&mutex);
171
172         mono_profiler_install (prof, prof_shutdown);
173
174         mono_profiler_install_jit_compile (prof_jit_enter, prof_jit_leave);
175
176         mono_profiler_set_events (MONO_PROFILE_JIT_COMPILATION);
177 }
178
179 static void
180 emit_byte (MonoProfiler *prof, guint8 value)
181 {
182         fwrite (&value, 1, 1, prof->outfile);
183 }
184
185 static void
186 emit_int32 (MonoProfiler *prof, int value)
187 {
188         // FIXME: Endianness
189         fwrite (&value, 4, 1, prof->outfile);
190 }
191
192 static void
193 emit_string (MonoProfiler *prof, const char *str)
194 {
195         int len = strlen (str);
196
197         emit_int32 (prof, len);
198         fwrite (str, len, 1, prof->outfile);
199 }
200
201 static void
202 emit_record (MonoProfiler *prof, AotProfRecordType type, int id)
203 {
204         emit_byte (prof, type);
205         emit_int32 (prof, id);
206 }
207
208 static int
209 add_image (MonoProfiler *prof, MonoImage *image)
210 {
211         int id = GPOINTER_TO_INT (g_hash_table_lookup (prof->images, image));
212         if (id)
213                 return id - 1;
214
215         id = prof->id ++;
216         emit_record (prof, AOTPROF_RECORD_IMAGE, id);
217         emit_string (prof, image->assembly->aname.name);
218         emit_string (prof, image->guid);
219         g_hash_table_insert (prof->images, image, GINT_TO_POINTER (id + 1));
220         return id;
221 }
222
223 static int
224 add_class (MonoProfiler *prof, MonoClass *klass);
225
226 static int
227 add_type (MonoProfiler *prof, MonoType *type)
228 {
229         switch (type->type) {
230 #if 0
231         case MONO_TYPE_SZARRAY: {
232                 int eid = add_type (prof, &type->data.klass->byval_arg);
233                 if (eid == -1)
234                         return -1;
235                 int id = prof->id ++;
236                 emit_record (prof, AOTPROF_RECORD_TYPE, id);
237                 emit_byte (prof, MONO_TYPE_SZARRAY);
238                 emit_int32 (prof, id);
239                 return id;
240         }
241 #endif
242         case MONO_TYPE_BOOLEAN:
243         case MONO_TYPE_CHAR:
244         case MONO_TYPE_I1:
245         case MONO_TYPE_U1:
246         case MONO_TYPE_I2:
247         case MONO_TYPE_U2:
248         case MONO_TYPE_I4:
249         case MONO_TYPE_U4:
250         case MONO_TYPE_I8:
251         case MONO_TYPE_U8:
252         case MONO_TYPE_R4:
253         case MONO_TYPE_R8:
254         case MONO_TYPE_I:
255         case MONO_TYPE_U:
256         case MONO_TYPE_OBJECT:
257         case MONO_TYPE_STRING:
258         case MONO_TYPE_CLASS:
259         case MONO_TYPE_VALUETYPE:
260         case MONO_TYPE_GENERICINST:
261                 return add_class (prof, mono_class_from_mono_type (type));
262         default:
263                 return -1;
264         }
265 }
266
267 static int
268 add_ginst (MonoProfiler *prof, MonoGenericInst *inst)
269 {
270         int i, id;
271         int *ids;
272
273         // FIXME: Cache
274         ids = g_malloc0 (inst->type_argc * sizeof (int));
275         for (i = 0; i < inst->type_argc; ++i) {
276                 MonoType *t = inst->type_argv [i];
277                 ids [i] = add_type (prof, t);
278                 if (ids [i] == -1) {
279                         g_free (ids);
280                         return -1;
281                 }
282         }
283         id = prof->id ++;
284         emit_record (prof, AOTPROF_RECORD_GINST, id);
285         emit_int32 (prof, inst->type_argc);
286         for (i = 0; i < inst->type_argc; ++i)
287                 emit_int32 (prof, ids [i]);
288         g_free (ids);
289
290         return id;
291 }
292
293 static int
294 add_class (MonoProfiler *prof, MonoClass *klass)
295 {
296         int id, inst_id = -1, image_id;
297         char *name;
298
299         id = GPOINTER_TO_INT (g_hash_table_lookup (prof->classes, klass));
300         if (id)
301                 return id - 1;
302
303         image_id = add_image (prof, klass->image);
304
305         if (mono_class_is_ginst (klass)) {
306                 MonoGenericContext *ctx = mono_class_get_context (klass);
307                 inst_id = add_ginst (prof, ctx->class_inst);
308                 if (inst_id == -1)
309                         return -1;
310         }
311
312         if (klass->nested_in)
313                 name = g_strdup_printf ("%s.%s/%s", klass->nested_in->name_space, klass->nested_in->name, klass->name);
314         else
315                 name = g_strdup_printf ("%s.%s", klass->name_space, klass->name);
316
317         id = prof->id ++;
318         emit_record (prof, AOTPROF_RECORD_TYPE, id);
319         emit_byte (prof, MONO_TYPE_CLASS);
320         emit_int32 (prof, image_id);
321         emit_int32 (prof, inst_id);
322         emit_string (prof, name);
323         g_free (name);
324         g_hash_table_insert (prof->classes, klass, GINT_TO_POINTER (id + 1));
325         return id;
326 }
327
328 static void
329 add_method (MonoProfiler *prof, MonoMethod *m)
330 {
331         MonoError error;
332         MonoMethodSignature *sig;
333         char *s;
334
335         sig = mono_method_signature_checked (m, &error);
336         g_assert (mono_error_ok (&error));
337
338         int class_id = add_class (prof, m->klass);
339         if (class_id == -1)
340                 return;
341         int inst_id = -1;
342
343         if (m->is_inflated) {
344                 MonoGenericContext *ctx = mono_method_get_context (m);
345                 if (ctx->method_inst)
346                         inst_id = add_ginst (prof, ctx->method_inst);
347         }
348         int id = prof->id ++;
349         emit_record (prof, AOTPROF_RECORD_METHOD, id);
350         emit_int32 (prof, class_id);
351         emit_int32 (prof, inst_id);
352         emit_int32 (prof, sig->param_count);
353         emit_string (prof, m->name);
354         s = mono_signature_full_name (sig);
355         emit_string (prof, s);
356         g_free (s);
357         if (verbose)
358                 printf ("%s %d\n", mono_method_full_name (m, 1), id);
359 }
360
361 /* called at the end of the program */
362 static void
363 prof_shutdown (MonoProfiler *prof)
364 {
365         FILE *outfile;
366         int mindex;
367         char magic [32];
368
369         printf ("Creating output file: %s\n", prof->outfile_name);
370
371         if (prof->outfile_name [0] == '#') {
372                 int fd = strtol (prof->outfile_name + 1, NULL, 10);
373                 outfile = fdopen (fd, "a");
374         } else {
375                 outfile = fopen (prof->outfile_name, "w+");
376         }
377         if (!outfile) {
378                 fprintf (stderr, "Unable to create output file '%s': %s.\n", prof->outfile_name, strerror (errno));
379                 return;
380         }
381         prof->outfile = outfile;
382
383         gint32 version = (AOT_PROFILER_MAJOR_VERSION << 16) | AOT_PROFILER_MINOR_VERSION;
384         sprintf (magic, AOT_PROFILER_MAGIC);
385         fwrite (magic, strlen (magic), 1, outfile);
386         emit_int32 (prof, version);
387
388         GHashTable *all_methods = g_hash_table_new (NULL, NULL);
389         for (mindex = 0; mindex < prof->methods->len; ++mindex) {
390             MonoMethod *m = (MonoMethod*)g_ptr_array_index (prof->methods, mindex);
391
392                 if (!mono_method_get_token (m))
393                         continue;
394
395                 if (g_hash_table_lookup (all_methods, m))
396                         continue;
397                 g_hash_table_insert (all_methods, m, m);
398
399                 add_method (prof, m);
400         }
401         emit_record (prof, AOTPROF_RECORD_NONE, 0);
402
403         fclose (outfile);
404
405         g_hash_table_destroy (all_methods);
406         g_hash_table_destroy (prof->classes);
407         g_hash_table_destroy (prof->images);
408         g_ptr_array_free (prof->methods, TRUE);
409         g_free (prof->outfile_name);
410 }