Merge pull request #5714 from alexischr/update_bockbuild
[mono.git] / mono / sgen / sgen-pinning-stats.c
1 /**
2  * \file
3  * Copyright 2001-2003 Ximian, Inc
4  * Copyright 2003-2010 Novell, Inc.
5  * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
6  * 
7  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
8  */
9
10 #include "config.h"
11 #ifdef HAVE_SGEN_GC
12
13 #include <string.h>
14
15 #include "mono/sgen/sgen-gc.h"
16 #include "mono/sgen/sgen-pinning.h"
17 #include "mono/sgen/sgen-hash-table.h"
18 #include "mono/sgen/sgen-client.h"
19
20 typedef struct _PinStatAddress PinStatAddress;
21 struct _PinStatAddress {
22         char *addr;
23         int pin_types;
24         PinStatAddress *left;
25         PinStatAddress *right;
26 };
27
28 typedef struct {
29         size_t num_pins [PIN_TYPE_MAX];
30 } PinnedClassEntry;
31
32 typedef struct {
33         gulong num_remsets;
34 } GlobalRemsetClassEntry;
35
36 static gboolean do_pin_stats = FALSE;
37
38 static PinStatAddress *pin_stat_addresses = NULL;
39 static size_t pinned_byte_counts [PIN_TYPE_MAX];
40
41 static size_t pinned_bytes_in_generation [GENERATION_MAX];
42 static int pinned_objects_in_generation [GENERATION_MAX];
43
44 static SgenPointerQueue pinned_objects = SGEN_POINTER_QUEUE_INIT (INTERNAL_MEM_STATISTICS);
45
46 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);
47 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);
48
49 void
50 sgen_pin_stats_enable (void)
51 {
52         do_pin_stats = TRUE;
53 }
54
55 static void
56 pin_stats_tree_free (PinStatAddress *node)
57 {
58         if (!node)
59                 return;
60         pin_stats_tree_free (node->left);
61         pin_stats_tree_free (node->right);
62         sgen_free_internal_dynamic (node, sizeof (PinStatAddress), INTERNAL_MEM_STATISTICS);
63 }
64
65 void
66 sgen_pin_stats_reset (void)
67 {
68         int i;
69         pin_stats_tree_free (pin_stat_addresses);
70         pin_stat_addresses = NULL;
71         for (i = 0; i < PIN_TYPE_MAX; ++i)
72                 pinned_byte_counts [i] = 0;
73         for (i = 0; i < GENERATION_MAX; ++i) {
74                 pinned_bytes_in_generation [i] = 0;
75                 pinned_objects_in_generation [i] = 0;
76         }
77         sgen_pointer_queue_clear (&pinned_objects);
78         sgen_hash_table_clean (&pinned_class_hash_table);
79         sgen_hash_table_clean (&global_remset_class_hash_table);
80 }
81
82 void
83 sgen_pin_stats_register_address (char *addr, int pin_type)
84 {
85         PinStatAddress **node_ptr = &pin_stat_addresses;
86         PinStatAddress *node;
87         int pin_type_bit = 1 << pin_type;
88
89         if (!do_pin_stats)
90                 return;
91         while (*node_ptr) {
92                 node = *node_ptr;
93                 if (addr == node->addr) {
94                         node->pin_types |= pin_type_bit;
95                         return;
96                 }
97                 if (addr < node->addr)
98                         node_ptr = &node->left;
99                 else
100                         node_ptr = &node->right;
101         }
102
103         node = (PinStatAddress *)sgen_alloc_internal_dynamic (sizeof (PinStatAddress), INTERNAL_MEM_STATISTICS, TRUE);
104         node->addr = addr;
105         node->pin_types = pin_type_bit;
106         node->left = node->right = NULL;
107
108         *node_ptr = node;
109 }
110
111 static void
112 pin_stats_count_object_from_tree (GCObject *object, size_t size, PinStatAddress *node, int *pin_types)
113 {
114         char *obj = (char*)object;
115         if (!node)
116                 return;
117         if (node->addr >= obj && node->addr < obj + size) {
118                 int i;
119                 for (i = 0; i < PIN_TYPE_MAX; ++i) {
120                         int pin_bit = 1 << i;
121                         if (!(*pin_types & pin_bit) && (node->pin_types & pin_bit)) {
122                                 pinned_byte_counts [i] += size;
123                                 *pin_types |= pin_bit;
124                         }
125                 }
126         }
127         if (obj < node->addr)
128                 pin_stats_count_object_from_tree (object, size, node->left, pin_types);
129         if (obj + size - 1 > node->addr)
130                 pin_stats_count_object_from_tree (object, size, node->right, pin_types);
131 }
132
133 static gpointer
134 lookup_vtable_entry (SgenHashTable *hash_table, GCVTable vtable, gpointer empty_entry)
135 {
136         char *name = g_strdup_printf ("%s.%s", sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable));
137         gpointer entry = sgen_hash_table_lookup (hash_table, name);
138
139         if (entry) {
140                 g_free (name);
141         } else {
142                 sgen_hash_table_replace (hash_table, name, empty_entry, NULL);
143                 entry = sgen_hash_table_lookup (hash_table, name);
144         }
145
146         return entry;
147 }
148
149 static void
150 register_vtable (GCVTable vtable, int pin_types)
151 {
152         PinnedClassEntry empty_entry;
153         PinnedClassEntry *entry;
154         int i;
155
156         memset (&empty_entry, 0, sizeof (PinnedClassEntry));
157         entry = (PinnedClassEntry *)lookup_vtable_entry (&pinned_class_hash_table, vtable, &empty_entry);
158
159         for (i = 0; i < PIN_TYPE_MAX; ++i) {
160                 if (pin_types & (1 << i))
161                         ++entry->num_pins [i];
162         }
163 }
164
165 void
166 sgen_pin_stats_register_object (GCObject *obj, int generation)
167 {
168         int pin_types = 0;
169         size_t size = 0;
170
171         if (binary_protocol_is_enabled ()) {
172                 size = sgen_safe_object_get_size (obj);
173                 pinned_bytes_in_generation [generation] += size;
174                 ++pinned_objects_in_generation [generation];
175         }
176
177         if (!do_pin_stats)
178                 return;
179
180         if (!size)
181                 size = sgen_safe_object_get_size (obj);
182
183         pin_stats_count_object_from_tree (obj, size, pin_stat_addresses, &pin_types);
184         sgen_pointer_queue_add (&pinned_objects, obj);
185
186         if (pin_types)
187                 register_vtable (SGEN_LOAD_VTABLE (obj), pin_types);
188 }
189
190 void
191 sgen_pin_stats_register_global_remset (GCObject *obj)
192 {
193         GlobalRemsetClassEntry empty_entry;
194         GlobalRemsetClassEntry *entry;
195
196         if (!do_pin_stats)
197                 return;
198
199         memset (&empty_entry, 0, sizeof (GlobalRemsetClassEntry));
200         entry = (GlobalRemsetClassEntry *)lookup_vtable_entry (&global_remset_class_hash_table, SGEN_LOAD_VTABLE (obj), &empty_entry);
201
202         ++entry->num_remsets;
203 }
204
205 void
206 sgen_pin_stats_report (void)
207 {
208         char *name;
209         PinnedClassEntry *pinned_entry;
210         GlobalRemsetClassEntry *remset_entry;
211
212         binary_protocol_pin_stats (pinned_objects_in_generation [GENERATION_NURSERY], pinned_bytes_in_generation [GENERATION_NURSERY],
213                         pinned_objects_in_generation [GENERATION_OLD], pinned_bytes_in_generation [GENERATION_OLD]);
214
215         if (!do_pin_stats)
216                 return;
217
218         mono_gc_printf (gc_debug_file, "\n%-50s  %10s  %10s  %10s\n", "Class", "Stack", "Static", "Other");
219         SGEN_HASH_TABLE_FOREACH (&pinned_class_hash_table, char *, name, PinnedClassEntry *, pinned_entry) {
220                 int i;
221                 mono_gc_printf (gc_debug_file, "%-50s", name);
222                 for (i = 0; i < PIN_TYPE_MAX; ++i)
223                         mono_gc_printf (gc_debug_file, "  %10ld", pinned_entry->num_pins [i]);
224                 mono_gc_printf (gc_debug_file, "\n");
225         } SGEN_HASH_TABLE_FOREACH_END;
226
227         mono_gc_printf (gc_debug_file, "\n%-50s  %10s\n", "Class", "#Remsets");
228         SGEN_HASH_TABLE_FOREACH (&global_remset_class_hash_table, char *, name, GlobalRemsetClassEntry *, remset_entry) {
229                 mono_gc_printf (gc_debug_file, "%-50s  %10ld\n", name, remset_entry->num_remsets);
230         } SGEN_HASH_TABLE_FOREACH_END;
231
232         mono_gc_printf (gc_debug_file, "\nTotal bytes pinned from stack: %ld  static: %ld  other: %ld\n",
233                         pinned_byte_counts [PIN_TYPE_STACK],
234                         pinned_byte_counts [PIN_TYPE_STATIC_DATA],
235                         pinned_byte_counts [PIN_TYPE_OTHER]);
236 }
237
238 size_t
239 sgen_pin_stats_get_pinned_byte_count (int pin_type)
240 {
241         return pinned_byte_counts [pin_type];
242 }
243
244 SgenPointerQueue*
245 sgen_pin_stats_get_object_list (void)
246 {
247         return &pinned_objects;
248 }
249
250 #endif /* HAVE_SGEN_GC */