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