Finish spliting sgen-nursery-allocator into a separate compilation unit
[mono-project.git] / mono / profiler / mono-profiler-iomap.c
bloba7b9969c9404244549569475b6210c3b99a52180
1 /*
2 * mono-profiler-iomap.c: IOMAP string profiler for Mono.
4 * Authors:
5 * Marek Habersack <mhabersack@novell.com>
7 * Copyright (c) 2009 Novell, Inc (http://novell.com)
9 * Note: this profiler is completely unsafe wrt handling managed objects,
10 * don't use and don't copy code from here.
12 #include "config.h"
14 #include <string.h>
15 #include <mono/utils/mono-io-portability.h>
16 #include <mono/metadata/metadata.h>
17 #include <mono/metadata/metadata-internals.h>
18 #include <mono/metadata/class.h>
19 #include <mono/metadata/class-internals.h>
20 #include <mono/metadata/image.h>
21 #include <mono/metadata/mono-debug.h>
22 #include <mono/metadata/debug-helpers.h>
23 #include <mono/metadata/threads.h>
24 #include <mono/metadata/profiler.h>
25 #include <mono/metadata/loader.h>
26 #include <mono/io-layer/mono-mutex.h>
28 #define LOCATION_INDENT " "
29 #define BACKTRACE_SIZE 64
31 typedef struct _MonoStackBacktraceInfo
33 MonoMethod *method;
34 gint native_offset;
35 } MonoStackBacktraceInfo;
37 typedef struct
39 guint32 count;
40 gchar *requestedName;
41 gchar *actualName;
42 } MismatchedFilesStats;
44 typedef struct _SavedString
46 MonoString *string;
47 MonoDomain *domain;
48 void *stack [BACKTRACE_SIZE];
49 gint stack_entries;
50 struct _SavedString *next;
51 } SavedString;
53 typedef struct _SavedStringFindInfo
55 guint32 hash;
56 size_t len;
57 } SavedStringFindInfo;
59 typedef struct _StringLocation
61 gchar *hint;
62 struct _StringLocation *next;
63 } StringLocation;
65 struct _MonoProfiler
67 GHashTable *mismatched_files_hash;
68 GHashTable *saved_strings_hash;
69 GHashTable *string_locations_hash;
70 gboolean may_have_locations;
73 typedef struct _StackWalkData
75 MonoProfiler *prof;
76 void **stack;
77 int stack_size;
78 int frame_count;
79 } StackWalkData;
81 static mono_mutex_t mismatched_files_section;
82 static gboolean runtime_initialized = FALSE;
84 static inline void append_report (GString **report, const gchar *format, ...);
85 static inline void print_report (const gchar *format, ...);
86 static inline guint32 do_calc_string_hash (guint32 hash, const gchar *str);
87 static inline guint32 calc_strings_hash (const gchar *str1, const gchar *str2, guint32 *str1hash);
88 static void print_mismatched_stats (MonoProfiler *prof);
89 static inline gchar *build_hint (SavedString *head);
90 static inline gchar *build_hint_from_stack (MonoDomain *domain, void **stack, gint stack_entries);
91 static inline void store_string_location (MonoProfiler *prof, const gchar *string, guint32 hash, size_t len);
92 static void mono_portability_remember_string (MonoProfiler *prof, MonoDomain *domain, MonoString *str);
93 void mono_profiler_startup (const char *desc);
95 static void mismatched_stats_foreach_func (gpointer key, gpointer value, gpointer user_data)
97 MismatchedFilesStats *stats = (MismatchedFilesStats*)value;
98 StringLocation *location;
99 MonoProfiler *prof = (MonoProfiler*)user_data;
100 guint32 hash;
101 gboolean bannerShown = FALSE;
103 hash = do_calc_string_hash (0, stats->requestedName);
104 fprintf (stdout,
105 " Count: %u\n"
106 "Requested: %s\n"
107 " Actual: %s\n",
108 stats->count, stats->requestedName, stats->actualName);
110 if (!prof->may_have_locations) {
111 fprintf (stdout, "\n");
112 return;
115 location = g_hash_table_lookup (prof->string_locations_hash, &hash);
116 while (location) {
117 if (location->hint && strlen (location->hint) > 0) {
118 if (!bannerShown) {
119 fprintf (stdout, "Locations:\n");
120 bannerShown = TRUE;
122 fprintf (stdout, "%s", location->hint);
124 location = location->next;
125 if (location)
126 fprintf (stdout, LOCATION_INDENT "--\n");
129 fprintf (stdout, "\n");
132 static void print_mismatched_stats (MonoProfiler *prof)
134 if (!prof->mismatched_files_hash || g_hash_table_size (prof->mismatched_files_hash) == 0)
135 return;
137 prof->may_have_locations = g_hash_table_size (prof->string_locations_hash) > 0;
139 fprintf (stdout, "\n-=-=-=-=-=-=-= MONO_IOMAP Stats -=-=-=-=-=-=-=\n");
140 g_hash_table_foreach (prof->mismatched_files_hash, mismatched_stats_foreach_func, (gpointer)prof);
141 fflush (stdout);
144 static guint mismatched_files_guint32_hash (gconstpointer key)
146 if (!key)
147 return 0;
149 return *((guint32*)key);
152 static gboolean mismatched_files_guint32_equal (gconstpointer key1, gconstpointer key2)
154 if (!key1 || !key2)
155 return FALSE;
157 return (gboolean)(*((guint32*)key1) == *((guint32*)key2));
160 static inline guint32 do_calc_string_hash (guint32 hash, const gchar *str)
162 guint32 ret = hash;
163 gchar *cc = (gchar*)str;
164 gchar *end = (gchar*)(str + strlen (str) - 1);
166 for (; cc < end; cc += 2) {
167 ret = (ret << 5) - ret + *cc;
168 ret = (ret << 5) - ret + cc [1];
170 end++;
171 if (cc < end)
172 ret = (ret << 5) - ret + *cc;
174 return ret;
177 static inline guint32 calc_strings_hash (const gchar *str1, const gchar *str2, guint32 *str1hash)
179 guint32 hash = do_calc_string_hash (0, str1);
180 if (str1hash)
181 *str1hash = hash;
182 return do_calc_string_hash (hash, str2);
185 static inline void print_report (const gchar *format, ...)
187 MonoClass *klass;
188 MonoProperty *prop;
189 MonoString *str;
190 char *stack_trace;
191 va_list ap;
193 fprintf (stdout, "-=-=-=-=-=-=- MONO_IOMAP REPORT -=-=-=-=-=-=-\n");
194 va_start (ap, format);
195 vfprintf (stdout, format, ap);
196 fprintf (stdout, "\n");
197 va_end (ap);
198 klass = mono_class_from_name (mono_get_corlib (), "System", "Environment");
199 mono_class_init (klass);
200 prop = mono_class_get_property_from_name (klass, "StackTrace");
201 str = (MonoString*)mono_property_get_value (prop, NULL, NULL, NULL);
202 stack_trace = mono_string_to_utf8 (str);
204 fprintf (stdout, "-= Stack Trace =-\n%s\n\n", stack_trace);
205 g_free (stack_trace);
206 fflush (stdout);
209 static inline void append_report (GString **report, const gchar *format, ...)
211 #if defined (_EGLIB_MAJOR) || GLIB_CHECK_VERSION(2,14,0)
212 va_list ap;
213 if (!*report)
214 *report = g_string_new ("");
216 va_start (ap, format);
217 g_string_append_vprintf (*report, format, ap);
218 va_end (ap);
219 #else
220 g_assert_not_reached ();
221 #endif
224 static gboolean saved_strings_find_func (gpointer key, gpointer value, gpointer user_data)
226 SavedStringFindInfo *info = (SavedStringFindInfo*)user_data;
227 SavedString *saved = (SavedString*)value;
228 gchar *utf_str;
229 guint32 hash;
231 if (!info || !saved || mono_string_length (saved->string) != info->len)
232 return FALSE;
234 utf_str = mono_string_to_utf8 (saved->string);
235 hash = do_calc_string_hash (0, utf_str);
236 g_free (utf_str);
238 if (hash != info->hash)
239 return FALSE;
241 return TRUE;
244 static inline void store_string_location (MonoProfiler *prof, const gchar *string, guint32 hash, size_t len)
246 StringLocation *location = g_hash_table_lookup (prof->string_locations_hash, &hash);
247 SavedString *saved;
248 SavedStringFindInfo info;
249 guint32 *hashptr;
251 if (location)
252 return;
254 info.hash = hash;
255 info.len = len;
257 /* Expensive but unavoidable... */
258 saved = (SavedString*)g_hash_table_find (prof->saved_strings_hash, saved_strings_find_func, &info);
259 hashptr = (guint32*)g_malloc (sizeof (guint32));
260 *hashptr = hash;
261 location = (StringLocation*)g_malloc0 (sizeof (location));
263 g_hash_table_insert (prof->string_locations_hash, hashptr, location);
264 if (!saved)
265 return;
267 g_hash_table_remove (prof->saved_strings_hash, saved->string);
268 location->hint = build_hint (saved);
271 static gboolean ignore_frame (MonoMethod *method)
273 MonoClass *klass = method->klass;
275 if (method->wrapper_type != MONO_WRAPPER_NONE)
276 return TRUE;
278 /* Now ignore the assemblies we know shouldn't contain mixed-case names (only the most frequent cases) */
279 if (klass->image ) {
280 if (strcmp (klass->image->assembly_name, "mscorlib") == 0)
281 return TRUE;
282 else if (strcmp (klass->image->assembly_name, "System") == 0)
283 return TRUE;
284 else if (strncmp (klass->image->assembly_name, "Mono.", 5) == 0)
285 return TRUE;
286 else if (strncmp (klass->image->assembly_name, "System.", 7) == 0)
287 return TRUE;
288 else if (strcmp (klass->image->assembly_name, "PEAPI") == 0)
289 return TRUE;
292 return FALSE;
295 static inline gchar *build_hint_from_stack (MonoDomain *domain, void **stack, gint stack_entries)
297 gchar *hint;
298 MonoMethod *method, *selectedMethod;
299 MonoAssembly *assembly;
300 MonoImage *image;
301 MonoDebugSourceLocation *location;
302 MonoStackBacktraceInfo *info;
303 gboolean use_full_trace;
304 char *methodName;
305 gint i, native_offset, firstAvailable;
307 selectedMethod = NULL;
308 firstAvailable = -1;
309 use_full_trace = FALSE;
310 native_offset = -1;
311 for (i = 0; i < stack_entries; i++) {
312 info = (MonoStackBacktraceInfo*) stack [i];
313 method = info ? info->method : NULL;
315 if (!method || method->wrapper_type != MONO_WRAPPER_NONE)
316 continue;
318 if (firstAvailable == -1)
319 firstAvailable = i;
321 image = method->klass->image;
322 assembly = image->assembly;
324 if ((assembly && assembly->in_gac) || ignore_frame (method))
325 continue;
326 selectedMethod = method;
327 native_offset = info->native_offset;
328 break;
331 if (!selectedMethod) {
332 /* All the frames were from assemblies installed in GAC. Find first frame that is
333 * not in the ignore list */
334 for (i = 0; i < stack_entries; i++) {
335 info = (MonoStackBacktraceInfo*) stack [i];
336 method = info ? info->method : NULL;
338 if (!method || ignore_frame (method))
339 continue;
340 selectedMethod = method;
341 native_offset = info->native_offset;
342 break;
345 if (!selectedMethod)
346 use_full_trace = TRUE;
349 hint = NULL;
350 if (use_full_trace) {
351 GString *trace = g_string_new ("Full trace:\n");
352 for (i = firstAvailable; i < stack_entries; i++) {
353 info = (MonoStackBacktraceInfo*) stack [i];
354 method = info ? info->method : NULL;
355 if (!method || method->wrapper_type != MONO_WRAPPER_NONE)
356 continue;
358 location = mono_debug_lookup_source_location (method, info->native_offset, domain);
359 methodName = mono_method_full_name (method, TRUE);
361 if (location) {
362 append_report (&trace, LOCATION_INDENT "%s in %s:%u\n", methodName, location->source_file, location->row);
363 mono_debug_free_source_location (location);
364 } else
365 append_report (&trace, LOCATION_INDENT "%s\n", methodName);
366 g_free (methodName);
369 if (trace) {
370 if (trace->len)
371 hint = g_string_free (trace, FALSE);
372 else
373 g_string_free (trace, TRUE);
375 } else {
376 location = mono_debug_lookup_source_location (selectedMethod, native_offset, domain);
377 methodName = mono_method_full_name (selectedMethod, TRUE);
379 if (location) {
380 hint = g_strdup_printf (LOCATION_INDENT "%s in %s:%u\n", methodName, location->source_file, location->row);
381 mono_debug_free_source_location (location);
382 } else
383 hint = g_strdup_printf (LOCATION_INDENT "%s\n", methodName);
384 g_free (methodName);
387 return hint;
390 static inline gchar *build_hint (SavedString *head)
392 SavedString *current;
393 gchar *tmp;
394 GString *hint = NULL;
396 current = head;
397 while (current) {
398 tmp = build_hint_from_stack (current->domain, current->stack, current->stack_entries);
399 current = current->next;
400 if (!tmp)
401 continue;
403 append_report (&hint, tmp);
406 if (hint) {
407 if (hint->len)
408 return g_string_free (hint, FALSE);
409 else {
410 g_string_free (hint, FALSE);
411 return NULL;
415 return NULL;
418 static gboolean stack_walk_func (MonoMethod *method, gint32 native_offset, gint32 il_offset, gboolean managed, gpointer data)
420 StackWalkData *swdata = (StackWalkData*)data;
421 MonoStackBacktraceInfo *info;
423 if (swdata->frame_count >= swdata->stack_size)
424 return TRUE;
426 info = (MonoStackBacktraceInfo*)g_malloc (sizeof (*info));
427 info->method = method;
428 info->native_offset = native_offset;
430 swdata->stack [swdata->frame_count++] = info;
431 return FALSE;
434 static inline int mono_stack_backtrace (MonoProfiler *prof, MonoDomain *domain, void **stack, int size)
436 StackWalkData data;
438 data.prof = prof;
439 data.stack = stack;
440 data.stack_size = size;
441 data.frame_count = 0;
443 mono_stack_walk_no_il (stack_walk_func, (gpointer)&data);
445 return data.frame_count;
448 static void mono_portability_remember_string (MonoProfiler *prof, MonoDomain *domain, MonoString *str)
450 SavedString *head, *entry;
452 if (!str || !domain || !runtime_initialized)
453 return;
455 entry = (SavedString*)g_malloc0 (sizeof (SavedString));
456 entry->string = str;
457 entry->domain = domain;
458 entry->stack_entries = mono_stack_backtrace (prof, domain, entry->stack, BACKTRACE_SIZE);
459 if (entry->stack_entries == 0) {
460 g_free (entry);
461 return;
464 mono_mutex_lock (&mismatched_files_section);
465 head = (SavedString*)g_hash_table_lookup (prof->saved_strings_hash, (gpointer)str);
466 if (head) {
467 while (head->next)
468 head = head->next;
469 head->next = entry;
470 } else
471 g_hash_table_insert (prof->saved_strings_hash, (gpointer)str, (gpointer)entry);
472 mono_mutex_unlock (&mismatched_files_section);
475 static MonoClass *string_class = NULL;
477 static void mono_portability_remember_alloc (MonoProfiler *prof, MonoObject *obj, MonoClass *klass)
479 if (klass != string_class)
480 return;
481 mono_portability_remember_string (prof, mono_object_get_domain (obj), (MonoString*)obj);
484 static void mono_portability_iomap_event (MonoProfiler *prof, const char *report, const char *pathname, const char *new_pathname)
486 guint32 hash, pathnameHash;
487 MismatchedFilesStats *stats;
489 if (!runtime_initialized)
490 return;
492 mono_mutex_lock (&mismatched_files_section);
493 hash = calc_strings_hash (pathname, new_pathname, &pathnameHash);
494 stats = (MismatchedFilesStats*)g_hash_table_lookup (prof->mismatched_files_hash, &hash);
495 if (stats == NULL) {
496 guint32 *hashptr;
498 stats = (MismatchedFilesStats*) g_malloc (sizeof (MismatchedFilesStats));
499 stats->count = 1;
500 stats->requestedName = g_strdup (pathname);
501 stats->actualName = g_strdup (new_pathname);
502 hashptr = (guint32*)g_malloc (sizeof (guint32));
503 if (hashptr) {
504 *hashptr = hash;
505 g_hash_table_insert (prof->mismatched_files_hash, (gpointer)hashptr, stats);
506 } else
507 g_error ("Out of memory allocating integer pointer for mismatched files hash table.");
509 store_string_location (prof, (const gchar*)stats->requestedName, pathnameHash, strlen (stats->requestedName));
510 mono_mutex_unlock (&mismatched_files_section);
512 print_report ("%s - Found file path: '%s'\n", report, new_pathname);
513 } else {
514 mono_mutex_unlock (&mismatched_files_section);
515 stats->count++;
519 static void runtime_initialized_cb (MonoProfiler *prof)
521 runtime_initialized = TRUE;
522 string_class = mono_get_string_class ();
525 static void profiler_shutdown (MonoProfiler *prof)
527 print_mismatched_stats (prof);
528 mono_mutex_destroy (&mismatched_files_section);
531 void mono_profiler_startup (const char *desc)
533 MonoProfiler *prof = g_new0 (MonoProfiler, 1);
535 mono_mutex_init (&mismatched_files_section, NULL);
536 prof->mismatched_files_hash = g_hash_table_new (mismatched_files_guint32_hash, mismatched_files_guint32_equal);
537 prof->saved_strings_hash = g_hash_table_new (NULL, NULL);
538 prof->string_locations_hash = g_hash_table_new (mismatched_files_guint32_hash, mismatched_files_guint32_equal);
540 mono_profiler_install (prof, profiler_shutdown);
541 mono_profiler_install_runtime_initialized (runtime_initialized_cb);
542 mono_profiler_install_iomap (mono_portability_iomap_event);
543 mono_profiler_install_allocation (mono_portability_remember_alloc);
545 mono_profiler_set_events (MONO_PROFILE_ALLOCATIONS | MONO_PROFILE_IOMAP_EVENTS);