[sgen] Make GCVTable type opaque, not a pointer.
[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 = 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 (char *obj, size_t size, PinStatAddress *node, int *pin_types)
120 {
121         if (!node)
122                 return;
123         if (node->addr >= obj && node->addr < obj + size) {
124                 int i;
125                 for (i = 0; i < PIN_TYPE_MAX; ++i) {
126                         int pin_bit = 1 << i;
127                         if (!(*pin_types & pin_bit) && (node->pin_types & pin_bit)) {
128                                 pinned_byte_counts [i] += size;
129                                 *pin_types |= pin_bit;
130                         }
131                 }
132         }
133         if (obj < node->addr)
134                 pin_stats_count_object_from_tree (obj, size, node->left, pin_types);
135         if (obj + size - 1 > node->addr)
136                 pin_stats_count_object_from_tree (obj, size, node->right, pin_types);
137 }
138
139 static gpointer
140 lookup_vtable_entry (SgenHashTable *hash_table, GCVTable vtable, gpointer empty_entry)
141 {
142         char *name = g_strdup_printf ("%s.%s", sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable));
143         gpointer entry = sgen_hash_table_lookup (hash_table, name);
144
145         if (entry) {
146                 g_free (name);
147         } else {
148                 sgen_hash_table_replace (hash_table, name, empty_entry, NULL);
149                 entry = sgen_hash_table_lookup (hash_table, name);
150         }
151
152         return entry;
153 }
154
155 static void
156 register_vtable (GCVTable vtable, int pin_types)
157 {
158         PinnedClassEntry empty_entry;
159         PinnedClassEntry *entry;
160         int i;
161
162         memset (&empty_entry, 0, sizeof (PinnedClassEntry));
163         entry = lookup_vtable_entry (&pinned_class_hash_table, vtable, &empty_entry);
164
165         for (i = 0; i < PIN_TYPE_MAX; ++i) {
166                 if (pin_types & (1 << i))
167                         ++entry->num_pins [i];
168         }
169 }
170
171 void
172 sgen_pin_stats_register_object (char *obj, size_t size)
173 {
174         int pin_types = 0;
175
176         if (!do_pin_stats)
177                 return;
178
179         pin_stats_count_object_from_tree (obj, size, pin_stat_addresses, &pin_types);
180         sgen_pointer_queue_add (&pinned_objects, obj);
181
182         if (pin_types)
183                 register_vtable ((GCVTable)SGEN_LOAD_VTABLE (obj), pin_types);
184 }
185
186 void
187 sgen_pin_stats_register_global_remset (char *obj)
188 {
189         GlobalRemsetClassEntry empty_entry;
190         GlobalRemsetClassEntry *entry;
191
192         if (!do_pin_stats)
193                 return;
194
195         memset (&empty_entry, 0, sizeof (GlobalRemsetClassEntry));
196         entry = lookup_vtable_entry (&global_remset_class_hash_table, (GCVTable)SGEN_LOAD_VTABLE (obj), &empty_entry);
197
198         ++entry->num_remsets;
199 }
200
201 void
202 sgen_pin_stats_print_class_stats (void)
203 {
204         char *name;
205         PinnedClassEntry *pinned_entry;
206         GlobalRemsetClassEntry *remset_entry;
207
208         if (!do_pin_stats)
209                 return;
210
211         mono_gc_printf (gc_debug_file, "\n%-50s  %10s  %10s  %10s\n", "Class", "Stack", "Static", "Other");
212         SGEN_HASH_TABLE_FOREACH (&pinned_class_hash_table, name, pinned_entry) {
213                 int i;
214                 mono_gc_printf (gc_debug_file, "%-50s", name);
215                 for (i = 0; i < PIN_TYPE_MAX; ++i)
216                         mono_gc_printf (gc_debug_file, "  %10ld", pinned_entry->num_pins [i]);
217                 mono_gc_printf (gc_debug_file, "\n");
218         } SGEN_HASH_TABLE_FOREACH_END;
219
220         mono_gc_printf (gc_debug_file, "\n%-50s  %10s\n", "Class", "#Remsets");
221         SGEN_HASH_TABLE_FOREACH (&global_remset_class_hash_table, name, remset_entry) {
222                 mono_gc_printf (gc_debug_file, "%-50s  %10ld\n", name, remset_entry->num_remsets);
223         } SGEN_HASH_TABLE_FOREACH_END;
224
225         mono_gc_printf (gc_debug_file, "\nTotal bytes pinned from stack: %ld  static: %ld  other: %ld\n",
226                         pinned_byte_counts [PIN_TYPE_STACK],
227                         pinned_byte_counts [PIN_TYPE_STATIC_DATA],
228                         pinned_byte_counts [PIN_TYPE_OTHER]);
229 }
230
231 size_t
232 sgen_pin_stats_get_pinned_byte_count (int pin_type)
233 {
234         return pinned_byte_counts [pin_type];
235 }
236
237 SgenPointerQueue*
238 sgen_pin_stats_get_object_list (void)
239 {
240         return &pinned_objects;
241 }
242
243 #endif /* HAVE_SGEN_GC */