1
/*
2
 * $Id$
3
 *
4
 * Copyright (C) 2003-2009 JNode.org
5
 *
6
 * This library is free software; you can redistribute it and/or modify it
7
 * under the terms of the GNU Lesser General Public License as published
8
 * by the Free Software Foundation; either version 2.1 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful, but 
12
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
14
 * License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public License
17
 * along with this library; If not, write to the Free Software Foundation, Inc., 
18
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
 */
20
 
21
package org.jnode.vm.memmgr.generational;
22
23
import org.jnode.annotation.Inline;
24
import org.jnode.annotation.MagicPermission;
25
import org.jnode.annotation.NoInline;
26
import org.jnode.vm.ObjectVisitor;
27
import org.jnode.vm.Unsafe;
28
import org.jnode.vm.VmArchitecture;
29
import org.jnode.vm.VmMagic;
30
import org.jnode.vm.classmgr.ObjectFlags;
31
import org.jnode.vm.classmgr.VmNormalClass;
32
import org.jnode.vm.classmgr.VmType;
33
import org.jnode.vm.memmgr.HeapHelper;
34
import org.jnode.vm.scheduler.Monitor;
35
import org.vmmagic.pragma.Uninterruptible;
36
import org.vmmagic.unboxed.Address;
37
import org.vmmagic.unboxed.ObjectReference;
38
import org.vmmagic.unboxed.Offset;
39
import org.vmmagic.unboxed.Word;
40
41
/**
42
 * @author epr
43
 */
44
@MagicPermission
45
final class GenGCMarkVisitor extends ObjectVisitor implements ObjectFlags, Uninterruptible {
46
47
    /**
48
     * The marking stack
49
     */
50
    private final GenGCStack stack;
51
52
    /**
53
     * The number of marked objects.
54
     */
55
    private int markedObjects;
56
57
    /**
58
     * If true, all white and grey objects will be marked, otherwise only the
59
     * grey objects will be marked
60
     */
61
    private boolean rootSet;
62
63
    private VmArchitecture arch;
64
65
//    private final int slotSize;
66
67
    private GenHeapManager heapManager;
68
69
    private HeapHelper helper;
70
71
//    private final ProcessChildVisitor processChildVisitor = new ProcessChildVisitor();
72
73
    /**
74
     * Create a new instance
75
     *
76
     * @param stack
77
     */
78
    public GenGCMarkVisitor(GenGCStack stack, GenHeapManager heapManager, VmArchitecture arch) {
79
        this.stack = stack;
80
        this.markedObjects = 0;
81
        this.rootSet = false;
82
        // this.slotSize = arch.getReferenceSize();
83
        this.heapManager = heapManager;
84
        this.helper = heapManager.getHelper();
85
        this.arch = arch;
86
    }
87
88
    /**
89
     * @param object
90
     * @return boolean
91
     * @see org.jnode.vm.ObjectVisitor#visit(java.lang.Object)
92
     */
93
    @Override
94
    public boolean visit(Object object) {
95
96
        // Check the current color first, since a stackoverflow of
97
        // the mark stack results in another iteration of visits.
98
        final int gcColor = VmMagic.getObjectColor(object);
99
        if (gcColor == GC_BLACK) {
100
            return true;
101
        } else if (rootSet || (gcColor == GC_GREY)) {
102
            switch (gcColor) {
103
                case GC_YELLOW:
104
105
                    //Avoid telling such stories in a garbage collector!!! 
106
                    //Unsafe.debug("Yellow Object in the rootset.\n");
107
                    //Unsafe.debug("Perhaps corrupted Heap or bad luck.\n");
108
                    //Unsafe.debug("Continue as long as we don't have a");
109
                    //Unsafe.debug("gc map for the stack.\n Panic JNode once");
110
                    //Unsafe.debug("we have an exact GC.\n");
111
112
                    //Unsafe.die("Corrupted Heap\n");
113
                    return true;
114
                case GC_WHITE: {
115
                    final boolean ok;
116
                    ok = helper.atomicChangeObjectColor(object, gcColor,
117
                        GC_GREY);
118
                    if (!ok) {
119
                        Unsafe.debug("Could not change object color. ");
120
                    }
121
                    break;
122
                }                
123
                case GC_GREY:
124
                    break;
125
                default: {
126
                    Unsafe.debug("color");
127
                    Unsafe.debug(gcColor);
128
                    helper.die("Unknown GC color on object");
129
                }
130
            }
131
            stack.push(object);
132
            mark();
133
        }
134
135
        final boolean rc = (!stack.isOverflow());
136
        return rc;
137
    }
138
139
    /**
140
     * Reset this visitor to its original state.
141
     */
142
    @Inline
143
    public void reset() {
144
        this.markedObjects = 0;
145
    }
146
147
    /**
148
     * Process all objects on the markstack, until the markstack is empty.
149
     */
150
    @NoInline
151
    protected final void mark() {
152
        while (!stack.isEmpty()) {
153
            final Object object = stack.pop();
154
            markedObjects++;
155
            VmType<?> vmClass;
156
            try {
157
                vmClass = VmMagic.getObjectType(object);
158
            } catch (NullPointerException ex) {
159
                // This is a symptom of heap corruption
160
                if (object == null) {
161
                    helper.die("GCMarkError: null object");
162
                } else {
163
                    final Address objAddr = ObjectReference.fromObject(object).toAddress();
164
                    Unsafe.debug("Object address is ");
165
                    Unsafe.debug(objAddr);
166
                    Unsafe.debug(", tib is ");
167
                    Object[] tib = VmMagic.getTIB(object);
168
                    Unsafe.debug(ObjectReference.fromObject(tib).toAddress());
169
                    Unsafe.debug(", flags word is ");
170
                    Word flags = VmMagic.getObjectFlags(object);
171
                    Unsafe.debug(flags);
172
                    Unsafe.debug(", markedObjects is ");
173
                    Unsafe.debug(markedObjects);
174
                    Unsafe.debug('\n');
175
                    helper.die("GCMarkError: NPE");
176
                }
177
                throw ex;
178
            }
179
            if (vmClass == null) {
180
                Unsafe.debug("Oops vmClass == null in (");
181
                Unsafe.debug(markedObjects);
182
                Unsafe.debug(")");
183
                helper.die("vmClass == null in mark()");
184
            } else if (vmClass.isArray()) {
185
                if (!vmClass.isPrimitiveArray()) {
186
                    markArray(object);
187
                }
188
            } else {
189
                markObject(object, (VmNormalClass<?>) vmClass);
190
            }
191
            processChild(VmMagic.getTIB(object));
192
            final Monitor monitor = helper.getInflatedMonitor(object, arch);
193
            if (monitor != null) {
194
                processChild(monitor);
195
            }
196
            final int gcColor = VmMagic.getObjectColor(object);
197
            helper.atomicChangeObjectColor(object, gcColor, GC_BLACK);
198
        }
199
    }
200
201
    /**
202
     * Mark all elements in the given array. The array must contain references
203
     * only.
204
     *
205
     * @param object
206
     */
207
    @Inline
208
    private void markArray(Object object) {
209
        try {
210
            final Object[] arr = (Object[]) object;
211
            final int length = arr.length;
212
            for (int i = 0; i < length; i++) {
213
                final Object child = arr[i];
214
                if (child != null) {
215
                    // Enable the following in the case of heap corruption
216
                    if (true) {
217
                        verifyChild(child, object, "array child", i, i);
218
                    }
219
                    
220
                    processChild(child);
221
                }
222
            }
223
        } catch (ClassCastException ex) {
224
            System.out.println("object.class=" + object.getClass().getName());
225
            throw ex;
226
        }
227
    }
228
229
    /**
230
     * Mark all instance variables of the given object.
231
     *
232
     * @param object
233
     * @param vmClass
234
     */
235
    @Inline
236
    private void markObject(Object object, VmNormalClass<?> vmClass) {
237
        final int[] referenceOffsets = vmClass.getReferenceOffsets();
238
        final int cnt = referenceOffsets.length;
239
        if (cnt == 0) {
240
            return;
241
        }
242
243
        final int size = vmClass.getObjectSize();
244
        final Address objAddr = ObjectReference.fromObject(object).toAddress();
245
246
        for (int i = 0; i < cnt; i++) {
247
            final int offset = referenceOffsets[i];
248
            if ((offset < 0) || (offset >= size)) {
249
                Unsafe.debug("reference offset out of range!");
250
                Unsafe.debug(vmClass.getName());
251
                helper.die("Class internal error");
252
            } else {
253
                final ObjectReference child = objAddr.loadObjectReference(Offset.fromIntZeroExtend(offset));
254
                if (child != null) {
255
                    // Enable the following in the case of heap corruption
256
                    if (false) {
257
                        verifyChild(child, object, "object child", i, offset);
258
                    }
259
                    
260
                    processChild(child);
261
                }
262
            }
263
        }
264
    }
265
266
    @Inline
267
    private final void verifyChild(Object child, Object parent, String where, int i, int offset) {
268
        if (child != null) {
269
            final ObjectReference childRef = ObjectReference.fromObject(child);
270
            if (!heapManager.isObject(childRef.toAddress())) {
271
                Unsafe.debug("GCMarkError: in ");
272
                Unsafe.debug(where);
273
                Unsafe.debug(", i ");
274
                Unsafe.debug(i);
275
                Unsafe.debug(", offset ");
276
                Unsafe.debug(offset);
277
                Unsafe.debug(", parent type ");
278
                Unsafe.debug(VmMagic.getObjectType(parent).getName());
279
                Unsafe.debug(VmMagic.getObjectColor(parent));
280
                Unsafe.debug("; child (");
281
                Unsafe.debug(childRef.toAddress().toInt());
282
                Unsafe.debug(") is not an object ");
283
                Unsafe.debug(VmMagic.getObjectColor(childRef));
284
                helper.die("Corrupted heap");
285
            }
286
        }
287
    }
288
289
    /**
290
     * Process a child of an object (this child is a reference).
291
     *
292
     * @param child
293
     */
294
    @Inline
295
    final void processChild(Object child) {
296
        final int gcColor = VmMagic.getObjectColor(child);
297
        if (gcColor <= GC_WHITE) {
298
            // Yellow or White
299
            helper.atomicChangeObjectColor(child, gcColor, GC_GREY);
300
            try {
301
                // TEST for a valid vmclass.
302
                stack.push(child);
303
            } catch (NullPointerException ex) {
304
                Unsafe.debug("\nObject address ");
305
                Unsafe.debug(ObjectReference.fromObject(child).toAddress().toInt());
306
                Unsafe.debug("\nObject TIB ");
307
                Unsafe.debug(ObjectReference.fromObject(VmMagic.getTIB(child)).toAddress().toInt());
308
                helper.die("NPE in processChild; probably corrupted heap");
309
            }
310
        }
311
    }
312
313
    /**
314
     * Gets the number of objects marked by this visitor.
315
     *
316
     * @return int
317
     */
318
    @Inline
319
    public int getMarkedObjects() {
320
        return markedObjects;
321
    }
322
323
    /**
324
     * Gets the rootSet attribute.
325
     *
326
     * @return boolean
327
     */
328
    @Inline
329
    public boolean isRootSet() {
330
        return rootSet;
331
    }
332
333
    /**
334
     * Sets the rootSet attribute.
335
     *
336
     * @param b If true, all white and grey objects will be marked, otherwise
337
     *          only the grey objects will be marked.
338
     */
339
    @Inline
340
    public void setRootSet(boolean b) {
341
        rootSet = b;
342
    }
343
}