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