Merge pull request #201 from QuickJack/master
[mono.git] / mono / metadata / sgen-cardtable.c
1 /*
2  * sgen-cardtable.c: Card table implementation for sgen
3  *
4  * Author:
5  *      Rodrigo Kumpera (rkumpera@novell.com)
6  *
7  * SGen is licensed under the terms of the MIT X11 license
8  *
9  * Copyright 2001-2003 Ximian, Inc
10  * Copyright 2003-2010 Novell, Inc.
11  * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
12  * 
13  * Permission is hereby granted, free of charge, to any person obtaining
14  * a copy of this software and associated documentation files (the
15  * "Software"), to deal in the Software without restriction, including
16  * without limitation the rights to use, copy, modify, merge, publish,
17  * distribute, sublicense, and/or sell copies of the Software, and to
18  * permit persons to whom the Software is furnished to do so, subject to
19  * the following conditions:
20  * 
21  * The above copyright notice and this permission notice shall be
22  * included in all copies or substantial portions of the Software.
23  * 
24  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31  */
32
33 #ifdef SGEN_HAVE_CARDTABLE
34
35 //#define CARDTABLE_STATS
36
37 #include <unistd.h>
38 #ifdef HAVE_SYS_MMAN_H
39 #include <sys/mman.h>
40 #endif
41 #include <sys/types.h>
42
43 guint8 *sgen_cardtable;
44
45
46 #ifdef HEAVY_STATISTICS
47 long long marked_cards;
48 long long scanned_cards;
49 long long scanned_objects;
50 long long remarked_cards;
51
52 static long long los_marked_cards;
53 static long long large_objects;
54 static long long bloby_objects;
55 static long long los_array_cards;
56 static long long los_array_remsets;
57
58 #endif
59 static long long major_card_scan_time;
60 static long long los_card_scan_time;
61
62 static long long last_major_scan_time;
63 static long long last_los_scan_time;
64 /*WARNING: This function returns the number of cards regardless of overflow in case of overlapping cards.*/
65 static mword
66 cards_in_range (mword address, mword size)
67 {
68         mword end = address + MAX (1, size) - 1;
69         return (end >> CARD_BITS) - (address >> CARD_BITS) + 1;
70 }
71
72 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
73
74 guint8 *sgen_shadow_cardtable;
75
76 #define SGEN_SHADOW_CARDTABLE_END (sgen_shadow_cardtable + CARD_COUNT_IN_BYTES)
77 #define SGEN_CARDTABLE_END (sgen_cardtable + CARD_COUNT_IN_BYTES)
78
79 static gboolean
80 sgen_card_table_region_begin_scanning (mword start, mword end)
81 {
82         /*XXX this can be improved to work on words and have a single loop induction var */
83         while (start <= end) {
84                 if (sgen_card_table_card_begin_scanning (start))
85                         return TRUE;
86                 start += CARD_SIZE_IN_BYTES;
87         }
88         return FALSE;
89 }
90
91 #else
92
93 static gboolean
94 sgen_card_table_region_begin_scanning (mword start, mword size)
95 {
96         gboolean res = FALSE;
97         guint8 *card = sgen_card_table_get_card_address (start);
98         guint8 *end = card + cards_in_range (start, size);
99
100         /*XXX this can be improved to work on words and have a branchless body */
101         while (card != end) {
102                 if (*card++) {
103                         res = TRUE;
104                         break;
105                 }
106         }
107
108         memset (sgen_card_table_get_card_address (start), 0, size >> CARD_BITS);
109
110         return res;
111 }
112
113 #endif
114
115 /*FIXME this assumes that major blocks are multiple of 4K which is pretty reasonable */
116 gboolean
117 sgen_card_table_get_card_data (guint8 *data_dest, mword address, mword cards)
118 {
119         mword *start = (mword*)sgen_card_table_get_card_scan_address (address);
120         mword *dest = (mword*)data_dest;
121         mword *end = (mword*)(data_dest + cards);
122         mword mask = 0;
123
124         for (; dest < end; ++dest, ++start) {
125                 mword v = *start;
126                 *dest = v;
127                 mask |= v;
128
129 #ifndef SGEN_HAVE_OVERLAPPING_CARDS
130                 *start = 0;
131 #endif
132         }
133
134         return mask;
135 }
136
137 static gboolean
138 sgen_card_table_address_is_marked (mword address)
139 {
140         return *sgen_card_table_get_card_address (address) != 0;
141 }
142
143 void
144 sgen_card_table_mark_address (mword address)
145 {
146         *sgen_card_table_get_card_address (address) = 1;
147 }
148
149 void*
150 sgen_card_table_align_pointer (void *ptr)
151 {
152         return (void*)((mword)ptr & ~(CARD_SIZE_IN_BYTES - 1));
153 }
154
155 void
156 sgen_card_table_mark_range (mword address, mword size)
157 {
158         memset (sgen_card_table_get_card_address (address), 1, cards_in_range (address, size));
159 }
160
161 static gboolean
162 sgen_card_table_is_range_marked (guint8 *cards, mword address, mword size)
163 {
164         guint8 *end = cards + cards_in_range (address, size);
165
166         /*This is safe since this function is only called by code that only passes continuous card blocks*/
167         while (cards != end) {
168                 if (*cards++)
169                         return TRUE;
170         }
171         return FALSE;
172
173 }
174
175 static void
176 card_table_init (void)
177 {
178         sgen_cardtable = mono_sgen_alloc_os_memory (CARD_COUNT_IN_BYTES, TRUE);
179
180 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
181         sgen_shadow_cardtable = mono_sgen_alloc_os_memory (CARD_COUNT_IN_BYTES, TRUE);
182 #endif
183
184 #ifdef HEAVY_STATISTICS
185         mono_counters_register ("marked cards", MONO_COUNTER_GC | MONO_COUNTER_LONG, &marked_cards);
186         mono_counters_register ("scanned cards", MONO_COUNTER_GC | MONO_COUNTER_LONG, &scanned_cards);
187         mono_counters_register ("remarked cards", MONO_COUNTER_GC | MONO_COUNTER_LONG, &remarked_cards);
188
189         mono_counters_register ("los marked cards", MONO_COUNTER_GC | MONO_COUNTER_LONG, &los_marked_cards);
190         mono_counters_register ("los array cards scanned ", MONO_COUNTER_GC | MONO_COUNTER_LONG, &los_array_cards);
191         mono_counters_register ("los array remsets", MONO_COUNTER_GC | MONO_COUNTER_LONG, &los_array_remsets);
192         mono_counters_register ("cardtable scanned objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &scanned_objects);
193         mono_counters_register ("cardtable large objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &large_objects);
194         mono_counters_register ("cardtable bloby objects", MONO_COUNTER_GC | MONO_COUNTER_LONG, &bloby_objects);
195 #endif
196         mono_counters_register ("cardtable major scan time", MONO_COUNTER_GC | MONO_COUNTER_LONG, &major_card_scan_time);
197         mono_counters_register ("cardtable los scan time", MONO_COUNTER_GC | MONO_COUNTER_LONG, &los_card_scan_time);
198 }
199
200 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
201
202 static void
203 move_cards_to_shadow_table (mword start, mword size)
204 {
205         guint8 *from = sgen_card_table_get_card_address (start);
206         guint8 *to = sgen_card_table_get_shadow_card_address (start);
207         size_t bytes = cards_in_range (start, size);
208
209         if (to + bytes > SGEN_SHADOW_CARDTABLE_END) {
210                 size_t first_chunk = SGEN_SHADOW_CARDTABLE_END - to;
211                 size_t second_chunk = MIN (CARD_COUNT_IN_BYTES, bytes) - first_chunk;
212
213                 memcpy (to, from, first_chunk);
214                 memcpy (sgen_shadow_cardtable, sgen_cardtable, second_chunk);
215         } else {
216                 memcpy (to, from, bytes);
217         }
218 }
219
220 static void
221 clear_cards (mword start, mword size)
222 {
223         guint8 *addr = sgen_card_table_get_card_address (start);
224         size_t bytes = cards_in_range (start, size);
225
226         if (addr + bytes > SGEN_CARDTABLE_END) {
227                 size_t first_chunk = SGEN_CARDTABLE_END - addr;
228
229                 memset (addr, 0, first_chunk);
230                 memset (sgen_cardtable, 0, bytes - first_chunk);
231         } else {
232                 memset (addr, 0, bytes);
233         }
234 }
235
236
237 #else
238
239 static void
240 clear_cards (mword start, mword size)
241 {
242         memset (sgen_card_table_get_card_address (start), 0, cards_in_range (start, size));
243 }
244
245
246 #endif
247
248 static void
249 card_table_clear (void)
250 {
251         /*XXX we could do this in 2 ways. using mincore or iterating over all sections/los objects */
252         if (use_cardtable) {
253                 major_collector.iterate_live_block_ranges (clear_cards);
254                 mono_sgen_los_iterate_live_block_ranges (clear_cards);
255         }
256 }
257 static void
258 scan_from_card_tables (void *start_nursery, void *end_nursery, GrayQueue *queue)
259 {
260         if (use_cardtable) {
261                 TV_DECLARE (atv);
262                 TV_DECLARE (btv);
263
264 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
265         /*FIXME we should have a bit on each block/los object telling if the object have marked cards.*/
266         /*First we copy*/
267         major_collector.iterate_live_block_ranges (move_cards_to_shadow_table);
268         mono_sgen_los_iterate_live_block_ranges (move_cards_to_shadow_table);
269
270         /*Then we clear*/
271         card_table_clear ();
272 #endif
273                 TV_GETTIME (atv);
274                 major_collector.scan_card_table (queue);
275                 TV_GETTIME (btv);
276                 last_major_scan_time = TV_ELAPSED_MS (atv, btv); 
277                 major_card_scan_time += last_major_scan_time;
278                 mono_sgen_los_scan_card_table (queue);
279                 TV_GETTIME (atv);
280                 last_los_scan_time = TV_ELAPSED_MS (btv, atv);
281                 los_card_scan_time += last_los_scan_time;
282         }
283 }
284
285 guint8*
286 mono_gc_get_card_table (int *shift_bits, gpointer *mask)
287 {
288         if (!use_cardtable)
289                 return NULL;
290
291         g_assert (sgen_cardtable);
292         *shift_bits = CARD_BITS;
293 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
294         *mask = (gpointer)CARD_MASK;
295 #else
296         *mask = NULL;
297 #endif
298
299         return sgen_cardtable;
300 }
301
302 #if 0
303 static void
304 collect_faulted_cards (void)
305 {
306 #define CARD_PAGES (CARD_COUNT_IN_BYTES / 4096)
307         int i, count = 0;
308         unsigned char faulted [CARD_PAGES] = { 0 };
309         mincore (sgen_cardtable, CARD_COUNT_IN_BYTES, faulted);
310
311         for (i = 0; i < CARD_PAGES; ++i) {
312                 if (faulted [i])
313                         ++count;
314         }
315
316         printf ("TOTAL card pages %d faulted %d\n", CARD_PAGES, count);
317 }
318
319 void
320 sgen_card_table_dump_obj_card (char *object, size_t size, void *dummy)
321 {
322         guint8 *start = sgen_card_table_get_card_scan_address (object);
323         guint8 *end = start + cards_in_range (object, size);
324         int cnt = 0;
325         printf ("--obj %p %d cards [%p %p]--", object, size, start, end);
326         for (; start < end; ++start) {
327                 if (cnt == 0)
328                         printf ("\n\t[%p] ", start);
329                 printf ("%x ", *start);
330                 ++cnt;
331                 if (cnt == 8)
332                         cnt = 0;
333         }
334         printf ("\n");
335 }
336 #endif
337
338 #define MWORD_MASK (sizeof (mword) - 1)
339
340 static inline int
341 find_card_offset (mword card)
342 {
343 /*XXX Use assembly as this generates some pretty bad code */
344 #if defined(__i386__) && defined(__GNUC__)
345         return  (__builtin_ffs (card) - 1) / 8;
346 #elif defined(__x86_64__) && defined(__GNUC__)
347         return (__builtin_ffsll (card) - 1) / 8;
348 #elif defined(__s390x__)
349         return (__builtin_ffsll (GUINT64_TO_LE(card)) - 1) / 8;
350 #else
351         int i;
352         guint8 *ptr = (guint *) &card;
353         for (i = 0; i < sizeof (mword); ++i) {
354                 if (ptr[i])
355                         return i;
356         }
357         return 0;
358 #endif
359 }
360
361 static guint8*
362 find_next_card (guint8 *card_data, guint8 *end)
363 {
364         mword *cards, *cards_end;
365         mword card;
366
367         while ((((mword)card_data) & MWORD_MASK) && card_data < end) {
368                 if (*card_data)
369                         return card_data;
370                 ++card_data;
371         }
372
373         if (card_data == end)
374                 return end;
375
376         cards = (mword*)card_data;
377         cards_end = (mword*)((mword)end & ~MWORD_MASK);
378         while (cards < cards_end) {
379                 card = *cards;
380                 if (card)
381                         return (guint8*)cards + find_card_offset (card);
382                 ++cards;
383         }
384
385         card_data = (guint8*)cards_end;
386         while (card_data < end) {
387                 if (*card_data)
388                         return card_data;
389                 ++card_data;
390         }
391
392         return end;
393 }
394
395 void
396 sgen_cardtable_scan_object (char *obj, mword block_obj_size, guint8 *cards, SgenGrayQueue *queue)
397 {
398         MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (obj);
399         MonoClass *klass = vt->klass;
400         CopyOrMarkObjectFunc copy_func = mono_sgen_get_copy_object ();
401         ScanObjectFunc scan_object_func = mono_sgen_get_minor_scan_object ();
402         ScanVTypeFunc scan_vtype_func = mono_sgen_get_minor_scan_vtype ();
403
404         HEAVY_STAT (++large_objects);
405
406         if (!SGEN_VTABLE_HAS_REFERENCES (vt))
407                 return;
408
409         if (vt->rank) {
410                 guint8 *card_data, *card_base;
411                 guint8 *card_data_end;
412                 char *obj_start = sgen_card_table_align_pointer (obj);
413                 mword obj_size = mono_sgen_par_object_get_size (vt, (MonoObject*)obj);
414                 char *obj_end = obj + obj_size;
415                 size_t card_count;
416                 int extra_idx = 0;
417
418                 MonoArray *arr = (MonoArray*)obj;
419                 mword desc = (mword)klass->element_class->gc_descr;
420                 int elem_size = mono_array_element_size (klass);
421
422 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
423                 guint8 *overflow_scan_end = NULL;
424 #endif
425
426                 if (cards)
427                         card_data = cards;
428                 else
429                         card_data = sgen_card_table_get_card_scan_address ((mword)obj);
430
431                 card_base = card_data;
432                 card_count = cards_in_range ((mword)obj, obj_size);
433                 card_data_end = card_data + card_count;
434
435
436 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
437                 /*Check for overflow and if so, setup to scan in two steps*/
438                 if (!cards && card_data_end >= SGEN_SHADOW_CARDTABLE_END) {
439                         overflow_scan_end = sgen_shadow_cardtable + (card_data_end - SGEN_SHADOW_CARDTABLE_END);
440                         card_data_end = SGEN_SHADOW_CARDTABLE_END;
441                 }
442
443 LOOP_HEAD:
444 #endif
445
446                 card_data = find_next_card (card_data, card_data_end);
447                 for (; card_data < card_data_end; card_data = find_next_card (card_data + 1, card_data_end)) {
448                         int index;
449                         int idx = (card_data - card_base) + extra_idx;
450                         char *start = (char*)(obj_start + idx * CARD_SIZE_IN_BYTES);
451                         char *card_end = start + CARD_SIZE_IN_BYTES;
452                         char *elem;
453
454                         HEAVY_STAT (++los_marked_cards);
455
456                         if (!cards)
457                                 sgen_card_table_prepare_card_for_scanning (card_data);
458
459                         card_end = MIN (card_end, obj_end);
460
461                         if (start <= (char*)arr->vector)
462                                 index = 0;
463                         else
464                                 index = ARRAY_OBJ_INDEX (start, obj, elem_size);
465
466                         elem = (char*)mono_array_addr_with_size ((MonoArray*)obj, elem_size, index);
467                         if (klass->element_class->valuetype) {
468                                 for (; elem < card_end; elem += elem_size)
469                                         scan_vtype_func (elem, desc, queue);
470                         } else {
471                                 HEAVY_STAT (++los_array_cards);
472                                 for (; elem < card_end; elem += SIZEOF_VOID_P) {
473                                         gpointer new, old = *(gpointer*)elem;
474                                         if (G_UNLIKELY (ptr_in_nursery (old))) {
475                                                 HEAVY_STAT (++los_array_remsets);
476                                                 copy_func ((void**)elem, queue);
477                                                 new = *(gpointer*)elem;
478                                                 if (G_UNLIKELY (ptr_in_nursery (new)))
479                                                         mono_sgen_add_to_global_remset (elem);
480                                         }
481                                 }
482                         }
483                 }
484
485 #ifdef SGEN_HAVE_OVERLAPPING_CARDS
486                 if (overflow_scan_end) {
487                         extra_idx = card_data - card_base;
488                         card_base = card_data = sgen_shadow_cardtable;
489                         card_data_end = overflow_scan_end;
490                         overflow_scan_end = NULL;
491                         goto LOOP_HEAD;
492                 }
493 #endif
494
495         } else {
496                 HEAVY_STAT (++bloby_objects);
497                 if (cards) {
498                         if (sgen_card_table_is_range_marked (cards, (mword)obj, block_obj_size))
499                                 scan_object_func (obj, queue);
500                 } else if (sgen_card_table_region_begin_scanning ((mword)obj, block_obj_size)) {
501                         scan_object_func (obj, queue);
502                 }
503         }
504 }
505
506 #ifdef CARDTABLE_STATS
507
508 typedef struct {
509         int total, marked, remarked;    
510 } card_stats;
511
512 static card_stats major_stats, los_stats;
513 static card_stats *cur_stats;
514
515 static void
516 count_marked_cards (mword start, mword size)
517 {
518         mword end = start + size;
519         while (start <= end) {
520                 ++cur_stats->total;
521                 if (sgen_card_table_address_is_marked (start))
522                         ++cur_stats->marked;
523                 start += CARD_SIZE_IN_BYTES;
524         }
525 }
526
527 static void
528 count_remarked_cards (mword start, mword size)
529 {
530         mword end = start + size;
531         while (start <= end) {
532                 if (sgen_card_table_address_is_marked (start))
533                         ++cur_stats->remarked;
534                 start += CARD_SIZE_IN_BYTES;
535         }
536 }
537
538 #endif
539
540 static void
541 card_tables_collect_stats (gboolean begin)
542 {
543 #ifdef CARDTABLE_STATS
544         if (begin) {
545                 memset (&major_stats, 0, sizeof (card_stats));
546                 memset (&los_stats, 0, sizeof (card_stats));
547                 cur_stats = &major_stats;
548                 major_collector.iterate_live_block_ranges (count_marked_cards);
549                 cur_stats = &los_stats;
550                 mono_sgen_los_iterate_live_block_ranges (count_marked_cards);
551         } else {
552                 cur_stats = &major_stats;
553                 major_collector.iterate_live_block_ranges (count_marked_cards);
554                 cur_stats = &los_stats;
555                 mono_sgen_los_iterate_live_block_ranges (count_remarked_cards);
556                 printf ("cards major (t %d m %d r %d)  los (t %d m %d r %d) major_scan %lld los_scan %lld\n", 
557                         major_stats.total, major_stats.marked, major_stats.remarked,
558                         los_stats.total, los_stats.marked, los_stats.remarked,
559                         last_major_scan_time, last_los_scan_time);
560         }
561 #endif
562 }
563
564 #else
565
566 void
567 sgen_card_table_mark_address (mword address)
568 {
569         g_assert_not_reached ();
570 }
571
572 void
573 sgen_card_table_mark_range (mword address, mword size)
574 {
575         g_assert_not_reached ();
576 }
577
578 #define sgen_card_table_address_is_marked(p)    FALSE
579 #define scan_from_card_tables(start,end,queue)
580 #define card_table_clear()
581 #define card_table_init()
582 #define card_tables_collect_stats(begin)
583
584 guint8*
585 mono_gc_get_card_table (int *shift_bits, gpointer *mask)
586 {
587         return NULL;
588 }
589
590 #endif