5edccfaabec10720fcb6658f1eb8e7d54b8ccc24
[mono.git] / mono / sgen / sgen-pinning-stats.c
1 /*
2  * Copyright 2001-2003 Ximian, Inc
3  * Copyright 2003-2010 Novell, Inc.
4  * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
5  * 
6  * Permission is hereby granted, free of charge, to any person obtaining
7  * a copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sublicense, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  * 
14  * The above copyright notice and this permission notice shall be
15  * included in all copies or substantial portions of the Software.
16  * 
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24  */
25
26 #include "config.h"
27 #ifdef HAVE_SGEN_GC
28
29 #include <string.h>
30
31 #include "mono/sgen/sgen-gc.h"
32 #include "mono/sgen/sgen-pinning.h"
33 #include "mono/sgen/sgen-hash-table.h"
34 #include "mono/sgen/sgen-client.h"
35
36 typedef struct _PinStatAddress PinStatAddress;
37 struct _PinStatAddress {
38         char *addr;
39         int pin_types;
40         PinStatAddress *left;
41         PinStatAddress *right;
42 };
43
44 typedef struct {
45         size_t num_pins [PIN_TYPE_MAX];
46 } PinnedClassEntry;
47
48 typedef struct {
49         gulong num_remsets;
50 } GlobalRemsetClassEntry;
51
52 static gboolean do_pin_stats = FALSE;
53
54 static PinStatAddress *pin_stat_addresses = NULL;
55 static size_t pinned_byte_counts [PIN_TYPE_MAX];
56
57 static SgenPointerQueue pinned_objects = SGEN_POINTER_QUEUE_INIT (INTERNAL_MEM_STATISTICS);
58
59 static SgenHashTable pinned_class_hash_table = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_STATISTICS, INTERNAL_MEM_STAT_PINNED_CLASS, sizeof (PinnedClassEntry), g_str_hash, g_str_equal);
60 static SgenHashTable global_remset_class_hash_table = SGEN_HASH_TABLE_INIT (INTERNAL_MEM_STATISTICS, INTERNAL_MEM_STAT_REMSET_CLASS, sizeof (GlobalRemsetClassEntry), g_str_hash, g_str_equal);
61
62 void
63 sgen_pin_stats_enable (void)
64 {
65         do_pin_stats = TRUE;
66 }
67
68 static void
69 pin_stats_tree_free (PinStatAddress *node)
70 {
71         if (!node)
72                 return;
73         pin_stats_tree_free (node->left);
74         pin_stats_tree_free (node->right);
75         sgen_free_internal_dynamic (node, sizeof (PinStatAddress), INTERNAL_MEM_STATISTICS);
76 }
77
78 void
79 sgen_pin_stats_reset (void)
80 {
81         int i;
82         pin_stats_tree_free (pin_stat_addresses);
83         pin_stat_addresses = NULL;
84         for (i = 0; i < PIN_TYPE_MAX; ++i)
85                 pinned_byte_counts [i] = 0;
86         sgen_pointer_queue_clear (&pinned_objects);
87         sgen_hash_table_clean (&pinned_class_hash_table);
88         sgen_hash_table_clean (&global_remset_class_hash_table);
89 }
90
91 void
92 sgen_pin_stats_register_address (char *addr, int pin_type)
93 {
94         PinStatAddress **node_ptr = &pin_stat_addresses;
95         PinStatAddress *node;
96         int pin_type_bit = 1 << pin_type;
97
98         while (*node_ptr) {
99                 node = *node_ptr;
100                 if (addr == node->addr) {
101                         node->pin_types |= pin_type_bit;
102                         return;
103                 }
104                 if (addr < node->addr)
105                         node_ptr = &node->left;
106                 else
107                         node_ptr = &node->right;
108         }
109
110         node = (PinStatAddress *)sgen_alloc_internal_dynamic (sizeof (PinStatAddress), INTERNAL_MEM_STATISTICS, TRUE);
111         node->addr = addr;
112         node->pin_types = pin_type_bit;
113         node->left = node->right = NULL;
114
115         *node_ptr = node;
116 }
117
118 static void
119 pin_stats_count_object_from_tree (GCObject *object, size_t size, PinStatAddress *node, int *pin_types)
120 {
121         char *obj = (char*)object;
122         if (!node)
123                 return;
124         if (node->addr >= obj && node->addr < obj + size) {
125                 int i;
126                 for (i = 0; i < PIN_TYPE_MAX; ++i) {
127                         int pin_bit = 1 << i;
128                         if (!(*pin_types & pin_bit) && (node->pin_types & pin_bit)) {
129                                 pinned_byte_counts [i] += size;
130                                 *pin_types |= pin_bit;
131                         }
132                 }
133         }
134         if (obj < node->addr)
135                 pin_stats_count_object_from_tree (object, size, node->left, pin_types);
136         if (obj + size - 1 > node->addr)
137                 pin_stats_count_object_from_tree (object, size, node->right, pin_types);
138 }
139
140 static gpointer
141 lookup_vtable_entry (SgenHashTable *hash_table, GCVTable vtable, gpointer empty_entry)
142 {
143         char *name = g_strdup_printf ("%s.%s", sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable));
144         gpointer entry = sgen_hash_table_lookup (hash_table, name);
145
146         if (entry) {
147                 g_free (name);
148         } else {
149                 sgen_hash_table_replace (hash_table, name, empty_entry, NULL);
150                 entry = sgen_hash_table_lookup (hash_table, name);
151         }
152
153         return entry;
154 }
155
156 static void
157 register_vtable (GCVTable vtable, int pin_types)
158 {
159         PinnedClassEntry empty_entry;
160         PinnedClassEntry *entry;
161         int i;
162
163         memset (&empty_entry, 0, sizeof (PinnedClassEntry));
164         entry = (PinnedClassEntry *)lookup_vtable_entry (&pinned_class_hash_table, vtable, &empty_entry);
165
166         for (i = 0; i < PIN_TYPE_MAX; ++i) {
167                 if (pin_types & (1 << i))
168                         ++entry->num_pins [i];
169         }
170 }
171
172 void
173 sgen_pin_stats_register_object (GCObject *obj, size_t size)
174 {
175         int pin_types = 0;
176
177         if (!do_pin_stats)
178                 return;
179
180         pin_stats_count_object_from_tree (obj, size, pin_stat_addresses, &pin_types);
181         sgen_pointer_queue_add (&pinned_objects, obj);
182
183         if (pin_types)
184                 register_vtable (SGEN_LOAD_VTABLE (obj), pin_types);
185 }
186
187 void
188 sgen_pin_stats_register_global_remset (GCObject *obj)
189 {
190         GlobalRemsetClassEntry empty_entry;
191         GlobalRemsetClassEntry *entry;
192
193         if (!do_pin_stats)
194                 return;
195
196         memset (&empty_entry, 0, sizeof (GlobalRemsetClassEntry));
197         entry = (GlobalRemsetClassEntry *)lookup_vtable_entry (&global_remset_class_hash_table, SGEN_LOAD_VTABLE (obj), &empty_entry);
198
199         ++entry->num_remsets;
200 }
201
202 void
203 sgen_pin_stats_print_class_stats (void)
204 {
205         char *name;
206         PinnedClassEntry *pinned_entry;
207         GlobalRemsetClassEntry *remset_entry;
208
209         if (!do_pin_stats)
210                 return;
211
212         mono_gc_printf (gc_debug_file, "\n%-50s  %10s  %10s  %10s\n", "Class", "Stack", "Static", "Other");
213         SGEN_HASH_TABLE_FOREACH (&pinned_class_hash_table, char *, name, PinnedClassEntry *, pinned_entry) {
214                 int i;
215                 mono_gc_printf (gc_debug_file, "%-50s", name);
216                 for (i = 0; i < PIN_TYPE_MAX; ++i)
217                         mono_gc_printf (gc_debug_file, "  %10ld", pinned_entry->num_pins [i]);
218                 mono_gc_printf (gc_debug_file, "\n");
219         } SGEN_HASH_TABLE_FOREACH_END;
220
221         mono_gc_printf (gc_debug_file, "\n%-50s  %10s\n", "Class", "#Remsets");
222         SGEN_HASH_TABLE_FOREACH (&global_remset_class_hash_table, char *, name, GlobalRemsetClassEntry *, remset_entry) {
223                 mono_gc_printf (gc_debug_file, "%-50s  %10ld\n", name, remset_entry->num_remsets);
224         } SGEN_HASH_TABLE_FOREACH_END;
225
226         mono_gc_printf (gc_debug_file, "\nTotal bytes pinned from stack: %ld  static: %ld  other: %ld\n",
227                         pinned_byte_counts [PIN_TYPE_STACK],
228                         pinned_byte_counts [PIN_TYPE_STATIC_DATA],
229                         pinned_byte_counts [PIN_TYPE_OTHER]);
230 }
231
232 size_t
233 sgen_pin_stats_get_pinned_byte_count (int pin_type)
234 {
235         return pinned_byte_counts [pin_type];
236 }
237
238 SgenPointerQueue*
239 sgen_pin_stats_get_object_list (void)
240 {
241         return &pinned_objects;
242 }
243
244 #endif /* HAVE_SGEN_GC */