[sgen] Move time stuff to client code.
[mono.git] / mono / metadata / sgen-client-mono.h
1 /*
2  * sgen-client-mono.h: Mono's client definitions for SGen.
3  *
4  * Copyright (C) 2014 Xamarin Inc
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License 2.0 as published by the Free Software Foundation;
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License 2.0 along with this library; if not, write to the Free
17  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19
20 #ifdef SGEN_DEFINE_OBJECT_VTABLE
21
22 #include "utils/mono-threads.h"
23 #include "utils/mono-mmap.h"
24
25 typedef MonoObject GCObject;
26 typedef MonoVTable GCVTable;
27
28 /* FIXME: This should return a GCVTable* and be a function. */
29 #define SGEN_LOAD_VTABLE_UNCHECKED(obj) ((void*)(((GCObject*)(obj))->vtable))
30
31 static inline mword
32 sgen_vtable_get_descriptor (GCVTable *vtable)
33 {
34         return (mword)vtable->gc_descr;
35 }
36
37 static mword /*__attribute__((noinline)) not sure if this hint is a good idea*/
38 sgen_client_slow_object_get_size (GCVTable *vtable, GCObject* o)
39 {
40         MonoClass *klass = ((MonoVTable*)vtable)->klass;
41
42         /*
43          * We depend on mono_string_length_fast and
44          * mono_array_length_fast not using the object's vtable.
45          */
46         if (klass == mono_defaults.string_class) {
47                 return G_STRUCT_OFFSET (MonoString, chars) + 2 * mono_string_length_fast ((MonoString*) o) + 2;
48         } else if (klass->rank) {
49                 MonoArray *array = (MonoArray*)o;
50                 size_t size = sizeof (MonoArray) + klass->sizes.element_size * mono_array_length_fast (array);
51                 if (G_UNLIKELY (array->bounds)) {
52                         size += sizeof (mono_array_size_t) - 1;
53                         size &= ~(sizeof (mono_array_size_t) - 1);
54                         size += sizeof (MonoArrayBounds) * klass->rank;
55                 }
56                 return size;
57         } else {
58                 /* from a created object: the class must be inited already */
59                 return klass->instance_size;
60         }
61 }
62
63 /*
64  * This function can be called on an object whose first word, the
65  * vtable field, is not intact.  This is necessary for the parallel
66  * collector.
67  */
68 static MONO_NEVER_INLINE mword
69 sgen_client_par_object_get_size (GCVTable *vtable, GCObject* o)
70 {
71         mword descr = sgen_vtable_get_descriptor (vtable);
72         mword type = descr & DESC_TYPE_MASK;
73
74         if (type == DESC_TYPE_RUN_LENGTH || type == DESC_TYPE_SMALL_PTRFREE) {
75                 mword size = descr & 0xfff8;
76                 SGEN_ASSERT (9, size >= sizeof (MonoObject), "Run length object size to small");
77                 return size;
78         } else if (descr == SGEN_DESC_STRING) {
79                 return G_STRUCT_OFFSET (MonoString, chars) + 2 * mono_string_length_fast ((MonoString*) o) + 2;
80         } else if (type == DESC_TYPE_VECTOR) {
81                 int element_size = ((descr) >> VECTOR_ELSIZE_SHIFT) & MAX_ELEMENT_SIZE;
82                 MonoArray *array = (MonoArray*)o;
83                 size_t size = sizeof (MonoArray) + element_size * mono_array_length_fast (array);
84
85                 /*
86                  * Non-vector arrays with a single dimension whose lower bound is zero are
87                  * allocated without bounds.
88                  */
89                 if ((descr & VECTOR_KIND_ARRAY) && array->bounds) {
90                         size += sizeof (mono_array_size_t) - 1;
91                         size &= ~(sizeof (mono_array_size_t) - 1);
92                         size += sizeof (MonoArrayBounds) * ((MonoVTable*)vtable)->klass->rank;
93                 }
94                 return size;
95         }
96
97         return sgen_client_slow_object_get_size (vtable, o);
98 }
99
100 typedef struct _SgenClientThreadInfo SgenClientThreadInfo;
101 struct _SgenClientThreadInfo {
102         MonoThreadInfo info;
103
104         /*
105          * `skip` is set to TRUE when STW fails to suspend a thread, most probably because
106          * the underlying thread is dead.
107         */
108         gboolean skip, suspend_done;
109         volatile int in_critical_region;
110
111         gpointer stopped_ip;    /* only valid if the thread is stopped */
112         MonoDomain *stopped_domain; /* dsto */
113
114         /*
115         This is set the argument of mono_gc_set_skip_thread.
116
117         A thread that knowingly holds no managed state can call this
118         function around blocking loops to reduce the GC burden by not
119         been scanned.
120         */
121         gboolean gc_disabled;
122
123         gpointer runtime_data;
124
125         void *stack_end;
126         void *stack_start;
127         void *stack_start_limit;
128
129         /*FIXME pretty please finish killing ARCH_NUM_REGS */
130 #ifdef USE_MONO_CTX
131         MonoContext ctx;                /* ditto */
132 #else
133         gpointer regs[ARCH_NUM_REGS];       /* ditto */
134 #endif
135 };
136
137 #else
138
139 #include "metadata/profiler-private.h"
140 #include "utils/dtrace.h"
141 #include "utils/mono-counters.h"
142 #include "utils/mono-logger-internal.h"
143 #include "utils/mono-time.h"
144
145 extern void mono_sgen_register_moved_object (void *obj, void *destination);
146 extern void mono_sgen_gc_event_moves (void);
147
148 extern void mono_sgen_init_stw (void);
149
150 enum {
151         INTERNAL_MEM_EPHEMERON_LINK = INTERNAL_MEM_FIRST_CLIENT,
152         INTERNAL_MEM_MAX
153 };
154
155 #define SGEN_CLIENT_OBJECT_HEADER_SIZE          (sizeof (GCObject))
156 #define SGEN_CLIENT_MINIMUM_OBJECT_SIZE         SGEN_CLIENT_OBJECT_HEADER_SIZE
157
158 static MONO_ALWAYS_INLINE size_t G_GNUC_UNUSED
159 sgen_client_array_element_size (GCVTable *gc_vtable)
160 {
161         MonoVTable *vt = (MonoVTable*)gc_vtable;
162         return mono_array_element_size (vt->klass);
163 }
164
165 static MONO_ALWAYS_INLINE G_GNUC_UNUSED char*
166 sgen_client_array_data_start (GCObject *obj)
167 {
168         return (char*)(obj) +  G_STRUCT_OFFSET (MonoArray, vector);
169 }
170
171 static MONO_ALWAYS_INLINE size_t G_GNUC_UNUSED
172 sgen_client_array_length (GCObject *obj)
173 {
174         return mono_array_length_fast ((MonoArray*)obj);
175 }
176
177 static MONO_ALWAYS_INLINE gboolean G_GNUC_UNUSED
178 sgen_client_object_is_array_fill (GCObject *o)
179 {
180         return ((MonoObject*)o)->synchronisation == GINT_TO_POINTER (-1);
181 }
182
183 /* FIXME: Why do we even need this?  Can't we get it from the descriptor? */
184 static gboolean G_GNUC_UNUSED
185 sgen_client_vtable_has_references (GCVTable *vt)
186 {
187         return ((MonoVTable*)vt)->klass->has_references;
188 }
189
190 static MONO_ALWAYS_INLINE void G_GNUC_UNUSED
191 sgen_client_pre_copy_checks (char *destination, GCVTable *gc_vtable, void *obj, mword objsize)
192 {
193         MonoVTable *vt = (MonoVTable*)gc_vtable;
194         SGEN_ASSERT (9, vt->klass->inited, "vtable %p for class %s:%s was not initialized", vt, vt->klass->name_space, vt->klass->name);
195 }
196
197 static MONO_ALWAYS_INLINE void G_GNUC_UNUSED
198 sgen_client_update_copied_object (char *destination, GCVTable *gc_vtable, void *obj, mword objsize)
199 {
200         MonoVTable *vt = (MonoVTable*)gc_vtable;
201         if (G_UNLIKELY (vt->rank && ((MonoArray*)obj)->bounds)) {
202                 MonoArray *array = (MonoArray*)destination;
203                 array->bounds = (MonoArrayBounds*)((char*)destination + ((char*)((MonoArray*)obj)->bounds - (char*)obj));
204                 SGEN_LOG (9, "Array instance %p: size: %lu, rank: %d, length: %lu", array, (unsigned long)objsize, vt->rank, (unsigned long)mono_array_length (array));
205         }
206
207         if (G_UNLIKELY (mono_profiler_events & MONO_PROFILE_GC_MOVES))
208                 mono_sgen_register_moved_object (obj, destination);
209 }
210
211 #ifdef XDOMAIN_CHECKS_IN_WBARRIER
212 extern gboolean sgen_mono_xdomain_checks;
213
214 #define sgen_client_wbarrier_generic_nostore_check(ptr) do {            \
215                 /* FIXME: ptr_in_heap must be called with the GC lock held */ \
216                 if (sgen_mono_xdomain_checks && *(MonoObject**)ptr && ptr_in_heap (ptr)) { \
217                         char *start = find_object_for_ptr (ptr);        \
218                         MonoObject *value = *(MonoObject**)ptr;         \
219                         LOCK_GC;                                        \
220                         SGEN_ASSERT (0, start, "Write barrier outside an object?"); \
221                         if (start) {                                    \
222                                 MonoObject *obj = (MonoObject*)start;   \
223                                 if (obj->vtable->domain != value->vtable->domain) \
224                                         SGEN_ASSERT (0, is_xdomain_ref_allowed (ptr, start, obj->vtable->domain), "Cross-domain ref not allowed"); \
225                         }                                               \
226                         UNLOCK_GC;                                      \
227                 }                                                       \
228         } while (0)
229 #else
230 #define sgen_client_wbarrier_generic_nostore_check(ptr)
231 #endif
232
233 static gboolean G_GNUC_UNUSED
234 sgen_client_object_has_critical_finalizer (GCObject *obj)
235 {
236         MonoClass *class;
237
238         if (!mono_defaults.critical_finalizer_object)
239                 return FALSE;
240
241         class = ((MonoVTable*)SGEN_LOAD_VTABLE (obj))->klass;
242
243         return mono_class_has_parent_fast (class, mono_defaults.critical_finalizer_object);
244 }
245
246 const char* sgen_client_vtable_get_namespace (GCVTable *vtable);
247 const char* sgen_client_vtable_get_name (GCVTable *vtable);
248
249 static void G_GNUC_UNUSED
250 sgen_client_binary_protocol_collection_requested (int generation, size_t requested_size, gboolean force)
251 {
252         MONO_GC_REQUESTED (generation, requested_size, force);
253 }
254
255 static void G_GNUC_UNUSED
256 sgen_client_binary_protocol_collection_begin (int minor_gc_count, int generation)
257 {
258         MONO_GC_BEGIN (generation);
259
260         mono_profiler_gc_event (MONO_GC_EVENT_START, generation);
261 }
262
263 static void G_GNUC_UNUSED
264 sgen_client_binary_protocol_collection_end (int minor_gc_count, int generation, long long num_objects_scanned, long long num_unique_objects_scanned)
265 {
266         MONO_GC_END (generation);
267
268         mono_profiler_gc_event (MONO_GC_EVENT_END, generation);
269 }
270
271 static void G_GNUC_UNUSED
272 sgen_client_binary_protocol_concurrent_start (void)
273 {
274         MONO_GC_CONCURRENT_START_BEGIN (GENERATION_OLD);
275 }
276
277 static void G_GNUC_UNUSED
278 sgen_client_binary_protocol_concurrent_update (void)
279 {
280         MONO_GC_CONCURRENT_UPDATE_FINISH_BEGIN (GENERATION_OLD, sgen_get_major_collector ()->get_and_reset_num_major_objects_marked ());
281 }
282
283 static void G_GNUC_UNUSED
284 sgen_client_binary_protocol_concurrent_finish (void)
285 {
286         MONO_GC_CONCURRENT_UPDATE_FINISH_BEGIN (GENERATION_OLD, sgen_get_major_collector ()->get_and_reset_num_major_objects_marked ());
287 }
288
289 static void G_GNUC_UNUSED
290 sgen_client_binary_protocol_sweep_begin (int generation, int full_sweep)
291 {
292         MONO_GC_SWEEP_BEGIN (generation, full_sweep);
293 }
294
295 static void G_GNUC_UNUSED
296 sgen_client_binary_protocol_sweep_end (int generation, int full_sweep)
297 {
298         MONO_GC_SWEEP_END (generation, full_sweep);
299 }
300
301 static void G_GNUC_UNUSED
302 sgen_client_binary_protocol_world_stopping (int generation, long long timestamp)
303 {
304         MONO_GC_WORLD_STOP_BEGIN ();
305
306         mono_profiler_gc_event (MONO_GC_EVENT_PRE_STOP_WORLD, generation);
307 }
308
309 static void G_GNUC_UNUSED
310 sgen_client_binary_protocol_world_stopped (int generation, long long timestamp, long long total_major_cards, long long marked_major_cards, long long total_los_cards, long long marked_los_cards)
311 {
312         MONO_GC_WORLD_STOP_END ();
313
314         mono_profiler_gc_event (MONO_GC_EVENT_POST_STOP_WORLD, generation);
315 }
316
317 static void G_GNUC_UNUSED
318 sgen_client_binary_protocol_world_restarting (int generation, long long timestamp, long long total_major_cards, long long marked_major_cards, long long total_los_cards, long long marked_los_cards)
319 {
320         MONO_GC_WORLD_RESTART_BEGIN (generation);
321
322         mono_profiler_gc_event (MONO_GC_EVENT_PRE_START_WORLD, generation);
323 }
324
325 static void G_GNUC_UNUSED
326 sgen_client_binary_protocol_world_restarted (int generation, long long timestamp)
327 {
328         MONO_GC_WORLD_RESTART_END (generation);
329
330         mono_profiler_gc_event (MONO_GC_EVENT_POST_START_WORLD, generation);
331 }
332
333 static void G_GNUC_UNUSED
334 sgen_client_binary_protocol_mark_start (int generation)
335 {
336         mono_profiler_gc_event (MONO_GC_EVENT_MARK_START, generation);
337 }
338
339 static void G_GNUC_UNUSED
340 sgen_client_binary_protocol_mark_end (int generation)
341 {
342         mono_profiler_gc_event (MONO_GC_EVENT_MARK_END, generation);
343 }
344
345 static void G_GNUC_UNUSED
346 sgen_client_binary_protocol_reclaim_start (int generation)
347 {
348         mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_START, generation);
349 }
350
351 static void G_GNUC_UNUSED
352 sgen_client_binary_protocol_reclaim_end (int generation)
353 {
354         mono_profiler_gc_event (MONO_GC_EVENT_RECLAIM_END, generation);
355 }
356
357 static void
358 mono_binary_protocol_alloc_generic (gpointer obj, gpointer vtable, size_t size, gboolean pinned)
359 {
360 #ifdef ENABLE_DTRACE
361         const char *namespace = sgen_client_vtable_get_namespace (vtable);
362         const char *name = sgen_client_vtable_get_name (vtable);
363
364         if (sgen_ptr_in_nursery (obj)) {
365                 if (G_UNLIKELY (MONO_GC_NURSERY_OBJ_ALLOC_ENABLED ()))
366                         MONO_GC_NURSERY_OBJ_ALLOC ((mword)obj, size, namespace, name);
367         } else {
368                 if (size > SGEN_MAX_SMALL_OBJ_SIZE) {
369                         if (G_UNLIKELY (MONO_GC_MAJOR_OBJ_ALLOC_LARGE_ENABLED ()))
370                                 MONO_GC_MAJOR_OBJ_ALLOC_LARGE ((mword)obj, size, namespace, name);
371                 } else if (pinned) {
372                         MONO_GC_MAJOR_OBJ_ALLOC_PINNED ((mword)obj, size, namespace, name);
373                 }
374         }
375 #endif
376 }
377
378 static void G_GNUC_UNUSED
379 sgen_client_binary_protocol_alloc (gpointer obj, gpointer vtable, size_t size)
380 {
381         mono_binary_protocol_alloc_generic (obj, vtable, size, FALSE);
382 }
383
384 static void G_GNUC_UNUSED
385 sgen_client_binary_protocol_alloc_pinned (gpointer obj, gpointer vtable, size_t size)
386 {
387         mono_binary_protocol_alloc_generic (obj, vtable, size, TRUE);
388 }
389
390 static void G_GNUC_UNUSED
391 sgen_client_binary_protocol_alloc_degraded (gpointer obj, gpointer vtable, size_t size)
392 {
393         MONO_GC_MAJOR_OBJ_ALLOC_DEGRADED ((mword)obj, size, sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable));
394 }
395
396 static void G_GNUC_UNUSED
397 sgen_client_binary_protocol_pin (gpointer obj, gpointer vtable, size_t size)
398 {
399 #ifdef ENABLE_DTRACE
400         if (G_UNLIKELY (MONO_GC_OBJ_PINNED_ENABLED ())) {
401                 int gen = sgen_ptr_in_nursery (obj) ? GENERATION_NURSERY : GENERATION_OLD;
402                 MONO_GC_OBJ_PINNED ((mword)obj,
403                                 sgen_safe_object_get_size (obj),
404                                 sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable), gen);
405         }
406 #endif
407 }
408
409 static void G_GNUC_UNUSED
410 sgen_client_binary_protocol_cement (gpointer ptr, gpointer vtable, size_t size)
411 {
412 #ifdef ENABLE_DTRACE
413         if (G_UNLIKELY (MONO_GC_OBJ_CEMENTED_ENABLED())) {
414                 MONO_GC_OBJ_CEMENTED ((mword)ptr, sgen_safe_object_get_size ((GCObject*)ptr),
415                                 sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable));
416         }
417 #endif
418 }
419
420 static void G_GNUC_UNUSED
421 sgen_client_binary_protocol_copy (gpointer from, gpointer to, gpointer vtable, size_t size)
422 {
423 #ifdef ENABLE_DTRACE
424         if (G_UNLIKELY (MONO_GC_OBJ_MOVED_ENABLED ())) {
425                 int dest_gen = sgen_ptr_in_nursery (to) ? GENERATION_NURSERY : GENERATION_OLD;
426                 int src_gen = sgen_ptr_in_nursery (from) ? GENERATION_NURSERY : GENERATION_OLD;
427                 MONO_GC_OBJ_MOVED ((mword)to, (mword)from, dest_gen, src_gen, size, sgen_client_vtable_get_namespace (vtable), sgen_client_vtable_get_name (vtable));
428         }
429 #endif
430 }
431
432 static void G_GNUC_UNUSED
433 sgen_client_binary_protocol_global_remset (gpointer ptr, gpointer value, gpointer value_vtable)
434 {
435 #ifdef ENABLE_DTRACE
436         if (G_UNLIKELY (MONO_GC_GLOBAL_REMSET_ADD_ENABLED ())) {
437                 MONO_GC_GLOBAL_REMSET_ADD ((mword)ptr, (mword)value, sgen_safe_object_get_size (value),
438                                 sgen_client_vtable_get_namespace (value_vtable), sgen_client_vtable_get_name (value_vtable));
439         }
440 #endif
441 }
442
443 static void G_GNUC_UNUSED
444 sgen_client_binary_protocol_dislink_update (gpointer link, gpointer obj, gboolean track, gboolean staged)
445 {
446 #ifdef ENABLE_DTRACE
447         if (MONO_GC_WEAK_UPDATE_ENABLED ()) {
448                 GCVTable *vt = obj ? (GCVTable*)SGEN_LOAD_VTABLE (obj) : NULL;
449                 MONO_GC_WEAK_UPDATE ((mword)link,
450                                 (mword)obj,
451                                 obj ? (mword)sgen_safe_object_get_size (obj) : (mword)0,
452                                 obj ? sgen_client_vtable_get_namespace (vt) : NULL,
453                                 obj ? sgen_client_vtable_get_name (vt) : NULL,
454                                 track ? 1 : 0);
455         }
456 #endif
457 }
458
459 static void G_GNUC_UNUSED
460 sgen_client_binary_protocol_empty (gpointer start, size_t size)
461 {
462         if (sgen_ptr_in_nursery (start))
463                 MONO_GC_NURSERY_SWEPT ((mword)start, size);
464         else
465                 MONO_GC_MAJOR_SWEPT ((mword)start, size);
466 }
467
468 static void G_GNUC_UNUSED
469 sgen_client_binary_protocol_thread_suspend (gpointer thread, gpointer stopped_ip)
470 {
471 }
472
473 static void G_GNUC_UNUSED
474 sgen_client_binary_protocol_thread_restart (gpointer thread)
475 {
476 }
477
478 static void G_GNUC_UNUSED
479 sgen_client_binary_protocol_thread_register (gpointer thread)
480 {
481 }
482
483 static void G_GNUC_UNUSED
484 sgen_client_binary_protocol_thread_unregister (gpointer thread)
485 {
486 }
487
488 static void G_GNUC_UNUSED
489 sgen_client_binary_protocol_missing_remset (gpointer obj, gpointer obj_vtable, int offset, gpointer value, gpointer value_vtable, gboolean value_pinned)
490 {
491 }
492
493 static void G_GNUC_UNUSED
494 sgen_client_binary_protocol_cement_reset (void)
495 {
496 }
497
498 static void G_GNUC_UNUSED
499 sgen_client_binary_protocol_domain_unload_begin (gpointer domain)
500 {
501 }
502
503 static void G_GNUC_UNUSED
504 sgen_client_binary_protocol_domain_unload_end (gpointer domain)
505 {
506 }
507
508 int sgen_thread_handshake (BOOL suspend);
509 gboolean sgen_suspend_thread (SgenThreadInfo *info);
510 gboolean sgen_resume_thread (SgenThreadInfo *info);
511 void sgen_wait_for_suspend_ack (int count);
512
513 #ifdef HAVE_KW_THREAD
514 #define TLAB_ACCESS_INIT
515 #define IN_CRITICAL_REGION sgen_thread_info->client_info.in_critical_region
516 #else
517 #define TLAB_ACCESS_INIT        SgenThreadInfo *__thread_info__ = mono_native_tls_get_value (thread_info_key)
518 #define IN_CRITICAL_REGION (__thread_info__->client_info.in_critical_region)
519 #endif
520
521 #ifndef DISABLE_CRITICAL_REGION
522
523 #ifdef HAVE_KW_THREAD
524 #define IN_CRITICAL_REGION sgen_thread_info->client_info.in_critical_region
525 #else
526 #define IN_CRITICAL_REGION (__thread_info__->client_info.in_critical_region)
527 #endif
528
529 /* Enter must be visible before anything is done in the critical region. */
530 #define ENTER_CRITICAL_REGION do { mono_atomic_store_acquire (&IN_CRITICAL_REGION, 1); } while (0)
531
532 /* Exit must make sure all critical regions stores are visible before it signal the end of the region. 
533  * We don't need to emit a full barrier since we
534  */
535 #define EXIT_CRITICAL_REGION  do { mono_atomic_store_release (&IN_CRITICAL_REGION, 0); } while (0)
536
537 #endif
538
539 #define SGEN_TV_DECLARE(name) gint64 name
540 #define SGEN_TV_GETTIME(tv) tv = mono_100ns_ticks ()
541 #define SGEN_TV_ELAPSED(start,end) (int)((end-start))
542
543 #endif