[Facades] Use the Open.snk key for the System.ValueTuple facade (#4173)
[mono-project.git] / mono / profiler / mono-profiler-iomap.c
blob15baec017ac03cd4dc6a5451303577f73ab1433e
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.
11 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
13 #include "config.h"
15 #include <string.h>
16 #include <mono/utils/mono-io-portability.h>
17 #include <mono/metadata/metadata.h>
18 #include <mono/metadata/metadata-internals.h>
19 #include <mono/metadata/class.h>
20 #include <mono/metadata/class-internals.h>
21 #include <mono/metadata/object-internals.h>
22 #include <mono/metadata/image.h>
23 #include <mono/metadata/mono-debug.h>
24 #include <mono/metadata/debug-helpers.h>
25 #include <mono/metadata/threads.h>
26 #include <mono/metadata/profiler.h>
27 #include <mono/metadata/loader.h>
28 #include <mono/utils/mono-os-mutex.h>
30 #define LOCATION_INDENT " "
31 #define BACKTRACE_SIZE 64
33 typedef struct _MonoStackBacktraceInfo
35 MonoMethod *method;
36 gint native_offset;
37 } MonoStackBacktraceInfo;
39 typedef struct
41 guint32 count;
42 gchar *requestedName;
43 gchar *actualName;
44 } MismatchedFilesStats;
46 typedef struct _SavedString
48 MonoString *string;
49 MonoDomain *domain;
50 void *stack [BACKTRACE_SIZE];
51 gint stack_entries;
52 struct _SavedString *next;
53 } SavedString;
55 typedef struct _SavedStringFindInfo
57 guint32 hash;
58 size_t len;
59 } SavedStringFindInfo;
61 typedef struct _StringLocation
63 gchar *hint;
64 struct _StringLocation *next;
65 } StringLocation;
67 struct _MonoProfiler
69 GHashTable *mismatched_files_hash;
70 GHashTable *saved_strings_hash;
71 GHashTable *string_locations_hash;
72 gboolean may_have_locations;
75 typedef struct _StackWalkData
77 MonoProfiler *prof;
78 void **stack;
79 int stack_size;
80 int frame_count;
81 } StackWalkData;
83 static mono_mutex_t mismatched_files_section;
84 static gboolean runtime_initialized = FALSE;
86 static inline void append_report (GString **report, const gchar *format, ...);
87 static inline void print_report (const gchar *format, ...);
88 static inline guint32 do_calc_string_hash (guint32 hash, const gchar *str);
89 static inline guint32 calc_strings_hash (const gchar *str1, const gchar *str2, guint32 *str1hash);
90 static void print_mismatched_stats (MonoProfiler *prof);
91 static inline gchar *build_hint (SavedString *head);
92 static inline gchar *build_hint_from_stack (MonoDomain *domain, void **stack, gint stack_entries);
93 static inline void store_string_location (MonoProfiler *prof, const gchar *string, guint32 hash, size_t len);
94 static void mono_portability_remember_string (MonoProfiler *prof, MonoDomain *domain, MonoString *str);
95 void mono_profiler_startup (const char *desc);
97 static void mismatched_stats_foreach_func (gpointer key, gpointer value, gpointer user_data)
99 MismatchedFilesStats *stats = (MismatchedFilesStats*)value;
100 StringLocation *location;
101 MonoProfiler *prof = (MonoProfiler*)user_data;
102 guint32 hash;
103 gboolean bannerShown = FALSE;
105 hash = do_calc_string_hash (0, stats->requestedName);
106 fprintf (stdout,
107 " Count: %u\n"
108 "Requested: %s\n"
109 " Actual: %s\n",
110 stats->count, stats->requestedName, stats->actualName);
112 if (!prof->may_have_locations) {
113 fprintf (stdout, "\n");
114 return;
117 location = (StringLocation *)g_hash_table_lookup (prof->string_locations_hash, &hash);
118 while (location) {
119 if (location->hint && strlen (location->hint) > 0) {
120 if (!bannerShown) {
121 fprintf (stdout, "Locations:\n");
122 bannerShown = TRUE;
124 fprintf (stdout, "%s", location->hint);
126 location = location->next;
127 if (location)
128 fprintf (stdout, LOCATION_INDENT "--\n");
131 fprintf (stdout, "\n");
134 static void print_mismatched_stats (MonoProfiler *prof)
136 if (!prof->mismatched_files_hash || g_hash_table_size (prof->mismatched_files_hash) == 0)
137 return;
139 prof->may_have_locations = g_hash_table_size (prof->string_locations_hash) > 0;
141 fprintf (stdout, "\n-=-=-=-=-=-=-= MONO_IOMAP Stats -=-=-=-=-=-=-=\n");
142 g_hash_table_foreach (prof->mismatched_files_hash, mismatched_stats_foreach_func, (gpointer)prof);
143 fflush (stdout);
146 static guint mismatched_files_guint32_hash (gconstpointer key)
148 if (!key)
149 return 0;
151 return *((guint32*)key);
154 static gboolean mismatched_files_guint32_equal (gconstpointer key1, gconstpointer key2)
156 if (!key1 || !key2)
157 return FALSE;
159 return (gboolean)(*((guint32*)key1) == *((guint32*)key2));
162 static inline guint32 do_calc_string_hash (guint32 hash, const gchar *str)
164 guint32 ret = hash;
165 gchar *cc = (gchar*)str;
166 gchar *end = (gchar*)(str + strlen (str) - 1);
168 for (; cc < end; cc += 2) {
169 ret = (ret << 5) - ret + *cc;
170 ret = (ret << 5) - ret + cc [1];
172 end++;
173 if (cc < end)
174 ret = (ret << 5) - ret + *cc;
176 return ret;
179 static inline guint32 calc_strings_hash (const gchar *str1, const gchar *str2, guint32 *str1hash)
181 guint32 hash = do_calc_string_hash (0, str1);
182 if (str1hash)
183 *str1hash = hash;
184 return do_calc_string_hash (hash, str2);
187 static inline void print_report (const gchar *format, ...)
189 MonoError error;
190 MonoClass *klass;
191 MonoProperty *prop;
192 MonoString *str;
193 char *stack_trace;
194 va_list ap;
196 fprintf (stdout, "-=-=-=-=-=-=- MONO_IOMAP REPORT -=-=-=-=-=-=-\n");
197 va_start (ap, format);
198 vfprintf (stdout, format, ap);
199 fprintf (stdout, "\n");
200 va_end (ap);
201 klass = mono_class_load_from_name (mono_get_corlib (), "System", "Environment");
202 mono_class_init (klass);
203 prop = mono_class_get_property_from_name (klass, "StackTrace");
204 str = (MonoString*)mono_property_get_value_checked (prop, NULL, NULL, &error);
205 mono_error_assert_ok (&error);
206 stack_trace = mono_string_to_utf8_checked (str, &error);
207 mono_error_assert_ok (&error);
209 fprintf (stdout, "-= Stack Trace =-\n%s\n\n", stack_trace);
210 g_free (stack_trace);
211 fflush (stdout);
214 static inline void append_report (GString **report, const gchar *format, ...)
216 va_list ap;
217 if (!*report)
218 *report = g_string_new ("");
220 va_start (ap, format);
221 g_string_append_vprintf (*report, format, ap);
222 va_end (ap);
225 static gboolean saved_strings_find_func (gpointer key, gpointer value, gpointer user_data)
227 MonoError error;
228 SavedStringFindInfo *info = (SavedStringFindInfo*)user_data;
229 SavedString *saved = (SavedString*)value;
230 gchar *utf_str;
231 guint32 hash;
233 if (!info || !saved || mono_string_length (saved->string) != info->len)
234 return FALSE;
236 utf_str = mono_string_to_utf8_checked (saved->string, &error);
237 mono_error_assert_ok (&error);
238 hash = do_calc_string_hash (0, utf_str);
239 g_free (utf_str);
241 if (hash != info->hash)
242 return FALSE;
244 return TRUE;
247 static inline void store_string_location (MonoProfiler *prof, const gchar *string, guint32 hash, size_t len)
249 StringLocation *location = (StringLocation *)g_hash_table_lookup (prof->string_locations_hash, &hash);
250 SavedString *saved;
251 SavedStringFindInfo info;
252 guint32 *hashptr;
254 if (location)
255 return;
257 info.hash = hash;
258 info.len = len;
260 /* Expensive but unavoidable... */
261 saved = (SavedString*)g_hash_table_find (prof->saved_strings_hash, saved_strings_find_func, &info);
262 hashptr = (guint32*)g_malloc (sizeof (guint32));
263 *hashptr = hash;
264 location = (StringLocation*)g_malloc0 (sizeof (location));
266 g_hash_table_insert (prof->string_locations_hash, hashptr, location);
267 if (!saved)
268 return;
270 g_hash_table_remove (prof->saved_strings_hash, saved->string);
271 location->hint = build_hint (saved);
274 static gboolean ignore_frame (MonoMethod *method)
276 MonoClass *klass = method->klass;
278 if (method->wrapper_type != MONO_WRAPPER_NONE)
279 return TRUE;
281 /* Now ignore the assemblies we know shouldn't contain mixed-case names (only the most frequent cases) */
282 if (klass->image ) {
283 if (strcmp (klass->image->assembly_name, "mscorlib") == 0)
284 return TRUE;
285 else if (strcmp (klass->image->assembly_name, "System") == 0)
286 return TRUE;
287 else if (strncmp (klass->image->assembly_name, "Mono.", 5) == 0)
288 return TRUE;
289 else if (strncmp (klass->image->assembly_name, "System.", 7) == 0)
290 return TRUE;
291 else if (strcmp (klass->image->assembly_name, "PEAPI") == 0)
292 return TRUE;
295 return FALSE;
298 static inline gchar *build_hint_from_stack (MonoDomain *domain, void **stack, gint stack_entries)
300 gchar *hint;
301 MonoMethod *method, *selectedMethod;
302 MonoAssembly *assembly;
303 MonoImage *image;
304 MonoDebugSourceLocation *location;
305 MonoStackBacktraceInfo *info;
306 gboolean use_full_trace;
307 char *methodName;
308 gint i, native_offset, firstAvailable;
310 selectedMethod = NULL;
311 firstAvailable = -1;
312 use_full_trace = FALSE;
313 native_offset = -1;
314 for (i = 0; i < stack_entries; i++) {
315 info = (MonoStackBacktraceInfo*) stack [i];
316 method = info ? info->method : NULL;
318 if (!method || method->wrapper_type != MONO_WRAPPER_NONE)
319 continue;
321 if (firstAvailable == -1)
322 firstAvailable = i;
324 image = method->klass->image;
325 assembly = image->assembly;
327 if ((assembly && assembly->in_gac) || ignore_frame (method))
328 continue;
329 selectedMethod = method;
330 native_offset = info->native_offset;
331 break;
334 if (!selectedMethod) {
335 /* All the frames were from assemblies installed in GAC. Find first frame that is
336 * not in the ignore list */
337 for (i = 0; i < stack_entries; i++) {
338 info = (MonoStackBacktraceInfo*) stack [i];
339 method = info ? info->method : NULL;
341 if (!method || ignore_frame (method))
342 continue;
343 selectedMethod = method;
344 native_offset = info->native_offset;
345 break;
348 if (!selectedMethod)
349 use_full_trace = TRUE;
352 hint = NULL;
353 if (use_full_trace) {
354 GString *trace = g_string_new ("Full trace:\n");
355 for (i = firstAvailable; i < stack_entries; i++) {
356 info = (MonoStackBacktraceInfo*) stack [i];
357 method = info ? info->method : NULL;
358 if (!method || method->wrapper_type != MONO_WRAPPER_NONE)
359 continue;
361 location = mono_debug_lookup_source_location (method, info->native_offset, domain);
362 methodName = mono_method_full_name (method, TRUE);
364 if (location) {
365 append_report (&trace, LOCATION_INDENT "%s in %s:%u\n", methodName, location->source_file, location->row);
366 mono_debug_free_source_location (location);
367 } else
368 append_report (&trace, LOCATION_INDENT "%s\n", methodName);
369 g_free (methodName);
372 if (trace) {
373 if (trace->len)
374 hint = g_string_free (trace, FALSE);
375 else
376 g_string_free (trace, TRUE);
378 } else {
379 location = mono_debug_lookup_source_location (selectedMethod, native_offset, domain);
380 methodName = mono_method_full_name (selectedMethod, TRUE);
382 if (location) {
383 hint = g_strdup_printf (LOCATION_INDENT "%s in %s:%u\n", methodName, location->source_file, location->row);
384 mono_debug_free_source_location (location);
385 } else
386 hint = g_strdup_printf (LOCATION_INDENT "%s\n", methodName);
387 g_free (methodName);
390 return hint;
393 static inline gchar *build_hint (SavedString *head)
395 SavedString *current;
396 gchar *tmp;
397 GString *hint = NULL;
399 current = head;
400 while (current) {
401 tmp = build_hint_from_stack (current->domain, current->stack, current->stack_entries);
402 current = current->next;
403 if (!tmp)
404 continue;
406 append_report (&hint, tmp);
409 if (hint) {
410 if (hint->len)
411 return g_string_free (hint, FALSE);
412 else {
413 g_string_free (hint, FALSE);
414 return NULL;
418 return NULL;
421 static gboolean stack_walk_func (MonoMethod *method, gint32 native_offset, gint32 il_offset, gboolean managed, gpointer data)
423 StackWalkData *swdata = (StackWalkData*)data;
424 MonoStackBacktraceInfo *info;
426 if (swdata->frame_count >= swdata->stack_size)
427 return TRUE;
429 info = (MonoStackBacktraceInfo*)g_malloc (sizeof (*info));
430 info->method = method;
431 info->native_offset = native_offset;
433 swdata->stack [swdata->frame_count++] = info;
434 return FALSE;
437 static inline int mono_stack_backtrace (MonoProfiler *prof, MonoDomain *domain, void **stack, int size)
439 StackWalkData data;
441 data.prof = prof;
442 data.stack = stack;
443 data.stack_size = size;
444 data.frame_count = 0;
446 mono_stack_walk_no_il (stack_walk_func, (gpointer)&data);
448 return data.frame_count;
451 static void mono_portability_remember_string (MonoProfiler *prof, MonoDomain *domain, MonoString *str)
453 SavedString *head, *entry;
455 if (!str || !domain || !runtime_initialized)
456 return;
458 entry = (SavedString*)g_malloc0 (sizeof (SavedString));
459 entry->string = str;
460 entry->domain = domain;
461 entry->stack_entries = mono_stack_backtrace (prof, domain, entry->stack, BACKTRACE_SIZE);
462 if (entry->stack_entries == 0) {
463 g_free (entry);
464 return;
467 mono_os_mutex_lock (&mismatched_files_section);
468 head = (SavedString*)g_hash_table_lookup (prof->saved_strings_hash, (gpointer)str);
469 if (head) {
470 while (head->next)
471 head = head->next;
472 head->next = entry;
473 } else
474 g_hash_table_insert (prof->saved_strings_hash, (gpointer)str, (gpointer)entry);
475 mono_os_mutex_unlock (&mismatched_files_section);
478 static MonoClass *string_class = NULL;
480 static void mono_portability_remember_alloc (MonoProfiler *prof, MonoObject *obj, MonoClass *klass)
482 if (klass != string_class)
483 return;
484 mono_portability_remember_string (prof, mono_object_get_domain (obj), (MonoString*)obj);
487 static void mono_portability_iomap_event (MonoProfiler *prof, const char *report, const char *pathname, const char *new_pathname)
489 guint32 hash, pathnameHash;
490 MismatchedFilesStats *stats;
492 if (!runtime_initialized)
493 return;
495 mono_os_mutex_lock (&mismatched_files_section);
496 hash = calc_strings_hash (pathname, new_pathname, &pathnameHash);
497 stats = (MismatchedFilesStats*)g_hash_table_lookup (prof->mismatched_files_hash, &hash);
498 if (stats == NULL) {
499 guint32 *hashptr;
501 stats = (MismatchedFilesStats*) g_malloc (sizeof (MismatchedFilesStats));
502 stats->count = 1;
503 stats->requestedName = g_strdup (pathname);
504 stats->actualName = g_strdup (new_pathname);
505 hashptr = (guint32*)g_malloc (sizeof (guint32));
506 if (hashptr) {
507 *hashptr = hash;
508 g_hash_table_insert (prof->mismatched_files_hash, (gpointer)hashptr, stats);
509 } else
510 g_error ("Out of memory allocating integer pointer for mismatched files hash table.");
512 store_string_location (prof, (const gchar*)stats->requestedName, pathnameHash, strlen (stats->requestedName));
513 mono_os_mutex_unlock (&mismatched_files_section);
515 print_report ("%s - Found file path: '%s'\n", report, new_pathname);
516 } else {
517 mono_os_mutex_unlock (&mismatched_files_section);
518 stats->count++;
522 static void runtime_initialized_cb (MonoProfiler *prof)
524 runtime_initialized = TRUE;
525 string_class = mono_get_string_class ();
528 static void profiler_shutdown (MonoProfiler *prof)
530 print_mismatched_stats (prof);
531 mono_os_mutex_destroy (&mismatched_files_section);
534 void mono_profiler_startup (const char *desc)
536 MonoProfiler *prof = g_new0 (MonoProfiler, 1);
538 mono_os_mutex_init (&mismatched_files_section);
539 prof->mismatched_files_hash = g_hash_table_new (mismatched_files_guint32_hash, mismatched_files_guint32_equal);
540 prof->saved_strings_hash = g_hash_table_new (NULL, NULL);
541 prof->string_locations_hash = g_hash_table_new (mismatched_files_guint32_hash, mismatched_files_guint32_equal);
543 mono_profiler_install (prof, profiler_shutdown);
544 mono_profiler_install_runtime_initialized (runtime_initialized_cb);
545 mono_profiler_install_iomap (mono_portability_iomap_event);
546 mono_profiler_install_allocation (mono_portability_remember_alloc);
548 mono_profiler_set_events ((MonoProfileFlags)(MONO_PROFILE_ALLOCATIONS | MONO_PROFILE_IOMAP_EVENTS));