blob: 1537f7c50a3678dd224bbc472d44363e41c3abe5 [file] [log] [blame]
Barry Hayes6e5cf602010-06-22 12:32:59 -07001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18 * Needed for PROT_* definitions.
19 */
20#include <sys/mman.h>
21
22#include "Dalvik.h"
23#include "alloc/HeapSource.h"
24#include "alloc/Visit.h"
25
26/*
27 * Maintain a card table from the the write barrier. All writes of
28 * non-NULL values to heap addresses should go through an entry in
29 * WriteBarrier, and from there to here.
30 *
Barry Hayes8f921a72010-07-09 12:53:49 -070031 * The heap is divided into "cards" of GC_CARD_SIZE bytes, as
32 * determined by GC_CARD_SHIFT. The card table contains one byte of
33 * data per card, to be used by the GC. The value of the byte will be
34 * one of GC_CARD_CLEAN or GC_CARD_DIRTY.
Barry Hayes6e5cf602010-06-22 12:32:59 -070035 *
36 * After any store of a non-NULL object pointer into a heap object,
37 * code is obliged to mark the card dirty. The setters in
38 * ObjectInlines.h [such as dvmSetFieldObject] do this for you. The
39 * JIT and fast interpreters also contain code to mark cards as dirty.
40 *
41 * [TODO: Concurrent collection will have to expand on this, as it
42 * uses the card table as well.]
43 *
44 * The card table is used to support partial collection, which at the
45 * moment means "treat the zygote's heap as permanent, and only GC
46 * objects in the application heap". In order to do this efficiently,
47 * the GC need to find quickly references to objects in the
48 * application heap from the zygote heap. When an application creates
49 * an object and stores it into an object on the zygote heap, it will
50 * mark the corresponding card in the zygote heap as "dirty". When the
51 * GC does a partial collection, it can efficiently find all the
52 * cross-heap objects, since they are all on dirty cards. The GC also
53 * takes the opportunity to mark as "clean" any cards which are dirty,
54 * but no longer contain cross-heap pointers.
55 *
56 * The card table's base [the "biased card table"] gets set to a
57 * rather strange value. In order to keep the JIT from having to
58 * fabricate or load GC_DIRTY_CARD to store into the card table,
59 * biased base is within the mmap allocation at a point where it's low
60 * byte is equal to GC_DIRTY_CARD. See dvmCardTableStartup for details.
61 */
62
63/*
64 * Initializes the card table; must be called before any other
65 * dvmCardTable*() functions.
66 */
Barry Hayesb874ab92010-07-14 08:13:18 -070067bool dvmCardTableStartup(void)
Barry Hayes6e5cf602010-06-22 12:32:59 -070068{
69 size_t length;
70 void *allocBase;
71 u1 *biasedBase;
Barry Hayesb874ab92010-07-14 08:13:18 -070072 GcHeap *gcHeap = gDvm.gcHeap;
73 void *heapBase = dvmHeapSourceGetBase();
74 assert(gcHeap != NULL);
75 assert(heapBase != NULL);
Barry Hayes6e5cf602010-06-22 12:32:59 -070076
77 /* Set up the card table */
78 length = gDvm.heapSizeMax / GC_CARD_SIZE;
79 /* Allocate an extra 256 bytes to allow fixed low-byte of base */
80 allocBase = dvmAllocRegion(length + 0x100, PROT_READ | PROT_WRITE,
81 "dalvik-card-table");
82 if (allocBase == NULL) {
83 return false;
84 }
85 gcHeap->cardTableBase = allocBase;
86 gcHeap->cardTableLength = length;
87 /* All zeros is the correct initial value; all clean. */
88 assert(GC_CARD_CLEAN == 0);
89
90 biasedBase = (u1 *)((uintptr_t)allocBase -
91 ((uintptr_t)heapBase >> GC_CARD_SHIFT));
92 if (((uintptr_t)biasedBase & 0xff) != GC_CARD_DIRTY) {
93 int offset;
94 offset = GC_CARD_DIRTY - ((uintptr_t)biasedBase & 0xff);
95 biasedBase += offset + (offset < 0 ? 0x100 : 0);
96 }
97 assert(((uintptr_t)biasedBase & 0xff) == GC_CARD_DIRTY);
Barry Hayes4496ed92010-07-12 09:52:20 -070098 gDvm.biasedCardTableBase = biasedBase;
Barry Hayes6e5cf602010-06-22 12:32:59 -070099
100 return true;
101}
102
103/*
104 * Tears down the entire CardTable.
105 */
106void dvmCardTableShutdown()
107{
Barry Hayesb874ab92010-07-14 08:13:18 -0700108 gDvm.biasedCardTableBase = NULL;
Barry Hayes6e5cf602010-06-22 12:32:59 -0700109 munmap(gDvm.gcHeap->cardTableBase, gDvm.gcHeap->cardTableLength);
110}
111
112/*
Barry Hayes4496ed92010-07-12 09:52:20 -0700113 * Returns true iff the address is within the bounds of the card table.
114 */
115bool dvmIsValidCard(const u1 *cardAddr)
116{
117 GcHeap *h = gDvm.gcHeap;
118 return cardAddr >= h->cardTableBase &&
119 cardAddr < &h->cardTableBase[h->cardTableLength];
120}
121
122/*
Barry Hayes8f921a72010-07-09 12:53:49 -0700123 * Returns the address of the relevent byte in the card table, given
Barry Hayes6e5cf602010-06-22 12:32:59 -0700124 * an address on the heap.
125 */
126u1 *dvmCardFromAddr(const void *addr)
127{
Barry Hayes4496ed92010-07-12 09:52:20 -0700128 u1 *biasedBase = gDvm.biasedCardTableBase;
129 u1 *cardAddr = biasedBase + ((uintptr_t)addr >> GC_CARD_SHIFT);
130 assert(dvmIsValidCard(cardAddr));
Barry Hayes6e5cf602010-06-22 12:32:59 -0700131 return cardAddr;
132}
133
Barry Hayes8f921a72010-07-09 12:53:49 -0700134/*
135 * Returns the first address in the heap which maps to this card.
136 */
137void *dvmAddrFromCard(const u1 *cardAddr)
138{
Barry Hayes4496ed92010-07-12 09:52:20 -0700139 assert(dvmIsValidCard(cardAddr));
140 uintptr_t offset = cardAddr - gDvm.biasedCardTableBase;
Barry Hayes8f921a72010-07-09 12:53:49 -0700141 return (void *)(offset << GC_CARD_SHIFT);
Barry Hayes6e5cf602010-06-22 12:32:59 -0700142}
143
144/*
145 * Dirties the card for the given address.
146 */
147void dvmMarkCard(const void *addr)
148{
149 u1 *cardAddr = dvmCardFromAddr(addr);
150 *cardAddr = GC_CARD_DIRTY;
151}
152
153/*
154 * Returns true iff all address within the Object are on unmarked cards.
155 */
156static bool objectIsClean(const Object *obj)
157{
158 assert(dvmIsValidObject(obj));
159 size_t size = dvmHeapSourceChunkSize(obj);
160 u1 *start = dvmCardFromAddr(obj);
161 u1 *end = dvmCardFromAddr((char *)obj + size-1);
162 u1 *index;
163
164 for (index = start; index <= end; index++) {
165 if (*index != GC_CARD_CLEAN) {
166 return false;
167 }
168 }
169 return true;
170}
171
172/*
173 * A Visitor callback in support of checkCleanObjects. "arg" is
174 * expected to be the immuneLimit.
175 */
Barry Hayes8f921a72010-07-09 12:53:49 -0700176static void crossGenCheckVisitor(void *ptr, void *arg)
Barry Hayes6e5cf602010-06-22 12:32:59 -0700177{
178 Object *ref = *(Object **)ptr;
179 Object *immuneLimit = (Object *)arg;
180
181 if (ref >= immuneLimit) {
182 LOGE("Clean obj contains threatened ref %p: %p", ptr, ref);
183 dvmAbort();
184 }
185}
186
187/*
188 * A HeapBitmap callback in support of checkCleanObjects.
189 */
Barry Hayes8f921a72010-07-09 12:53:49 -0700190static bool crossGenCheckCallback(size_t numPtrs, void **ptrs,
Barry Hayes6e5cf602010-06-22 12:32:59 -0700191 const void *finger, void *arg)
192{
193 size_t i;
194 for (i = 0; i < numPtrs; i++) {
195 Object *obj = ptrs[i];
196 if (objectIsClean(obj)) {
197 dvmVisitObject(crossGenCheckVisitor, obj, arg);
198 }
199 }
200
201 return true;
202}
203
204/*
205 * dvmAbort if a clean, immune Object in the bitmap contains a pointer
206 * to a threatened Object.
207 */
208void dvmVerifyCardTable(HeapBitmap *bitmap, const char *immuneLimit)
209{
210 dvmHeapBitmapWalk(bitmap, crossGenCheckCallback, (void *)immuneLimit);
211}
212