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