2 * mono-profiler-iomap.c: IOMAP string profiler for Mono.
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.
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
37 } MonoStackBacktraceInfo
;
44 } MismatchedFilesStats
;
46 typedef struct _SavedString
50 void *stack
[BACKTRACE_SIZE
];
52 struct _SavedString
*next
;
55 typedef struct _SavedStringFindInfo
59 } SavedStringFindInfo
;
61 typedef struct _StringLocation
64 struct _StringLocation
*next
;
69 GHashTable
*mismatched_files_hash
;
70 GHashTable
*saved_strings_hash
;
71 GHashTable
*string_locations_hash
;
72 gboolean may_have_locations
;
75 typedef struct _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
;
103 gboolean bannerShown
= FALSE
;
105 hash
= do_calc_string_hash (0, stats
->requestedName
);
110 stats
->count
, stats
->requestedName
, stats
->actualName
);
112 if (!prof
->may_have_locations
) {
113 fprintf (stdout
, "\n");
117 location
= (StringLocation
*)g_hash_table_lookup (prof
->string_locations_hash
, &hash
);
119 if (location
->hint
&& strlen (location
->hint
) > 0) {
121 fprintf (stdout
, "Locations:\n");
124 fprintf (stdout
, "%s", location
->hint
);
126 location
= location
->next
;
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)
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
);
146 static guint
mismatched_files_guint32_hash (gconstpointer key
)
151 return *((guint32
*)key
);
154 static gboolean
mismatched_files_guint32_equal (gconstpointer key1
, gconstpointer key2
)
159 return (gboolean
)(*((guint32
*)key1
) == *((guint32
*)key2
));
162 static inline guint32
do_calc_string_hash (guint32 hash
, const gchar
*str
)
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];
174 ret
= (ret
<< 5) - ret
+ *cc
;
179 static inline guint32
calc_strings_hash (const gchar
*str1
, const gchar
*str2
, guint32
*str1hash
)
181 guint32 hash
= do_calc_string_hash (0, str1
);
184 return do_calc_string_hash (hash
, str2
);
187 static inline void print_report (const gchar
*format
, ...)
196 fprintf (stdout
, "-=-=-=-=-=-=- MONO_IOMAP REPORT -=-=-=-=-=-=-\n");
197 va_start (ap
, format
);
198 vfprintf (stdout
, format
, ap
);
199 fprintf (stdout
, "\n");
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
);
214 static inline void append_report (GString
**report
, const gchar
*format
, ...)
218 *report
= g_string_new ("");
220 va_start (ap
, format
);
221 g_string_append_vprintf (*report
, format
, ap
);
225 static gboolean
saved_strings_find_func (gpointer key
, gpointer value
, gpointer user_data
)
228 SavedStringFindInfo
*info
= (SavedStringFindInfo
*)user_data
;
229 SavedString
*saved
= (SavedString
*)value
;
233 if (!info
|| !saved
|| mono_string_length (saved
->string
) != info
->len
)
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
);
241 if (hash
!= info
->hash
)
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
);
251 SavedStringFindInfo info
;
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
));
264 location
= (StringLocation
*)g_malloc0 (sizeof (location
));
266 g_hash_table_insert (prof
->string_locations_hash
, hashptr
, location
);
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
)
281 /* Now ignore the assemblies we know shouldn't contain mixed-case names (only the most frequent cases) */
283 if (strcmp (klass
->image
->assembly_name
, "mscorlib") == 0)
285 else if (strcmp (klass
->image
->assembly_name
, "System") == 0)
287 else if (strncmp (klass
->image
->assembly_name
, "Mono.", 5) == 0)
289 else if (strncmp (klass
->image
->assembly_name
, "System.", 7) == 0)
291 else if (strcmp (klass
->image
->assembly_name
, "PEAPI") == 0)
298 static inline gchar
*build_hint_from_stack (MonoDomain
*domain
, void **stack
, gint stack_entries
)
301 MonoMethod
*method
, *selectedMethod
;
302 MonoAssembly
*assembly
;
304 MonoDebugSourceLocation
*location
;
305 MonoStackBacktraceInfo
*info
;
306 gboolean use_full_trace
;
308 gint i
, native_offset
, firstAvailable
;
310 selectedMethod
= NULL
;
312 use_full_trace
= FALSE
;
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
)
321 if (firstAvailable
== -1)
324 image
= method
->klass
->image
;
325 assembly
= image
->assembly
;
327 if ((assembly
&& assembly
->in_gac
) || ignore_frame (method
))
329 selectedMethod
= method
;
330 native_offset
= info
->native_offset
;
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
))
343 selectedMethod
= method
;
344 native_offset
= info
->native_offset
;
349 use_full_trace
= TRUE
;
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
)
361 location
= mono_debug_lookup_source_location (method
, info
->native_offset
, domain
);
362 methodName
= mono_method_full_name (method
, TRUE
);
365 append_report (&trace
, LOCATION_INDENT
"%s in %s:%u\n", methodName
, location
->source_file
, location
->row
);
366 mono_debug_free_source_location (location
);
368 append_report (&trace
, LOCATION_INDENT
"%s\n", methodName
);
374 hint
= g_string_free (trace
, FALSE
);
376 g_string_free (trace
, TRUE
);
379 location
= mono_debug_lookup_source_location (selectedMethod
, native_offset
, domain
);
380 methodName
= mono_method_full_name (selectedMethod
, TRUE
);
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
);
386 hint
= g_strdup_printf (LOCATION_INDENT
"%s\n", methodName
);
393 static inline gchar
*build_hint (SavedString
*head
)
395 SavedString
*current
;
397 GString
*hint
= NULL
;
401 tmp
= build_hint_from_stack (current
->domain
, current
->stack
, current
->stack_entries
);
402 current
= current
->next
;
406 append_report (&hint
, tmp
);
411 return g_string_free (hint
, FALSE
);
413 g_string_free (hint
, FALSE
);
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
)
429 info
= (MonoStackBacktraceInfo
*)g_malloc (sizeof (*info
));
430 info
->method
= method
;
431 info
->native_offset
= native_offset
;
433 swdata
->stack
[swdata
->frame_count
++] = info
;
437 static inline int mono_stack_backtrace (MonoProfiler
*prof
, MonoDomain
*domain
, void **stack
, int size
)
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
)
458 entry
= (SavedString
*)g_malloc0 (sizeof (SavedString
));
460 entry
->domain
= domain
;
461 entry
->stack_entries
= mono_stack_backtrace (prof
, domain
, entry
->stack
, BACKTRACE_SIZE
);
462 if (entry
->stack_entries
== 0) {
467 mono_os_mutex_lock (&mismatched_files_section
);
468 head
= (SavedString
*)g_hash_table_lookup (prof
->saved_strings_hash
, (gpointer
)str
);
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
)
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
)
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
);
501 stats
= (MismatchedFilesStats
*) g_malloc (sizeof (MismatchedFilesStats
));
503 stats
->requestedName
= g_strdup (pathname
);
504 stats
->actualName
= g_strdup (new_pathname
);
505 hashptr
= (guint32
*)g_malloc (sizeof (guint32
));
508 g_hash_table_insert (prof
->mismatched_files_hash
, (gpointer
)hashptr
, stats
);
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
);
517 mono_os_mutex_unlock (&mismatched_files_section
);
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
));