1 /**
2  * \file
3  * Handle to object in native code
4  *
5  * Authors:
6  *  - Ludovic Henry <ludovic@xamarin.com>
7  *  - Aleksey Klieger <aleksey.klieger@xamarin.com>
8  *  - Rodrigo Kumpera <kumpera@xamarin.com>
9  *
10  * Copyright 2016 Dot net foundation.
11  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
12  */
13 
14 #include <config.h>
15 #include <glib.h>
16 
17 #include <mono/metadata/handle.h>
18 #include <mono/metadata/object-internals.h>
19 #include <mono/metadata/gc-internals.h>
20 #include <mono/utils/atomic.h>
21 #include <mono/utils/mono-lazy-init.h>
22 #include <mono/utils/mono-threads.h>
23 #ifdef HAVE_BACKTRACE_SYMBOLS
24 #include <execinfo.h>
25 #endif
26 
27 /* TODO (missing pieces)
28 
29 Add counters for:
30 	number of stack marks
31 	stack marks per icall
32 	mix/max/avg size of stack marks
33 	handle stack wastage
34 
35 Actually do something in mono_handle_verify
36 
37 Shrink the handles stack in mono_handle_stack_scan
38 Properly report it to the profiler.
39 Add a boehm implementation
40 
41 TODO (things to explore):
42 
43 There's no convenient way to wrap the object allocation function.
44 Right now we do this:
45 	MonoCultureInfoHandle culture = MONO_HANDLE_NEW (MonoCultureInfo, mono_object_new_checked (domain, klass, &error));
46 
47 Maybe what we need is a round of cleanup around all exposed types in the runtime to unify all helpers under the same hoof.
48 Combine: MonoDefaults, GENERATE_GET_CLASS_WITH_CACHE, TYPED_HANDLE_DECL and friends.
49 	This would solve the age old issue of making it clear which types are optional and tell that to the linker.
50 	We could then generate neat type safe wrappers.
51 */
52 
53 /*
54  * NOTE: Async suspend
55  *
56  * If we are running with cooperative GC, all the handle stack
57  * manipulation will complete before a GC thread scans the handle
58  * stack. If we are using async suspend, however, a thread may be
59  * trying to allocate a new handle, or unwind the handle stack when
60  * the GC stops the world.
61  *
62  * In particular, we need to ensure that if the mutator thread is
63  * suspended while manipulating the handle stack, the stack is in a
64  * good enough state to be scanned.  In particular, the size of each
65  * chunk should be updated before an object is written into the
66  * handle, and chunks to be scanned (between bottom and top) should
67  * always be valid.
68  *
69  * Note that the handle stack is scanned PRECISELY (see
70  * sgen_client_scan_thread_data ()).  That means there should not be
71  * stale objects scanned.  So when we manipulate the size of a chunk,
72  * wemust ensure that the newly scannable slot is either null or
73  * points to a valid value.
74  */
75 
76 static HandleStack*
new_handle_stack(void)77 new_handle_stack (void)
78 {
79 	return g_new (HandleStack, 1);
80 }
81 
82 static void
free_handle_stack(HandleStack * stack)83 free_handle_stack (HandleStack *stack)
84 {
85 	g_free (stack);
86 }
87 
88 static HandleChunk*
new_handle_chunk(void)89 new_handle_chunk (void)
90 {
91 	return g_new (HandleChunk, 1);
92 }
93 
94 static void
free_handle_chunk(HandleChunk * chunk)95 free_handle_chunk (HandleChunk *chunk)
96 {
97 	g_free (chunk);
98 }
99 
100 const MonoObjectHandle mono_null_value_handle = NULL;
101 
102 #define THIS_IS_AN_OK_NUMBER_OF_HANDLES 100
103 
104 static HandleChunkElem*
chunk_element(HandleChunk * chunk,int idx)105 chunk_element (HandleChunk *chunk, int idx)
106 {
107 	return &chunk->elems[idx];
108 }
109 
110 static HandleChunkElem*
handle_to_chunk_element(MonoObjectHandle o)111 handle_to_chunk_element (MonoObjectHandle o)
112 {
113 	return (HandleChunkElem*)o;
114 }
115 
116 /* Given a HandleChunkElem* search through the current handle stack to find its chunk and offset. */
117 static HandleChunk*
chunk_element_to_chunk_idx(HandleStack * stack,HandleChunkElem * elem,int * out_idx)118 chunk_element_to_chunk_idx (HandleStack *stack, HandleChunkElem *elem, int *out_idx)
119 {
120 	HandleChunk *top = stack->top;
121 	HandleChunk *cur = stack->bottom;
122 
123 	*out_idx = 0;
124 
125 	while (cur != NULL) {
126 		HandleChunkElem *front = &cur->elems [0];
127 		HandleChunkElem *back = &cur->elems [cur->size];
128 
129 		if (front <= elem && elem < back) {
130 			*out_idx = (int)(elem - front);
131 			return cur;
132 		}
133 
134 		if (cur == top)
135 			break; /* didn't find it. */
136 		cur = cur->next;
137 	}
138 	return NULL;
139 }
140 
141 #ifdef MONO_HANDLE_TRACK_OWNER
142 #ifdef HAVE_BACKTRACE_SYMBOLS
143 #define SET_BACKTRACE(btaddrs) do {					\
144 	backtrace(btaddrs, 7);						\
145 	} while (0)
146 #else
147 #define SET_BACKTRACE(btaddrs) 0
148 #endif
149 #define SET_OWNER(chunk,idx) do { (chunk)->elems[(idx)].owner = owner; SET_BACKTRACE (&((chunk)->elems[(idx)].backtrace_ips[0])); } while (0)
150 #else
151 #define SET_OWNER(chunk,idx) do { } while (0)
152 #endif
153 
154 #ifdef MONO_HANDLE_TRACK_SP
155 #define SET_SP(handles,chunk,idx) do { (chunk)->elems[(idx)].alloc_sp = handles->stackmark_sp; } while (0)
156 #else
157 #define SET_SP(handles,chunk,idx) do { } while (0)
158 #endif
159 
160 #ifdef MONO_HANDLE_TRACK_SP
161 void
mono_handle_chunk_leak_check(HandleStack * handles)162 mono_handle_chunk_leak_check (HandleStack *handles) {
163 	if (handles->stackmark_sp) {
164 		/* walk back from the top to the topmost non-empty chunk */
165 		HandleChunk *c = handles->top;
166 		while (c && c->size <= 0 && c != handles->bottom) {
167 			c = c->prev;
168 		}
169 		if (c == NULL || c->size == 0)
170 			return;
171 		g_assert (c && c->size > 0);
172 		HandleChunkElem *e = chunk_element (c, c->size - 1);
173 		if (e->alloc_sp < handles->stackmark_sp) {
174 			/* If we get here, the topmost object on the handle stack was
175 			 * allocated from a function that is deeper in the call stack than
176 			 * the most recent HANDLE_FUNCTION_ENTER.  That means it was
177 			 * probably not wrapped in a HANDLE_FUNCTION_ENTER/_RETURN pair
178 			 * and will never be reclaimed. */
179 			g_warning ("Handle %p (object = %p) (allocated from \"%s\") is leaking.\n", e, e->o,
180 #ifdef MONO_HANDLE_TRACK_OWNER
181 				   e->owner
182 #else
183 				   "<unknown owner>"
184 #endif
185 				);
186 		}
187 	}
188 }
189 #endif
190 
191 /* Actual handles implementation */
192 MonoRawHandle
193 #ifndef MONO_HANDLE_TRACK_OWNER
mono_handle_new(MonoObject * obj)194 mono_handle_new (MonoObject *obj)
195 #else
196 mono_handle_new (MonoObject *obj, const char *owner)
197 #endif
198 {
199 	MonoThreadInfo *info = mono_thread_info_current ();
200 	HandleStack *handles = (HandleStack *)info->handle_stack;
201 	HandleChunk *top = handles->top;
202 #ifdef MONO_HANDLE_TRACK_SP
203 	mono_handle_chunk_leak_check (handles);
204 #endif
205 
206 retry:
207 	if (G_LIKELY (top->size < OBJECTS_PER_HANDLES_CHUNK)) {
208 		int idx = top->size;
209 		gpointer* objslot = &top->elems [idx].o;
210 		/* can be interrupted anywhere here, so:
211 		 * 1. make sure the new slot is null
212 		 * 2. make the new slot scannable (increment size)
213 		 * 3. put a valid object in there
214 		 *
215 		 * (have to do 1 then 3 so that if we're interrupted
216 		 * between 1 and 2, the object is still live)
217 		 */
218 		*objslot = NULL;
219 		SET_OWNER (top,idx);
220 		SET_SP (handles, top, idx);
221 		mono_memory_write_barrier ();
222 		top->size++;
223 		mono_memory_write_barrier ();
224 		*objslot = obj;
225 		return objslot;
226 	}
227 	if (G_LIKELY (top->next)) {
228 		top->next->size = 0;
229 		/* make sure size == 0 is visible to a GC thread before it sees the new top */
230 		mono_memory_write_barrier ();
231 		top = top->next;
232 		handles->top = top;
233 		goto retry;
234 	}
235 	HandleChunk *new_chunk = new_handle_chunk ();
236 	new_chunk->size = 0;
237 	new_chunk->prev = top;
238 	new_chunk->next = NULL;
239 	/* make sure size == 0 before new chunk is visible */
240 	mono_memory_write_barrier ();
241 	top->next = new_chunk;
242 	handles->top = new_chunk;
243 	goto retry;
244 }
245 
246 MonoRawHandle
247 #ifndef MONO_HANDLE_TRACK_OWNER
mono_handle_new_interior(gpointer rawptr)248 mono_handle_new_interior (gpointer rawptr)
249 #else
250 mono_handle_new_interior (gpointer rawptr, const char *owner)
251 #endif
252 {
253 	MonoThreadInfo *info = mono_thread_info_current ();
254 	HandleStack *handles = (HandleStack *)info->handle_stack;
255 	HandleChunk *top = handles->interior;
256 #ifdef MONO_HANDLE_TRACK_SP
257 	mono_handle_chunk_leak_check (handles);
258 #endif
259 
260 	g_assert (top);
261 
262 	/*
263 	 * Don't extend the chunk now, interior handles are
264 	 * only used for icall arguments, they shouldn't
265 	 * overflow.
266 	 */
267 	g_assert (top->size < OBJECTS_PER_HANDLES_CHUNK);
268 	int idx = top->size;
269 	gpointer *objslot = &top->elems [idx].o;
270 	*objslot = NULL;
271 	mono_memory_write_barrier ();
272 	top->size++;
273 	mono_memory_write_barrier ();
274 	*objslot = rawptr;
275 	SET_OWNER (top,idx);
276 	SET_SP (handles, top, idx);
277 	return objslot;
278 }
279 
280 HandleStack*
mono_handle_stack_alloc(void)281 mono_handle_stack_alloc (void)
282 {
283 	HandleStack *stack = new_handle_stack ();
284 	HandleChunk *chunk = new_handle_chunk ();
285 	HandleChunk *interior = new_handle_chunk ();
286 
287 	chunk->prev = chunk->next = NULL;
288 	chunk->size = 0;
289 	interior->prev = interior->next = NULL;
290 	interior->size = 0;
291 	mono_memory_write_barrier ();
292 	stack->top = stack->bottom = chunk;
293 	stack->interior = interior;
294 #ifdef MONO_HANDLE_TRACK_SP
295 	stack->stackmark_sp = NULL;
296 #endif
297 	return stack;
298 }
299 
300 void
mono_handle_stack_free(HandleStack * stack)301 mono_handle_stack_free (HandleStack *stack)
302 {
303 	if (!stack)
304 		return;
305 	HandleChunk *c = stack->bottom;
306 	stack->top = stack->bottom = NULL;
307 	mono_memory_write_barrier ();
308 	while (c) {
309 		HandleChunk *next = c->next;
310 		free_handle_chunk (c);
311 		c = next;
312 	}
313 	free_handle_chunk (c);
314 	free_handle_chunk (stack->interior);
315 	free_handle_stack (stack);
316 }
317 
318 void
mono_handle_stack_free_domain(HandleStack * stack,MonoDomain * domain)319 mono_handle_stack_free_domain (HandleStack *stack, MonoDomain *domain)
320 {
321 	/* Called by the GC while clearing out objects of the given domain from the heap. */
322 	/* If there are no handles-related bugs, there is nothing to do: if a
323 	 * thread accessed objects from the domain it was aborted, so any
324 	 * threads left alive cannot have any handles that point into the
325 	 * unloading domain.  However if there is a handle leak, the handle stack is not */
326 	if (!stack)
327 		return;
328 	/* Root domain only unloaded when mono is shutting down, don't need to check anything */
329 	if (domain == mono_get_root_domain () || mono_runtime_is_shutting_down ())
330 		return;
331 	HandleChunk *cur = stack->bottom;
332 	HandleChunk *last = stack->top;
333 	if (!cur)
334 		return;
335 	while (cur) {
336 		for (int idx = 0; idx < cur->size; ++idx) {
337 			HandleChunkElem *elem = &cur->elems[idx];
338 			if (!elem->o)
339 				continue;
340 			g_assert (mono_object_domain (elem->o) != domain);
341 		}
342 		if (cur == last)
343 			break;
344 		cur = cur->next;
345 	}
346 	/* We don't examine the interior pointers here because the GC treats
347 	 * them conservatively and anyway we don't have enough information here to
348 	 * find the object's vtable.
349 	 */
350 }
351 
352 static void
check_handle_stack_monotonic(HandleStack * stack)353 check_handle_stack_monotonic (HandleStack *stack)
354 {
355 	/* check that every allocated handle in the current handle stack is at no higher in the native stack than its predecessors */
356 #ifdef MONO_HANDLE_TRACK_SP
357 	HandleChunk *cur = stack->bottom;
358 	HandleChunk *last = stack->top;
359 	if (!cur)
360 		return;
361 	HandleChunkElem *prev = NULL;
362 	gboolean monotonic = TRUE;
363 	while (cur) {
364 		for (int i = 0;i < cur->size; ++i) {
365 			HandleChunkElem *elem = chunk_element (cur, i);
366 			if (prev && elem->alloc_sp > prev->alloc_sp) {
367 				monotonic = FALSE;
368 #ifdef MONO_HANDLE_TRACK_OWNER
369 				g_warning ("Handle %p (object %p) (allocated from \"%s\") was allocated deeper in the call stack than its successor Handle %p (object %p) (allocated from \"%s\").", prev, prev->o, prev->owner, elem, elem->o, elem->owner);
370 #else
371 				g_warning ("Handle %p (object %p) was allocated deeper in the call stack than its successor Handle %p (object %p).", prev, prev->o, elem, elem->o);
372 #endif
373 			}
374 			prev = elem;
375 		}
376 		if (cur == last)
377 			break;
378 		cur = cur->next;
379 	}
380 	g_assert (monotonic);
381 #endif
382 }
383 
384 void
mono_handle_stack_scan(HandleStack * stack,GcScanFunc func,gpointer gc_data,gboolean precise)385 mono_handle_stack_scan (HandleStack *stack, GcScanFunc func, gpointer gc_data, gboolean precise)
386 {
387 	if (precise) /* run just once (per handle stack) per GC */
388 		check_handle_stack_monotonic (stack);
389 	/*
390 	  We're called twice - on the imprecise pass we call func to pin the
391 	  objects where the handle points to its interior.  On the precise
392 	  pass, we scan all the objects where the handles point to the start of
393 	  the object.
394 
395 	  Note that if we're running, we know the world is stopped.
396 	*/
397 	if (precise) {
398 		HandleChunk *cur = stack->bottom;
399 		HandleChunk *last = stack->top;
400 
401 		while (cur) {
402 			for (int i = 0; i < cur->size; ++i) {
403 				HandleChunkElem* elem = chunk_element (cur, i);
404 				gpointer* obj_slot = &elem->o;
405 				if (*obj_slot != NULL)
406 					func (obj_slot, gc_data);
407 			}
408 			if (cur == last)
409 				break;
410 			cur = cur->next;
411 		}
412 	} else {
413 		HandleChunk *cur = stack->interior;
414 
415 		if (!cur)
416 			return;
417 		for (int i = 0; i < cur->size; ++i) {
418 			HandleChunkElem* elem = chunk_element (cur, i);
419 			gpointer* ptr_slot = &elem->o;
420 			if (*ptr_slot != NULL)
421 				func (ptr_slot, gc_data);
422 		}
423 	}
424 }
425 
426 void
mono_stack_mark_record_size(MonoThreadInfo * info,HandleStackMark * stackmark,const char * func_name)427 mono_stack_mark_record_size (MonoThreadInfo *info, HandleStackMark *stackmark, const char *func_name)
428 {
429 	HandleStack *handles = (HandleStack *)info->handle_stack;
430 	HandleChunk *cur = stackmark->chunk;
431 	int size = -stackmark->size; //discard the starting point of the stack
432 	while (cur) {
433 		size += cur->size;
434 		if (cur == handles->top)
435 			break;
436 		cur = cur->next;
437 	}
438 
439 	if (size > THIS_IS_AN_OK_NUMBER_OF_HANDLES)
440 		g_warning ("%s USED %d handles\n", func_name, size);
441 }
442 
443 /*
444  * Pop the stack until @stackmark and make @value the top value.
445  *
446  * @return the new handle for what @value points to
447  */
448 MonoRawHandle
mono_stack_mark_pop_value(MonoThreadInfo * info,HandleStackMark * stackmark,MonoRawHandle value)449 mono_stack_mark_pop_value (MonoThreadInfo *info, HandleStackMark *stackmark, MonoRawHandle value)
450 {
451 	MonoObject *obj = value ? *((MonoObject**)value) : NULL;
452 	mono_stack_mark_pop (info, stackmark);
453 #ifndef MONO_HANDLE_TRACK_OWNER
454 	return mono_handle_new (obj);
455 #else
456 	return mono_handle_new (obj, "<mono_stack_mark_pop_value>");
457 #endif
458 }
459 
460 /* Temporary place for some of the handle enabled wrapper functions*/
461 
462 MonoStringHandle
mono_string_new_handle(MonoDomain * domain,const char * data,MonoError * error)463 mono_string_new_handle (MonoDomain *domain, const char *data, MonoError *error)
464 {
465 	return MONO_HANDLE_NEW (MonoString, mono_string_new_checked (domain, data, error));
466 }
467 
468 MonoArrayHandle
mono_array_new_handle(MonoDomain * domain,MonoClass * eclass,uintptr_t n,MonoError * error)469 mono_array_new_handle (MonoDomain *domain, MonoClass *eclass, uintptr_t n, MonoError *error)
470 {
471 	return MONO_HANDLE_NEW (MonoArray, mono_array_new_checked (domain, eclass, n, error));
472 }
473 
474 MonoArrayHandle
mono_array_new_full_handle(MonoDomain * domain,MonoClass * array_class,uintptr_t * lengths,intptr_t * lower_bounds,MonoError * error)475 mono_array_new_full_handle (MonoDomain *domain, MonoClass *array_class, uintptr_t *lengths, intptr_t *lower_bounds, MonoError *error)
476 {
477 	return MONO_HANDLE_NEW (MonoArray, mono_array_new_full_checked (domain, array_class, lengths, lower_bounds, error));
478 }
479 
480 #ifdef ENABLE_CHECKED_BUILD
481 /* Checked build helpers */
482 void
mono_handle_verify(MonoRawHandle raw_handle)483 mono_handle_verify (MonoRawHandle raw_handle)
484 {
485 
486 }
487 #endif
488 
489 uintptr_t
mono_array_handle_length(MonoArrayHandle arr)490 mono_array_handle_length (MonoArrayHandle arr)
491 {
492 	MONO_REQ_GC_UNSAFE_MODE;
493 
494 	return MONO_HANDLE_RAW (arr)->max_length;
495 }
496 
497 uint32_t
mono_gchandle_from_handle(MonoObjectHandle handle,mono_bool pinned)498 mono_gchandle_from_handle (MonoObjectHandle handle, mono_bool pinned)
499 {
500 	/* FIXME: chunk_element_to_chunk_idx does a linear search through the
501 	 * chunks and we only need it for the assert */
502 	MonoThreadInfo *info = mono_thread_info_current ();
503 	HandleStack *stack = (HandleStack*) info->handle_stack;
504 	HandleChunkElem* elem = handle_to_chunk_element (handle);
505 	int elem_idx = 0;
506 	HandleChunk *chunk = chunk_element_to_chunk_idx (stack, elem, &elem_idx);
507 	/* gchandles cannot deal with interior pointers */
508 	g_assert (chunk != NULL);
509 	return mono_gchandle_new (MONO_HANDLE_RAW (handle), pinned);
510 }
511 
512 MonoObjectHandle
mono_gchandle_get_target_handle(uint32_t gchandle)513 mono_gchandle_get_target_handle (uint32_t gchandle)
514 {
515 	return MONO_HANDLE_NEW (MonoObject, mono_gchandle_get_target (gchandle));
516 }
517 
518 gpointer
mono_array_handle_pin_with_size(MonoArrayHandle handle,int size,uintptr_t idx,uint32_t * gchandle)519 mono_array_handle_pin_with_size (MonoArrayHandle handle, int size, uintptr_t idx, uint32_t *gchandle)
520 {
521 	g_assert (gchandle != NULL);
522 	*gchandle = mono_gchandle_from_handle (MONO_HANDLE_CAST(MonoObject,handle), TRUE);
523 	MonoArray *raw = MONO_HANDLE_RAW (handle);
524 	return mono_array_addr_with_size (raw, size, idx);
525 }
526 
527 gunichar2*
mono_string_handle_pin_chars(MonoStringHandle handle,uint32_t * gchandle)528 mono_string_handle_pin_chars (MonoStringHandle handle, uint32_t *gchandle)
529 {
530 	g_assert (gchandle != NULL);
531 	*gchandle = mono_gchandle_from_handle (MONO_HANDLE_CAST (MonoObject, handle), TRUE);
532 	MonoString *raw = MONO_HANDLE_RAW (handle);
533 	return mono_string_chars (raw);
534 }
535 
536 gpointer
mono_object_handle_pin_unbox(MonoObjectHandle obj,uint32_t * gchandle)537 mono_object_handle_pin_unbox (MonoObjectHandle obj, uint32_t *gchandle)
538 {
539 	g_assert (!MONO_HANDLE_IS_NULL (obj));
540 	MonoClass *klass = mono_handle_class (obj);
541 	g_assert (klass->valuetype);
542 	*gchandle = mono_gchandle_from_handle (obj, TRUE);
543 	return mono_object_unbox (MONO_HANDLE_RAW (obj));
544 }
545 
546 void
mono_array_handle_memcpy_refs(MonoArrayHandle dest,uintptr_t dest_idx,MonoArrayHandle src,uintptr_t src_idx,uintptr_t len)547 mono_array_handle_memcpy_refs (MonoArrayHandle dest, uintptr_t dest_idx, MonoArrayHandle src, uintptr_t src_idx, uintptr_t len)
548 {
549 	mono_array_memcpy_refs (MONO_HANDLE_RAW (dest), dest_idx, MONO_HANDLE_RAW (src), src_idx, len);
550 }
551 
552 gboolean
mono_handle_stack_is_empty(HandleStack * stack)553 mono_handle_stack_is_empty (HandleStack *stack)
554 {
555 	return (stack->top == stack->bottom && stack->top->size == 0);
556 }
557