4 #include <mono/io-layer/mono-mutex.h>
5 #include <mono/metadata/class.h>
6 #include <mono/metadata/profiler.h>
8 #include "gchandle-profiler.h"
11 mono_profiler_startup (const char *desc)
15 g_print ("*** Running with the GCHandle profiler ***\n");
17 // The profiler uses gchandle alloc info and jit info
18 prof = gchandle_profiler_new ();
19 mono_profiler_install (prof, gchandle_profiler_shutdown);
20 mono_profiler_install_gc_roots (gchandle_profiler_track_gchandle, NULL);
21 mono_profiler_install_jit_end (gchandle_profiler_method_jitted);
22 mono_profiler_set_events (MONO_PROFILE_GC_ROOTS | MONO_PROFILE_JIT_COMPILATION);
23 mono_profiler_set_events (MONO_PROFILE_GC_ROOTS);
26 void gchandle_profiler_shutdown (MonoProfiler *prof)
28 g_print ("Shutting down the profiler\n");
29 gchandle_profiler_dump_jitted_methods (prof);
30 gchandle_profiler_dump_gchandles (prof);
33 MonoProfiler *gchandle_profiler_new ()
37 prof = g_new0 (MonoProfiler, 1);
38 prof->type_name = g_getenv ("GCHANDLES_FOR_TYPE");
39 prof->gchandles = g_ptr_array_new ();
40 prof->jitted_methods = g_hash_table_new (g_str_hash, g_str_equal);
41 prof->stacktraces = g_ptr_array_new ();
42 mono_mutex_init (&prof->mutex, NULL);
45 g_print ("*** Recording GCHandle allocation stacktraces for type '%s'\n", prof->type_name);
51 gchandle_profiler_dump_jitted_methods (MonoProfiler *prof)
53 g_hash_table_foreach (prof->jitted_methods, dump_jitted_methods, NULL);
57 gchandle_profiler_method_jitted (MonoProfiler *prof, MonoMethod *method, MonoJitInfo* jinfo, int result)
59 // Whenever a method is jitted, store the method name and increment the count by 1.
60 // Methods can be jitted multiple times if instance delegates are passed to native code
64 mono_mutex_lock (&prof->mutex);
66 name = mono_method_full_name (method, 1);
67 count = GPOINTER_TO_INT (g_hash_table_lookup (prof->jitted_methods, name));
68 g_hash_table_insert (prof->jitted_methods, name, GINT_TO_POINTER (count + 1));
70 mono_mutex_unlock (&prof->mutex);
73 void gchandle_profiler_track_gchandle (MonoProfiler *prof, int op, int type, uintptr_t handle, MonoObject *obj)
77 GPtrArray *stacktraces;
79 // Ignore anything that isn't a strong GC handle
83 gchandles = prof->gchandles;
84 stacktraces = prof->stacktraces;
86 mono_mutex_lock (&prof->mutex);
88 // Keep the two arrays in sync so that the gchandle at index i stores its stacktrace at index i in the
90 if (op == MONO_PROFILER_GC_HANDLE_CREATED) {
91 g_ptr_array_add (gchandles, (gpointer) handle);
92 if (prof->type_name && !strcmp (prof->type_name, mono_class_get_name (mono_object_get_class(obj))))
93 g_ptr_array_add (stacktraces, get_stack_trace ());
95 g_ptr_array_add (stacktraces, NULL);
96 } else if (op == MONO_PROFILER_GC_HANDLE_DESTROYED) {
97 for (i = 0; i < (int)gchandles->len; i++) {
98 if (g_ptr_array_index (gchandles, i) == (gpointer) handle) {
99 g_free (g_ptr_array_index (stacktraces, i));
100 g_ptr_array_remove_index_fast (gchandles, i);
101 g_ptr_array_remove_index_fast (stacktraces, i);
107 mono_mutex_unlock (&prof->mutex);
110 void dump_jitted_methods (gpointer key, gpointer value, gpointer user_data)
112 // We only care about methods which are jitted multiple times.
113 int jit_count = GPOINTER_TO_INT (value);
115 g_print ("%d:\t%s\n", jit_count, (char*)key);
118 char *get_stack_trace ()
123 str = g_string_new ("");
124 mono_stack_walk_no_il (stack_walk_fn, str);
127 g_string_free (str, FALSE);
132 gboolean stack_walk_fn (MonoMethod *method, gint32 native_offset, gint32 il_offset, gboolean managed, gpointer data)
138 str = (GString *) data;
139 klass = mono_method_get_class (method);
141 g_string_append_c (str, '\t');
142 g_string_append (str, mono_class_get_namespace (klass));
143 g_string_append_c (str, '.');
144 g_string_append (str, mono_class_get_name (klass));
145 g_string_append_c (str, '.');
146 g_string_append (str, mono_method_get_name (method));
147 g_string_append_c (str, '\n');
153 void accumulate_gchandles_by_type (gpointer data, gpointer user_data)
155 // For every GCHandle we get the class name and store it in a hashtable
156 // along with the number of times we've seen that class name. This tells
157 // us how many GCHandles we have allocated for each class type.
162 by_type = (GHashTable*) user_data;
163 name = class_name_from_gchandle (GPOINTER_TO_INT (data));
166 count = GPOINTER_TO_INT (g_hash_table_lookup (by_type, name)) + 1;
167 g_hash_table_insert (by_type, (void*) name, GINT_TO_POINTER (count));
171 const char *class_name_from_gchandle (gint32 gchandle)
176 ob = mono_gchandle_get_target (gchandle);
180 // Add in specific support for Gtk.ToggleRef in gtk-sharp so that
181 // the profiler can detect what objects the ToggleRef is keeping alive
182 name = mono_class_get_name (mono_object_get_class(ob));
183 if (name && !strcmp (name, "ToggleRef")) {
184 MonoClassField *field = mono_class_get_field_from_name (mono_object_get_class(ob), "reference");
186 mono_field_get_value (ob, field, &ob);
188 name = mono_class_get_name (mono_object_get_class(ob));
195 void gchandle_profiler_dump_gchandles (MonoProfiler *prof)
199 GPtrArray *top_n_by_type;
201 by_type = g_hash_table_new (g_str_hash, g_str_equal);
202 top_n_by_type = g_ptr_array_new ();
204 // Generate a sorted/filtered list of results so that we can print the
205 // number of gchandles allocated for each type in ascending order so types
206 // with a lot of GChandles are printed last.
207 g_ptr_array_foreach (prof->gchandles, accumulate_gchandles_by_type, by_type);
208 g_hash_table_foreach (by_type, add_hashtable_keys_to_ptr_array, top_n_by_type);
209 g_ptr_array_sort_with_data (top_n_by_type, gchandle_instances_comparer, by_type);
211 for (i = 0; i < (int) top_n_by_type->len; i++)
212 g_print ("\t%d GCHandles referencing type '%s'\n", GPOINTER_TO_INT (g_hash_table_lookup (by_type, top_n_by_type->pdata [i])), (char *) top_n_by_type->pdata [i]);
215 gchandle_profiler_dump_gchandle_traces (prof);
218 void gchandle_profiler_dump_gchandle_traces (MonoProfiler *prof)
222 GPtrArray *gchandles;
225 GPtrArray *stacktraces;
227 if (!prof->type_name)
230 gchandles = prof->gchandles;
231 stacktraces = prof->stacktraces;
233 // For all allocated handles, see if any of them are referencing objects of the type
234 // we care about. If they are, print out the allocation trace of all handles targetting
235 // that object. Note that multiple handles targetting the same object are grouped together
236 for (i = 0; i < (int) gchandles->len; i ++) {
237 gchandle = GPOINTER_TO_INT (g_ptr_array_index (gchandles, i));
238 ob = mono_gchandle_get_target (gchandle);
239 name = class_name_from_gchandle (gchandle);
240 if (name && !strcmp (name, prof->type_name)) {
241 g_print ("Strong GCHandles allocated for object %p:\n", ob);
242 for (j = i; j < (int) gchandles->len; j++) {
243 if (mono_gchandle_get_target (GPOINTER_TO_INT (g_ptr_array_index (gchandles, j))) == ob) {
244 g_print ("%s\n", (char *) g_ptr_array_index (stacktraces, j));
245 g_ptr_array_remove_index_fast (gchandles, j);
246 g_ptr_array_remove_index_fast (stacktraces, j);
256 void add_hashtable_keys_to_ptr_array (gpointer key, gpointer value, gpointer user_data)
258 GPtrArray *by_type = (GPtrArray*) user_data;
259 g_ptr_array_add (by_type, key);
262 gint gchandle_instances_comparer (gconstpointer base1, gconstpointer base2, gpointer user_data)
264 GHashTable *by_type = (GHashTable *) user_data;
265 char *left = *((char **) base1);
266 char *right = *((char **) base2);
268 int iddiff = GPOINTER_TO_INT (g_hash_table_lookup (by_type, left)) - GPOINTER_TO_INT (g_hash_table_lookup (by_type, right));