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
sgen_pin_stats_enable(void)50 sgen_pin_stats_enable (void)
51 {
52 	do_pin_stats = TRUE;
53 }
54 
55 static void
pin_stats_tree_free(PinStatAddress * node)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
sgen_pin_stats_reset(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
sgen_pin_stats_register_address(char * addr,int pin_type)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
pin_stats_count_object_from_tree(GCObject * object,size_t size,PinStatAddress * node,int * pin_types)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
lookup_vtable_entry(SgenHashTable * hash_table,GCVTable vtable,gpointer empty_entry)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
register_vtable(GCVTable vtable,int pin_types)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
sgen_pin_stats_register_object(GCObject * obj,int generation)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
sgen_pin_stats_register_global_remset(GCObject * obj)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
sgen_pin_stats_report(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
sgen_pin_stats_get_pinned_byte_count(int pin_type)239 sgen_pin_stats_get_pinned_byte_count (int pin_type)
240 {
241 	return pinned_byte_counts [pin_type];
242 }
243 
244 SgenPointerQueue*
sgen_pin_stats_get_object_list(void)245 sgen_pin_stats_get_object_list (void)
246 {
247 	return &pinned_objects;
248 }
249 
250 #endif /* HAVE_SGEN_GC */
251