Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openj9
Path: blob/master/runtime/gc_check/CheckEngine.cpp
5985 views
1
/*******************************************************************************
2
* Copyright (c) 1991, 2021 IBM Corp. and others
3
*
4
* This program and the accompanying materials are made available under
5
* the terms of the Eclipse Public License 2.0 which accompanies this
6
* distribution and is available at https://www.eclipse.org/legal/epl-2.0/
7
* or the Apache License, Version 2.0 which accompanies this distribution and
8
* is available at https://www.apache.org/licenses/LICENSE-2.0.
9
*
10
* This Source Code may also be made available under the following
11
* Secondary Licenses when the conditions for such availability set
12
* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU
13
* General Public License, version 2 with the GNU Classpath
14
* Exception [1] and GNU General Public License, version 2 with the
15
* OpenJDK Assembly Exception [2].
16
*
17
* [1] https://www.gnu.org/software/classpath/license.html
18
* [2] http://openjdk.java.net/legal/assembly-exception.html
19
*
20
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception
21
*******************************************************************************/
22
23
/**
24
* @file
25
* @ingroup GC_CheckEngine
26
*/
27
28
#include "j9.h"
29
#include "j9cfg.h"
30
#include "HeapIteratorAPI.h"
31
#include "mmhook_internal.h"
32
33
#include "VMHelpers.hpp"
34
35
#if !defined(UT_TRACE_OVERHEAD)
36
#define UT_TRACE_OVERHEAD -1 /* disable assertions and tracepoints since we're not in the GC module proper */
37
#endif
38
39
#include "ArrayletLeafIterator.hpp"
40
#include "CheckEngine.hpp"
41
#include "Base.hpp"
42
#include "CheckBase.hpp"
43
#include "CheckCycle.hpp"
44
#include "CheckError.hpp"
45
#include "CheckReporter.hpp"
46
#include "CheckReporterTTY.hpp"
47
#include "ClassModel.hpp"
48
#include "ForwardedHeader.hpp"
49
#include "GCExtensions.hpp"
50
#include "HeapRegionDescriptor.hpp"
51
#include "ModronTypes.hpp"
52
#include "ObjectModel.hpp"
53
#include "ObjectAccessBarrier.hpp"
54
#include "ScanFormatter.hpp"
55
#include "SublistPool.hpp"
56
#include "SublistPuddle.hpp"
57
58
/**
59
* Private struct used as the user data for object slot iterator callbacks.
60
*/
61
typedef struct ObjectSlotIteratorCallbackUserData {
62
GC_CheckEngine* engine; /* Input */
63
J9MM_IterateRegionDescriptor* regionDesc; /* Input */
64
UDATA result; /* Output */
65
} ObjectSlotIteratorCallbackUserData;
66
67
static jvmtiIterationControl check_objectSlotsCallback(J9JavaVM *javaVM, J9MM_IterateObjectDescriptor *objectDesc, J9MM_IterateObjectRefDescriptor *refDesc, void *userData);
68
static bool isPointerInRegion(void *pointer, J9MM_IterateRegionDescriptor *regionDesc);
69
70
/*
71
* Define alignment masks for J9Objects:
72
*
73
* o On 64 bit all objects are 8 byte aligned
74
* o On 31/32 bit:
75
* - Objects allocated on stack are 4 bytes aligned
76
* - Objects allocated on heap are either 4 or 8 byte aligned dependent
77
* on platform
78
*/
79
#define J9MODRON_GCCHK_J9CLASS_ALIGNMENT_MASK 0xFF
80
81
#if defined(J9VM_ENV_DATA64)
82
#define J9MODRON_GCCHK_ONSTACK_ALIGNMENT_MASK 0x7
83
#else /* J9VM_ENV_DATA64 */
84
#define J9MODRON_GCCHK_ONSTACK_ALIGNMENT_MASK 0x3
85
#endif /* J9VM_ENV_DATA64*/
86
87
#define J9MODRON_GCCHK_J9CLASS_EYECATCHER (UDATA)0x99669966
88
89
/**
90
* Clear the counts of OwnableSynchronizerObjects
91
*/
92
void
93
GC_CheckEngine::clearCountsForOwnableSynchronizerObjects()
94
{
95
_ownableSynchronizerObjectCountOnList = UNINITIALIZED_SIZE_FOR_OWNABLESYNCHRONIER;
96
_ownableSynchronizerObjectCountOnHeap = UNINITIALIZED_SIZE_FOR_OWNABLESYNCHRONIER;
97
}
98
99
/**
100
*
101
*/
102
bool
103
GC_CheckEngine::verifyOwnableSynchronizerObjectCounts()
104
{
105
bool ret = true;
106
107
if ((UNINITIALIZED_SIZE_FOR_OWNABLESYNCHRONIER != _ownableSynchronizerObjectCountOnList) && (UNINITIALIZED_SIZE_FOR_OWNABLESYNCHRONIER != _ownableSynchronizerObjectCountOnHeap)) {
108
if (_ownableSynchronizerObjectCountOnList != _ownableSynchronizerObjectCountOnHeap) {
109
PORT_ACCESS_FROM_PORT(_portLibrary);
110
j9tty_printf(PORTLIB, " <gc check: found count=%zu of OwnableSynchronizerObjects on Heap doesn't match count=%zu on lists>\n", _ownableSynchronizerObjectCountOnHeap, _ownableSynchronizerObjectCountOnList);
111
ret = false;
112
}
113
}
114
115
return ret;
116
}
117
118
119
/**
120
* Clear the cache of previously visited objects.
121
*/
122
void
123
GC_CheckEngine::clearPreviousObjects()
124
{
125
_lastHeapObject1.type = GC_CheckElement::type_none;
126
_lastHeapObject2.type = GC_CheckElement::type_none;
127
_lastHeapObject3.type = GC_CheckElement::type_none;
128
}
129
130
/**
131
* Push an object onto the list of previously visited objects
132
* (and drop oldest object).
133
*/
134
void
135
GC_CheckEngine::pushPreviousObject(J9Object *objectPtr)
136
{
137
_lastHeapObject3 = _lastHeapObject2;
138
_lastHeapObject2 = _lastHeapObject1;
139
140
_lastHeapObject1.type = GC_CheckElement::type_object;
141
_lastHeapObject1.u.object = objectPtr;
142
}
143
144
/**
145
* Push a class onto the list of previously visited objects
146
* (and drop oldest object).
147
*/
148
void
149
GC_CheckEngine::pushPreviousClass(J9Class* clazz)
150
{
151
_lastHeapObject3 = _lastHeapObject2;
152
_lastHeapObject2 = _lastHeapObject1;
153
154
_lastHeapObject1.type = GC_CheckElement::type_class;
155
_lastHeapObject1.u.clazz = clazz;
156
}
157
158
159
/**
160
* Determine whether or not the given object is contained in the given segment
161
*
162
* @return true if objectPtr is contained in the given segment
163
* @return false otherwise
164
*/
165
bool
166
GC_CheckEngine::isPointerInSegment(void *pointer, J9MemorySegment *segment)
167
{
168
return ( ((UDATA)pointer >= (UDATA)segment->heapBase) && ((UDATA)pointer < (UDATA)segment->heapAlloc) );
169
}
170
171
/**
172
* Determine whether or not the given object is contained in the given stack
173
*
174
* @return true if objectPtr is contained in the given stack
175
* @return false otherwise
176
*/
177
bool
178
GC_CheckEngine::isObjectOnStack(J9Object *objectPtr, J9JavaStack *stack)
179
{
180
return ((UDATA)objectPtr < (UDATA)stack->end) && ((UDATA)objectPtr >= (UDATA)(stack+1));
181
}
182
183
/**
184
* Find the class segment which contains a given class.
185
*
186
* @param clazz Pointer to the class to search for
187
*
188
* @return the segment which contains the class, or
189
* NULL if the class was not found in any of the class segments
190
*/
191
J9MemorySegment*
192
GC_CheckEngine::findSegmentForClass(J9JavaVM *javaVM, J9Class *clazz)
193
{
194
J9MemorySegmentList * segmentList = javaVM->classMemorySegments;
195
J9MemorySegment * segment = (J9MemorySegment *) avl_search(&segmentList->avlTreeData, (UDATA)clazz);
196
if (NULL != segment) {
197
UDATA flags = MEMORY_TYPE_RAM_CLASS | MEMORY_TYPE_UNDEAD_CLASS;
198
if (0 != (segment->type & flags)) {
199
return segment;
200
}
201
}
202
return NULL;
203
}
204
205
/**
206
* Find the region which contains a given object. Return true and write to regionDescOutput if
207
* the region is found, return false otherwise.
208
*/
209
bool
210
GC_CheckEngine::findRegionForPointer(J9JavaVM* javaVM, void *pointer, J9MM_IterateRegionDescriptor* regionDescOutput)
211
{
212
bool found = false;
213
214
/* Check the local cache of the previously found region */
215
if (isPointerInRegion(pointer, &_regionDesc)) {
216
copyRegionDescription(&_regionDesc, regionDescOutput);
217
found = true;
218
} else if (0 != javaVM->memoryManagerFunctions->j9mm_find_region_for_pointer(javaVM, pointer, regionDescOutput)) {
219
/* If a region is found cache it in _regionDesc for the next lookup */
220
copyRegionDescription(regionDescOutput, &_regionDesc);
221
found = true;
222
}
223
224
return found;
225
}
226
227
/**
228
* Perform basic verification on an object pointer.
229
* Checks a pointer for alignment, and optionally searches memory segments for
230
* this pointer.
231
*
232
* @param objectPtr[in] Pointer to the object to be verified
233
* @param newObjectPtr[out] On return, contains the actual object pointer once forwarding pointers have been followed
234
* @param regionDesc[out] A pointer to a region structure describing the region of objectPtr. The region will be
235
* searched for, and the result stored back to the location pointed to by regionDesc
236
*
237
* @note regionDesc must not be NULL
238
* @see @ref findRegionForPointer
239
*
240
* @return @ref GCCheckWalkStageErrorCodes
241
*/
242
UDATA
243
GC_CheckEngine::checkJ9ObjectPointer(J9JavaVM *javaVM, J9Object *objectPtr, J9Object **newObjectPtr, J9MM_IterateRegionDescriptor *regionDesc)
244
{
245
MM_GCExtensionsBase* extensions = MM_GCExtensions::getExtensions(_javaVM);
246
assume0(regionDesc != NULL);
247
248
*newObjectPtr = objectPtr;
249
250
if (NULL == objectPtr) {
251
return J9MODRON_GCCHK_RC_OK;
252
}
253
254
bool foundRegion = findRegionForPointer(javaVM, objectPtr, regionDesc);
255
if (!foundRegion) {
256
/* Is the object on the stack? */
257
J9VMThread *vmThread;
258
GC_VMThreadListIterator threadListIterator(javaVM);
259
while ((vmThread = threadListIterator.nextVMThread()) != NULL) {
260
if (isObjectOnStack(objectPtr, vmThread->stackObject)) {
261
return J9MODRON_GCCHK_RC_STACK_OBJECT;
262
}
263
}
264
265
if (J9MODRON_GCCHK_J9CLASS_EYECATCHER == J9GC_J9OBJECT_CLAZZ_WITH_FLAGS_VM(objectPtr, javaVM)) {
266
return J9MODRON_GCCHK_RC_OBJECT_SLOT_POINTS_TO_J9CLASS;
267
}
268
269
return J9MODRON_GCCHK_RC_NOT_FOUND;
270
}
271
272
if (0 == regionDesc->objectAlignment) {
273
/* this is a heap region, but it's not intended for objects (could be free or an arraylet leaf) */
274
return J9MODRON_GCCHK_RC_NOT_IN_OBJECT_REGION;
275
}
276
277
/* Now we know object is not on stack we can check that it's correctly aligned
278
* for a J9Object.
279
*/
280
if ((UDATA)(objectPtr) & (regionDesc->objectAlignment - 1)) {
281
return J9MODRON_GCCHK_RC_UNALIGNED;
282
}
283
284
if (_cycle->getMiscFlags() & J9MODRON_GCCHK_MISC_MIDSCAVENGE) {
285
UDATA regionType = ((MM_HeapRegionDescriptor*)regionDesc->id)->getTypeFlags();
286
if ((regionType & MEMORY_TYPE_NEW) || extensions->isVLHGC()) {
287
// TODO: ideally, we should only check this in the evacuate segment
288
// TODO: do some safety checks first -- is there enough room in the segment?
289
MM_ForwardedHeader forwardedHeader(objectPtr, extensions->compressObjectReferences());
290
if (forwardedHeader.isForwardedPointer()) {
291
*newObjectPtr = forwardedHeader.getForwardedObject();
292
293
if (_cycle->getMiscFlags() & J9MODRON_GCCHK_VERBOSE) {
294
PORT_ACCESS_FROM_PORT(_portLibrary);
295
j9tty_printf(PORTLIB, " <gc check: found forwarded pointer %p -> %p>\n", objectPtr, *newObjectPtr);
296
}
297
298
objectPtr = *newObjectPtr;
299
if (!findRegionForPointer(javaVM, objectPtr, regionDesc)) {
300
return J9MODRON_GCCHK_RC_NOT_FOUND;
301
}
302
303
if (0 == regionDesc->objectAlignment) {
304
/* this is a heap region, but it's not intended for objects (could be free or an arraylet leaf) */
305
return J9MODRON_GCCHK_RC_NOT_IN_OBJECT_REGION;
306
}
307
308
/* make sure the forwarded pointer is also aligned */
309
if ((UDATA)(objectPtr) & (regionDesc->objectAlignment - 1)) {
310
return J9MODRON_GCCHK_RC_UNALIGNED;
311
}
312
}
313
}
314
}
315
316
/* Check that elements of a double array are aligned on an 8-byte boundary. For continuous
317
* arrays, verifying that the J9Indexable object is aligned on an 8-byte boundary is sufficient.
318
* For arraylets, depending on the layout, elements of the array may be stored on arraylet leafs
319
* or on the spine. Arraylet leafs should always be aligned on 8-byte boundaries. Checking both
320
* the first and last element will ensure that we are always checking that elements are aligned
321
* on the spine.
322
* */
323
if (extensions->objectModel.isDoubleArray(objectPtr)) {
324
j9array_t array = (j9array_t)objectPtr;
325
UDATA size = extensions->indexableObjectModel.getSizeInElements(array);
326
U_64* elementPtr = NULL;
327
328
if (0 != size) {
329
J9VMThread *vmThread = javaVM->internalVMFunctions->currentVMThread(javaVM);
330
elementPtr = J9JAVAARRAY_EA(vmThread, array, 0, U_64);
331
if ((UDATA)elementPtr & (sizeof(U_64)-1)) {
332
return J9MODRON_GCCHK_RC_DOUBLE_ARRAY_UNALIGNED;
333
}
334
335
elementPtr = J9JAVAARRAY_EA(vmThread, array, size-1, U_64);
336
if ((UDATA)elementPtr & (sizeof(U_64)-1)) {
337
return J9MODRON_GCCHK_RC_DOUBLE_ARRAY_UNALIGNED;
338
}
339
}
340
}
341
342
/* TODO: shouldn't the segment be checked for the objectPtr if segment was not NULL? */
343
return J9MODRON_GCCHK_RC_OK;
344
}
345
346
/**
347
* Perform basic verification on a class pointer.
348
* Calls @ref checkPointer to perform the most basic checks (alignment, is
349
* contained in a class segment), then performs some checks specific to a
350
* class pointer.
351
*
352
* @param clazz Pointer to the class
353
* @param allowUndead true if undead classes are allowed in this context
354
*
355
* @return @ref GCCheckWalkStageErrorCodes
356
* @see @ref checkFlags
357
* @see @ref findSegmentForClass
358
*/
359
UDATA
360
GC_CheckEngine::checkJ9ClassPointer(J9JavaVM *javaVM, J9Class *clazz, bool allowUndead )
361
{
362
/* Short circuit if we've recently checked this class.
363
* In JLTF, this cache is about 94% effective (when its size is 19).
364
*/
365
UDATA cacheIndex = ((UDATA)clazz) % CLASS_CACHE_SIZE;
366
if (allowUndead && (_checkedClassCacheAllowUndead[cacheIndex] == clazz)) {
367
return J9MODRON_GCCHK_RC_OK;
368
} else if (_checkedClassCache[cacheIndex] == clazz) {
369
return J9MODRON_GCCHK_RC_OK;
370
}
371
372
/* Verify class ptr in known segment.
373
* As it's a J9Class ptr we only search class segments
374
*/
375
if (NULL == clazz) {
376
return J9MODRON_GCCHK_RC_NULL_CLASS_POINTER;
377
}
378
379
if ((UDATA)(clazz) & J9MODRON_GCCHK_J9CLASS_ALIGNMENT_MASK) {
380
return J9MODRON_GCCHK_RC_CLASS_POINTER_UNALIGNED;
381
}
382
383
J9MemorySegment *segment = findSegmentForClass(javaVM, clazz);
384
if (segment == NULL) {
385
return J9MODRON_GCCHK_RC_CLASS_NOT_FOUND;
386
} else if (!allowUndead && (0 != (segment->type & MEMORY_TYPE_UNDEAD_CLASS))) {
387
return J9MODRON_GCCHK_RC_CLASS_IS_UNDEAD;
388
}
389
390
/* Check to ensure J9Class header has the correct eyecatcher.
391
*/
392
UDATA result = checkJ9ClassHeader(javaVM, clazz);
393
if ( J9MODRON_GCCHK_RC_OK != result) {
394
return result;
395
}
396
397
/* Check that class is not unloaded */
398
result = checkJ9ClassIsNotUnloaded(javaVM, clazz);
399
if ( J9MODRON_GCCHK_RC_OK != result) {
400
return result;
401
}
402
403
if (_cycle->getCheckFlags() & J9MODRON_GCCHK_VERIFY_RANGE) {
404
UDATA delta = (UDATA)segment->heapAlloc - (UDATA)clazz;
405
406
/* Basic check that there is enough room for the class header */
407
if (delta < sizeof(J9Class)) {
408
return J9MODRON_GCCHK_RC_CLASS_INVALID_RANGE;
409
}
410
}
411
412
/* class checked out. Record it in the cache so we don't need to check it again. */
413
if (allowUndead) {
414
_checkedClassCacheAllowUndead[cacheIndex] = clazz;
415
} else {
416
_checkedClassCache[cacheIndex] = clazz;
417
}
418
419
return J9MODRON_GCCHK_RC_OK;
420
}
421
422
/**
423
* Verify the integrity of an object on the heap.
424
* Checks various aspects of object integrity based on the checkFlags.
425
*
426
* @param objectPtr Pointer to the object
427
* @param segment The segment containing the pointer
428
* @param checkFlags Type/level of verification
429
*
430
* @return @ref GCCheckWalkStageErrorCodes
431
*
432
* @see @ref checkFlags
433
*/
434
UDATA
435
GC_CheckEngine::checkJ9Object(J9JavaVM *javaVM, J9Object* objectPtr, J9MM_IterateRegionDescriptor *regionDesc, UDATA checkFlags)
436
{
437
MM_GCExtensions * extensions = MM_GCExtensions::getExtensions(javaVM);
438
439
assume0(NULL != regionDesc);
440
441
if (NULL == objectPtr) {
442
return J9MODRON_GCCHK_RC_OK;
443
}
444
445
if (0 == regionDesc->objectAlignment) {
446
/* this is a heap region, but it's not intended for objects (could be free or an arraylet leaf) */
447
return J9MODRON_GCCHK_RC_NOT_IN_OBJECT_REGION;
448
}
449
450
if (((UDATA)objectPtr) & (regionDesc->objectAlignment - 1)) {
451
return J9MODRON_GCCHK_RC_UNALIGNED;
452
}
453
454
if (checkFlags & J9MODRON_GCCHK_VERIFY_CLASS_SLOT) {
455
/* Check that the class pointer points to the class heap, etc. */
456
UDATA ret = checkJ9ClassPointer(javaVM, J9GC_J9OBJECT_CLAZZ_VM(objectPtr, javaVM), true);
457
458
if (J9MODRON_GCCHK_RC_OK != ret) {
459
return ret;
460
}
461
}
462
463
#if defined(J9VM_ENV_DATA64)
464
if (OMR_ARE_ANY_BITS_SET(_cycle->getMiscFlags(), J9MODRON_GCCHK_VALID_INDEXABLE_DATA_ADDRESS) && extensions->objectModel.isIndexable(objectPtr)) {
465
/* Check that the indexable object has the correct data address pointer */
466
if (!extensions->indexableObjectModel.isCorrectDataAddr((J9IndexableObject*)objectPtr)) {
467
return J9MODRON_GCCHK_RC_INVALID_INDEXABLE_DATA_ADDRESS;
468
}
469
}
470
#endif /* (J9VM_ENV_DATA64) */
471
472
if (checkFlags & J9MODRON_GCCHK_VERIFY_RANGE) {
473
UDATA regionEnd = ((UDATA)regionDesc->regionStart) + regionDesc->regionSize;
474
UDATA delta = regionEnd - (UDATA)objectPtr;
475
J9MM_IterateObjectDescriptor objectDesc;
476
477
/* Basic check that there is enough room for the object header */
478
if (delta < J9JAVAVM_OBJECT_HEADER_SIZE(javaVM)) {
479
return J9MODRON_GCCHK_RC_INVALID_RANGE;
480
}
481
482
/* TODO: find out what the indexable header size should really be */
483
if (extensions->objectModel.isIndexable(objectPtr) && (delta < J9JAVAVM_CONTIGUOUS_HEADER_SIZE(javaVM))) {
484
return J9MODRON_GCCHK_RC_INVALID_RANGE;
485
}
486
487
javaVM->memoryManagerFunctions->j9mm_initialize_object_descriptor(javaVM, &objectDesc, objectPtr);
488
489
if (delta < objectDesc.size) {
490
return J9MODRON_GCCHK_RC_INVALID_RANGE;
491
}
492
}
493
494
if (checkFlags & J9MODRON_GCCHK_VERIFY_FLAGS) {
495
if (!extensions->objectModel.checkIndexableFlag(objectPtr)) {
496
return J9MODRON_GCCHK_RC_INVALID_FLAGS;
497
}
498
499
if (extensions->isStandardGC()) {
500
#if defined(J9VM_GC_GENERATIONAL)
501
UDATA regionType = ((MM_HeapRegionDescriptor*)regionDesc->id)->getTypeFlags();
502
if (regionType & MEMORY_TYPE_OLD) {
503
/* All objects in an old segment must have old bit set */
504
if (!extensions->isOld(objectPtr)) {
505
return J9MODRON_GCCHK_RC_OLD_SEGMENT_INVALID_FLAGS;
506
}
507
} else {
508
if (regionType & MEMORY_TYPE_NEW) {
509
/* Object in a new segment can't have old bit or remembered bit set */
510
if (extensions->isOld(objectPtr)) {
511
return J9MODRON_GCCHK_RC_NEW_SEGMENT_INVALID_FLAGS;
512
}
513
}
514
}
515
#endif /* J9VM_GC_GENERATIONAL */
516
}
517
}
518
519
return J9MODRON_GCCHK_RC_OK;
520
}
521
522
/**
523
* Verify the integrity of an J9Class object.
524
* Checks various aspects of object integrity based on the checkFlags.
525
*
526
* @param clazzPtr Pointer to the J9Class object
527
* @param segment The segment containing the pointer
528
* @param checkFlags Type/level of verification
529
*
530
* @return @ref GCCheckWalkStageErrorCodes
531
*
532
* @see @ref checkFlags
533
*/
534
UDATA
535
GC_CheckEngine::checkJ9Class(J9JavaVM *javaVM, J9Class *clazzPtr, J9MemorySegment *segment, UDATA checkFlags)
536
{
537
if (NULL == clazzPtr) {
538
return J9MODRON_GCCHK_RC_OK;
539
}
540
541
if (((UDATA)clazzPtr) & J9MODRON_GCCHK_J9CLASS_ALIGNMENT_MASK) {
542
return J9MODRON_GCCHK_RC_CLASS_POINTER_UNALIGNED;
543
}
544
545
/* Check that the class header contains the expected values */
546
UDATA ret = checkJ9ClassHeader(javaVM, clazzPtr);
547
if (J9MODRON_GCCHK_RC_OK != ret) {
548
return ret;
549
}
550
551
/* Check that class is not unloaded */
552
ret = checkJ9ClassIsNotUnloaded(javaVM, clazzPtr);
553
if (J9MODRON_GCCHK_RC_OK != ret) {
554
return ret;
555
}
556
557
if (checkFlags & J9MODRON_GCCHK_VERIFY_RANGE) {
558
UDATA delta = (UDATA)segment->heapAlloc - (UDATA)clazzPtr;
559
560
/* Basic check that there is enough room for the object header */
561
if (delta < sizeof(J9Class)) {
562
return J9MODRON_GCCHK_RC_CLASS_INVALID_RANGE;
563
}
564
}
565
566
return J9MODRON_GCCHK_RC_OK;
567
}
568
569
/**
570
* Verify J9Class header.
571
* Checks various aspects of object integrity based on the checkFlags.
572
*
573
* @param clazz Pointer to the J9Class object
574
*
575
* @return @ref GCCheckWalkStageErrorCodes
576
*/
577
UDATA
578
GC_CheckEngine::checkJ9ClassHeader(J9JavaVM *javaVM, J9Class *clazz)
579
{
580
/* Check to ensure J9Class header has the correct eyecatcher.
581
*/
582
if (clazz->eyecatcher != J9MODRON_GCCHK_J9CLASS_EYECATCHER) {
583
return J9MODRON_GCCHK_RC_J9CLASS_HEADER_INVALID;
584
}
585
return J9MODRON_GCCHK_RC_OK;
586
}
587
588
/**
589
* Verify J9Class is not unloaded.
590
*
591
* @param clazz Pointer to the J9Class object
592
*
593
* @return @ref GCCheckWalkStageErrorCodes
594
*/
595
UDATA
596
GC_CheckEngine::checkJ9ClassIsNotUnloaded(J9JavaVM *javaVM, J9Class *clazz)
597
{
598
/* Check to ensure J9Class header has the correct eyecatcher.
599
*/
600
if (0 != (clazz->classDepthAndFlags & J9AccClassDying)) {
601
return J9MODRON_GCCHK_RC_CLASS_IS_UNLOADED;
602
}
603
return J9MODRON_GCCHK_RC_OK;
604
}
605
606
/**
607
* Verify a stack object.
608
* Checks various aspects of object integrity based on the checkFlags.
609
*
610
* @param objectPtr Pointer to the object
611
* @param checkFlags Type/level of verification
612
*
613
* @return @ref GCCheckWalkStageErrorCodes
614
* @see @ref checkFlags
615
*
616
* @todo Fix checks for flags that are automatically set by the JIT
617
*/
618
UDATA
619
GC_CheckEngine::checkStackObject(J9JavaVM *javaVM, J9Object *objectPtr)
620
{
621
MM_GCExtensionsBase * extensions = MM_GCExtensions::getExtensions(javaVM);
622
623
if (NULL == objectPtr) {
624
return J9MODRON_GCCHK_RC_OK;
625
}
626
627
if ((UDATA)(objectPtr) & J9MODRON_GCCHK_ONSTACK_ALIGNMENT_MASK) {
628
return J9MODRON_GCCHK_RC_UNALIGNED;
629
}
630
631
if (_cycle->getCheckFlags() & J9MODRON_GCCHK_VERIFY_CLASS_SLOT) {
632
/* Check that the class pointer points to the class heap, etc. */
633
UDATA ret = checkJ9ClassPointer(javaVM, J9GC_J9OBJECT_CLAZZ_VM(objectPtr, javaVM));
634
if (J9MODRON_GCCHK_RC_OK != ret) {
635
return ret;
636
}
637
}
638
639
if (_cycle->getCheckFlags() & J9MODRON_GCCHK_VERIFY_FLAGS) {
640
641
if (!extensions->objectModel.checkIndexableFlag(objectPtr)) {
642
return J9MODRON_GCCHK_RC_INVALID_FLAGS;
643
}
644
}
645
646
return J9MODRON_GCCHK_RC_OK;
647
}
648
649
/**
650
* Verify an object pointer.
651
* Verify that the given pointer appears to be a valid heap pointer, and
652
* if so, proceed to verify the object that it points to.
653
* Calls checkJ9Object which does the actual checking.
654
*
655
* @param javaVM the J9JavaVM struct
656
* @param objectPtr the object to check
657
* @return @ref GCCheckWalkStageErrorCodes
658
*/
659
UDATA
660
GC_CheckEngine::checkObjectIndirect(J9JavaVM *javaVM, J9Object *objectPtr)
661
{
662
if (NULL == objectPtr) {
663
return J9MODRON_GCCHK_RC_OK;
664
}
665
666
/* Short circuit if we've recently checked this object.
667
* In JLTF, this cache is about 28% effective (size 19), 38% effective (size 61), or 49% effective (size 1021).
668
*/
669
UDATA cacheIndex = ((UDATA)objectPtr) % OBJECT_CACHE_SIZE;
670
if (_checkedObjectCache[cacheIndex] == objectPtr) {
671
return J9MODRON_GCCHK_RC_OK;
672
}
673
674
/* Check if reference to J9Object */
675
J9Object *newObjectPtr = NULL;
676
J9MM_IterateRegionDescriptor objectRegion; /* initialized by checkJ9ObjectPointer */
677
UDATA result = checkJ9ObjectPointer(javaVM, objectPtr, &newObjectPtr, &objectRegion);
678
679
if (J9MODRON_GCCHK_RC_OK == result) {
680
result = checkJ9Object(javaVM, newObjectPtr, &objectRegion, _cycle->getCheckFlags());
681
}
682
683
if (J9MODRON_GCCHK_RC_OK == result) {
684
/* Object is OK. Record it in the cache so we can short circuit if we see it again */
685
_checkedObjectCache[cacheIndex] = objectPtr;
686
}
687
688
return result;
689
}
690
691
/**
692
* Verify a class.
693
*
694
* @param clazz the class to be verified
695
* @param segment the class memory segment which contains the class
696
*
697
* @return #J9MODRON_SLOT_ITERATOR_OK
698
*/
699
UDATA
700
GC_CheckEngine::checkClassHeap(J9JavaVM *javaVM, J9Class *clazz, J9MemorySegment *segment)
701
{
702
MM_GCExtensions * extensions = MM_GCExtensions::getExtensions(javaVM);
703
UDATA result;
704
volatile j9object_t *slotPtr;
705
706
/*
707
* Verify that this is, in fact, a class
708
*/
709
result = checkJ9Class(javaVM, clazz, segment, _cycle->getCheckFlags());
710
if (J9MODRON_GCCHK_RC_OK != result) {
711
GC_CheckError error(clazz, _cycle, _currentCheck, "Class ", result, _cycle->nextErrorCount());
712
_reporter->report(&error);
713
}
714
715
/*
716
* Process object slots in the class
717
*/
718
GC_ClassIterator classIterator(extensions, clazz);
719
while((slotPtr = classIterator.nextSlot()) != NULL) {
720
int state = classIterator.getState();
721
722
J9Object *objectPtr = *slotPtr;
723
724
result = checkObjectIndirect(javaVM, objectPtr);
725
726
if (J9MODRON_GCCHK_RC_OK != result) {
727
const char *elementName = "";
728
729
switch (state) {
730
case classiterator_state_statics:
731
elementName = "static "; break;
732
case classiterator_state_constant_pool:
733
elementName = "constant "; break;
734
case classiterator_state_slots:
735
elementName = "slots "; break;
736
case classiterator_state_callsites:
737
elementName = "callsite "; break;
738
}
739
GC_CheckError error(clazz, (void*)slotPtr, _cycle, _currentCheck, elementName, result, _cycle->nextErrorCount());
740
_reporter->report(&error);
741
return J9MODRON_SLOT_ITERATOR_OK;
742
}
743
if (extensions->isStandardGC()) {
744
#if defined(J9VM_GC_GENERATIONAL)
745
/* If the slot has its old bit OFF, the class's remembered bit should be ON */
746
if (objectPtr && !extensions->isOld(objectPtr)) {
747
if (!extensions->objectModel.isRemembered((J9Object*)clazz->classObject)) {
748
GC_CheckError error(clazz, (void*)slotPtr, _cycle, _currentCheck, "Class ", J9MODRON_GCCHK_RC_REMEMBERED_SET_OLD_OBJECT, _cycle->nextErrorCount());
749
_reporter->report(&error);
750
return J9MODRON_SLOT_ITERATOR_OK;
751
}
752
}
753
#endif /* J9VM_GC_GENERATIONAL */
754
}
755
}
756
757
if (J9MODRON_GCCHK_RC_OK != checkClassStatics(javaVM, clazz)) {
758
return J9MODRON_SLOT_ITERATOR_OK;
759
}
760
761
J9Class* replaced = clazz->replacedClass;
762
if (NULL != replaced) {
763
/* if class replaces another class the replaced class must have J9AccClassHotSwappedOut flag set */
764
if (0 == (J9CLASS_FLAGS(replaced) & J9AccClassHotSwappedOut)) {
765
GC_CheckError error(clazz, (void*)&(clazz->replacedClass), _cycle, _currentCheck, "Class ", J9MODRON_GCCHK_RC_REPLACED_CLASS_HAS_NO_HOTSWAP_FLAG, _cycle->nextErrorCount());
766
_reporter->report(&error);
767
return J9MODRON_SLOT_ITERATOR_OK;
768
}
769
}
770
771
/*
772
* Process class slots in the class
773
*/
774
GC_ClassIteratorClassSlots classIteratorClassSlots(javaVM, clazz);
775
J9Class *classPtr;
776
while (NULL != (classPtr = classIteratorClassSlots.nextSlot())) {
777
int state = classIteratorClassSlots.getState();
778
const char *elementName = "";
779
780
result = J9MODRON_GCCHK_RC_OK;
781
782
switch (state) {
783
case classiteratorclassslots_state_constant_pool:
784
result = checkJ9ClassPointer(javaVM, classPtr);
785
elementName = "constant ";
786
break;
787
case classiteratorclassslots_state_superclasses:
788
result = checkJ9ClassPointer(javaVM, classPtr);
789
elementName = "superclass ";
790
break;
791
case classiteratorclassslots_state_interfaces:
792
result = checkJ9ClassPointer(javaVM, classPtr);
793
elementName = "interface ";
794
break;
795
case classiteratorclassslots_state_array_class_slots:
796
result = checkJ9ClassPointer(javaVM, classPtr);
797
elementName = "array class ";
798
break;
799
case classiteratorclassslots_state_flattened_class_cache_slots:
800
result = checkJ9ClassPointer(javaVM, classPtr);
801
elementName = "flattened class cache ";
802
break;
803
}
804
805
if (J9MODRON_GCCHK_RC_OK != result) {
806
GC_CheckError error(clazz, &classPtr, _cycle, _currentCheck, elementName, result, _cycle->nextErrorCount());
807
_reporter->report(&error);
808
return J9MODRON_SLOT_ITERATOR_OK;
809
}
810
}
811
812
return J9MODRON_SLOT_ITERATOR_OK;
813
}
814
815
UDATA
816
GC_CheckEngine::checkClassStatics(J9JavaVM* vm, J9Class* clazz)
817
{
818
UDATA result = J9MODRON_GCCHK_RC_OK;
819
bool validationRequired = true;
820
821
if (0 != (J9CLASS_FLAGS(clazz) & J9AccClassHotSwappedOut)) {
822
/* if class has been hot swapped (J9AccClassHotSwappedOut bit is set) in Fast HCR,
823
* the ramStatics of the existing class may be reused. The J9ClassReusedStatics
824
* bit in J9Class->classFlags will be set if that's the case.
825
* In Extended HCR mode ramStatics might be not NULL and must be valid
826
* NOTE: If class is hot swapped and the value in ramStatics is NULL it is valid
827
* to have the correspondent ROM Class value in objectStaticCount field greater then 0
828
*/
829
if (J9GC_CLASS_IS_ARRAY(clazz)) {
830
/* j9arrayclass should not be hot swapped */
831
result = J9MODRON_GCCHK_RC_CLASS_HOT_SWAPPED_FOR_ARRAY;
832
GC_CheckError error(clazz, _cycle, _currentCheck, "Class ", result, _cycle->nextErrorCount());
833
_reporter->report(&error);
834
validationRequired = false;
835
}
836
837
if (areExtensionsEnabled(vm)) {
838
/* This is Extended HSR mode so hot swapped class might have NULL in ramStatics field */
839
if (NULL == clazz->ramStatics) {
840
validationRequired = false;
841
}
842
}
843
if (J9ClassReusedStatics == (J9CLASS_EXTENDED_FLAGS(clazz) & J9ClassReusedStatics)) {
844
/* This case can also occur when running -Xint with extensions enabled */
845
validationRequired = false;
846
}
847
}
848
849
if (validationRequired) {
850
J9VMThread* currentThread = vm->internalVMFunctions->currentVMThread(vm);
851
J9ClassLoader* classLoader = clazz->classLoader;
852
J9ROMClass *romClazz = clazz->romClass;
853
J9ROMFieldWalkState state;
854
855
UDATA numberOfReferences = 0;
856
j9object_t *sectionStart = NULL;
857
j9object_t *sectionEnd = NULL;
858
859
/*
860
* Note: we have no special recognition for J9ArrayClass here
861
* J9ArrayClass does not have a ramStatics field but something else at this place
862
* so direct check (NULL != clazz->ramStatics) would not be correct,
863
* however romClazz->objectStaticCount must be 0 for this case
864
*/
865
if (0 != romClazz->objectStaticCount) {
866
sectionStart = (j9object_t *)clazz->ramStatics;
867
sectionEnd = sectionStart + romClazz->objectStaticCount;
868
}
869
870
J9ROMFieldShape* romFieldCursor = romFieldsStartDo(romClazz, &state);
871
872
/* Iterate all fields of ROM Class looking to statics fields pointed to java objects */
873
while (NULL != romFieldCursor) {
874
/* interested in static fields only */
875
if (romFieldCursor->modifiers & J9AccStatic) {
876
J9UTF8* nameUTF = J9ROMFIELDSHAPE_NAME(romFieldCursor);
877
J9UTF8* sigUTF = J9ROMFIELDSHAPE_SIGNATURE(romFieldCursor);
878
879
/* interested in objects and all kinds of arrays */
880
if ((IS_REF_OR_VAL_SIGNATURE(J9UTF8_DATA(sigUTF)[0]))
881
|| ('[' == J9UTF8_DATA(sigUTF)[0])
882
) {
883
numberOfReferences += 1;
884
885
/* get address of next field */
886
j9object_t* address = (j9object_t*)vm->internalVMFunctions->staticFieldAddress(currentThread, clazz, J9UTF8_DATA(nameUTF), J9UTF8_LENGTH(nameUTF), J9UTF8_DATA(sigUTF), J9UTF8_LENGTH(sigUTF), NULL, NULL, J9_LOOK_NO_JAVA, NULL);
887
888
/* an address must be in gc scan range */
889
if (!((address >= sectionStart) && (address < sectionEnd))) {
890
result = J9MODRON_GCCHK_RC_CLASS_STATICS_REFERENCE_IS_NOT_IN_SCANNING_RANGE;
891
GC_CheckError error(clazz, address, _cycle, _currentCheck, "Class ", result, _cycle->nextErrorCount());
892
_reporter->report(&error);
893
}
894
895
/* check only if we have an object */
896
if (NULL != *address) {
897
U_8* toSearchString = J9UTF8_DATA(sigUTF);
898
UDATA toSearchLength = J9UTF8_LENGTH(sigUTF);
899
900
if ('L' == J9UTF8_DATA(sigUTF)[0]) {
901
/* Convert signature to class name:
902
* Entering 'L' as well as closing ';' must be removed to get a proper class name
903
*/
904
toSearchString += 1;
905
toSearchLength -= 2;
906
}
907
908
J9Class* classToCast = vm->internalVMFunctions->internalFindClassUTF8(currentThread, toSearchString, toSearchLength, classLoader, J9_FINDCLASS_FLAG_EXISTING_ONLY);
909
910
/*
911
* If class with name extracted from RAM Static field's signature is not found in current classloader (classToCast == NULL)
912
* unfortunately it is no way to find it. Skip check for this case.
913
* However if class found - it must fit object's class
914
*/
915
if (NULL != classToCast) {
916
if (0 == instanceOfOrCheckCast(J9GC_J9OBJECT_CLAZZ_VM(*address, vm), classToCast)) {
917
result = J9MODRON_GCCHK_RC_CLASS_STATICS_FIELD_POINTS_WRONG_OBJECT;
918
GC_CheckError error(clazz, address, _cycle, _currentCheck, "Class ", result, _cycle->nextErrorCount());
919
_reporter->report(&error);
920
}
921
}
922
}
923
}
924
}
925
romFieldCursor = romFieldsNextDo(&state);
926
}
927
928
if (numberOfReferences != romClazz->objectStaticCount) {
929
result = J9MODRON_GCCHK_RC_CLASS_STATICS_WRONG_NUMBER_OF_REFERENCES;
930
GC_CheckError error(clazz, _cycle, _currentCheck, "Class ", result, _cycle->nextErrorCount());
931
_reporter->report(&error);
932
}
933
}
934
935
return result;
936
}
937
938
/**
939
* Verify a slot (double-indirect object pointer) in the object heap.
940
*
941
* @param objectIndirect the slot to be verified
942
* @param segment the segment containing the object whose slot is being verified
943
* @param objectIndirectBase the object whose slot is being verified
944
*
945
* @return #J9MODRON_SLOT_ITERATOR_OK
946
*/
947
UDATA
948
GC_CheckEngine::checkSlotObjectHeap(J9JavaVM *javaVM, J9Object *objectPtr, fj9object_t *objectIndirect, J9MM_IterateRegionDescriptor *regionDesc, J9Object *objectIndirectBase)
949
{
950
MM_GCExtensions * extensions = MM_GCExtensions::getExtensions(javaVM);
951
952
if (NULL == objectPtr) {
953
return J9MODRON_SLOT_ITERATOR_OK;
954
}
955
956
UDATA result = checkObjectIndirect(javaVM, objectPtr);
957
958
/* might the heap include dark matter? If so, ignore most errors */
959
if (_cycle->getMiscFlags() & J9MODRON_GCCHK_MISC_DARKMATTER) {
960
/* only report a subset of errors -- the rest are expected to be found in dark matter */
961
switch (result) {
962
case J9MODRON_GCCHK_RC_OK:
963
case J9MODRON_GCCHK_RC_UNALIGNED:
964
case J9MODRON_GCCHK_RC_STACK_OBJECT:
965
break;
966
967
/* These errors are unlikely, but not impossible to find in dark matter.
968
* Leave them enabled because they can help find real corruption
969
*/
970
case J9MODRON_GCCHK_RC_NOT_FOUND: /* can happen due to contraction */
971
break;
972
973
/* other errors in possible dark matter are expected, so ignore them and don't
974
* investigate this pointer any further
975
*/
976
default:
977
return J9MODRON_SLOT_ITERATOR_OK;
978
}
979
}
980
981
if (J9MODRON_GCCHK_RC_OK != result) {
982
const char *elementName = extensions->objectModel.isIndexable(objectIndirectBase) ? "IObject " : "Object ";
983
GC_CheckError error(objectIndirectBase, objectIndirect, _cycle, _currentCheck, (char *)elementName, result, _cycle->nextErrorCount());
984
_reporter->report(&error);
985
return J9MODRON_SLOT_ITERATOR_OK;
986
}
987
988
#if defined(J9VM_GC_GENERATIONAL)
989
if (MM_GCExtensions::getExtensions(javaVM)->scavengerEnabled) {
990
/* Old objects with references to newspace should have their remembered bit ON */
991
/* TODO: add a quick check to see if the object is in same region as the referring object? */
992
J9MM_IterateRegionDescriptor objectRegion;
993
if (!findRegionForPointer(javaVM, objectPtr, &objectRegion)) {
994
/* should be impossible, since checkObjectIndirect() already verified that the object exists */
995
const char *elementName = extensions->objectModel.isIndexable(objectIndirectBase) ? "IObject " : "Object ";
996
GC_CheckError error(objectIndirectBase, objectIndirect, _cycle, _currentCheck, (char *)elementName, J9MODRON_GCCHK_RC_NOT_FOUND, _cycle->nextErrorCount());
997
_reporter->report(&error);
998
return J9MODRON_SLOT_ITERATOR_OK;
999
}
1000
UDATA regionType = ((MM_HeapRegionDescriptor*)regionDesc->id)->getTypeFlags();
1001
UDATA objectRegionType = ((MM_HeapRegionDescriptor*)objectRegion.id)->getTypeFlags();
1002
1003
if (objectPtr && (regionType & MEMORY_TYPE_OLD) && (objectRegionType & MEMORY_TYPE_NEW) && !extensions->objectModel.isRemembered(objectIndirectBase)) {
1004
const char *elementName = extensions->objectModel.isIndexable(objectIndirectBase) ? "IObject " : "Object ";
1005
GC_CheckError error(objectIndirectBase, objectIndirect, _cycle, _currentCheck, (char *)elementName, J9MODRON_GCCHK_RC_NEW_POINTER_NOT_REMEMBERED, _cycle->nextErrorCount());
1006
_reporter->report(&error);
1007
return J9MODRON_SLOT_ITERATOR_OK;
1008
}
1009
1010
/* Old objects that point to objects with old bit OFF should have remembered bit ON */
1011
if (objectPtr && (regionType & MEMORY_TYPE_OLD) && !extensions->isOld(objectPtr) && !extensions->objectModel.isRemembered(objectIndirectBase)) {
1012
const char *elementName = extensions->objectModel.isIndexable(objectIndirectBase) ? "IObject " : "Object ";
1013
GC_CheckError error(objectIndirectBase, objectIndirect, _cycle, _currentCheck, (char *)elementName, J9MODRON_GCCHK_RC_REMEMBERED_SET_OLD_OBJECT, _cycle->nextErrorCount());
1014
_reporter->report(&error);
1015
return J9MODRON_SLOT_ITERATOR_OK;
1016
}
1017
}
1018
#endif /* J9VM_GC_GENERATIONAL */
1019
1020
return J9MODRON_SLOT_ITERATOR_OK;
1021
}
1022
1023
/**
1024
* Verify an object that was encountered directly while walking the heap.
1025
* Perform some basic checks on the object by calling @ref checkJ9Object,
1026
* and if the object appears to be valid, walk its slots and verify each
1027
* of them. We must be very careful here to avoid crashing gccheck itself.
1028
*
1029
* @param object the object to verify
1030
* @regionDesc the region which contains the object
1031
*
1032
* @return #J9MODRON_SLOT_ITERATOR_OK on success, #J9MODRON_SLOT_ITERATOR_UNRECOVERABLE_ERROR on failure
1033
*/
1034
UDATA
1035
GC_CheckEngine::checkObjectHeap(J9JavaVM *javaVM, J9MM_IterateObjectDescriptor *objectDesc, J9MM_IterateRegionDescriptor *regionDesc)
1036
{
1037
MM_GCExtensions * extensions = MM_GCExtensions::getExtensions(javaVM);
1038
J9Class* clazz = NULL;
1039
UDATA result = J9MODRON_SLOT_ITERATOR_OK;
1040
1041
/* If we encounter a hole with size 0, the heap walk will enter an infinite loop -- prevent this. */
1042
/* Size of hole can not be larger then rest of the region */
1043
if (FALSE == objectDesc->isObject) {
1044
if ((0 == objectDesc->size) || (objectDesc->size > ((UDATA)regionDesc->regionStart + regionDesc->regionSize - (UDATA)objectDesc->object))) {
1045
GC_CheckError error(objectDesc->object, _cycle, _currentCheck, "Object ", J9MODRON_GCCHK_RC_DEAD_OBJECT_SIZE, _cycle->nextErrorCount());
1046
_reporter->report(&error);
1047
_reporter->reportHeapWalkError(&error, _lastHeapObject1, _lastHeapObject2, _lastHeapObject3);
1048
return J9MODRON_SLOT_ITERATOR_UNRECOVERABLE_ERROR;
1049
}
1050
return J9MODRON_SLOT_ITERATOR_OK;
1051
}
1052
1053
result = checkJ9Object(javaVM, objectDesc->object, regionDesc, _cycle->getCheckFlags());
1054
if (J9MODRON_GCCHK_RC_OK != result) {
1055
const char *elementName = extensions->objectModel.isIndexable(objectDesc->object) ? "IObject " : "Object ";
1056
GC_CheckError error(objectDesc->object, _cycle, _currentCheck, (char *)elementName, result, _cycle->nextErrorCount());
1057
_reporter->report(&error);
1058
_reporter->reportHeapWalkError(&error, _lastHeapObject1, _lastHeapObject2, _lastHeapObject3);
1059
return J9MODRON_SLOT_ITERATOR_UNRECOVERABLE_ERROR;
1060
}
1061
1062
clazz = J9GC_J9OBJECT_CLAZZ_VM(objectDesc->object, javaVM);
1063
result = checkJ9ClassPointer(javaVM, clazz, true);
1064
if (J9MODRON_GCCHK_RC_OK == result) {
1065
ObjectSlotIteratorCallbackUserData userData;
1066
userData.engine = this;
1067
userData.regionDesc = regionDesc;
1068
userData.result = result;
1069
javaVM->memoryManagerFunctions->j9mm_iterate_object_slots(javaVM, _portLibrary, objectDesc, j9mm_iterator_flag_exclude_null_refs, check_objectSlotsCallback, &userData);
1070
result = userData.result;
1071
}
1072
1073
/* check Ownable Synchronizer Object consistency */
1074
if ((OBJECT_HEADER_SHAPE_MIXED == J9GC_CLASS_SHAPE(clazz)) && (0 != (J9CLASS_FLAGS(clazz) & J9AccClassOwnableSynchronizer))) {
1075
if (NULL == extensions->accessBarrier->isObjectInOwnableSynchronizerList(objectDesc->object)) {
1076
PORT_ACCESS_FROM_PORT(_portLibrary);
1077
j9tty_printf(PORTLIB, " <gc check: found Ownable SynchronizerObject %p is not on the list >\n", objectDesc->object);
1078
} else {
1079
_ownableSynchronizerObjectCountOnHeap += 1;
1080
}
1081
}
1082
1083
if (J9MODRON_SLOT_ITERATOR_OK == result) {
1084
/* this heap object is OK. Record it in the cache in case we find a pointer to it soon */
1085
UDATA cacheIndex = ((UDATA)objectDesc->object) % OBJECT_CACHE_SIZE;
1086
_checkedObjectCache[cacheIndex] = objectDesc->object;
1087
}
1088
1089
return result;
1090
}
1091
1092
/**
1093
* Verify a slot (double-indirect object pointer).
1094
*
1095
* @param objectIndirect the slot to be verified
1096
* @param objectIndirectBase the object which contains the slot
1097
*
1098
* @return #J9MODRON_SLOT_ITERATOR_OK
1099
*/
1100
UDATA
1101
GC_CheckEngine::checkSlotVMThread(J9JavaVM *javaVM, J9Object **objectIndirect, void *objectIndirectBase, UDATA objectType, GC_VMThreadIterator *vmthreadIterator)
1102
{
1103
J9Object *objectPtr = *objectIndirect;
1104
1105
UDATA result = checkObjectIndirect(javaVM, objectPtr);
1106
if (J9MODRON_GCCHK_RC_STACK_OBJECT == result) {
1107
if (vmthreaditerator_state_monitor_records != vmthreadIterator->getState()) {
1108
GC_CheckError error(objectIndirectBase, objectIndirect, _cycle, _currentCheck, result, _cycle->nextErrorCount(), objectType);
1109
_reporter->report(&error);
1110
}
1111
} else if (J9MODRON_GCCHK_RC_OK != result) {
1112
GC_CheckError error(objectIndirectBase, objectIndirect, _cycle, _currentCheck, result, _cycle->nextErrorCount(), objectType);
1113
_reporter->report(&error);
1114
}
1115
return J9MODRON_SLOT_ITERATOR_OK;
1116
}
1117
1118
/**
1119
* Verify a slot (double-indirect object pointer) on the stack.
1120
*
1121
* @param objectIndirect the slot to be verified
1122
* @param vmThread pointer to the thread whose stack contains the slot
1123
*
1124
* @return #J9MODRON_SLOT_ITERATOR_OK
1125
*/
1126
UDATA
1127
GC_CheckEngine::checkSlotStack(J9JavaVM *javaVM, J9Object **objectIndirect, J9VMThread *vmThread, const void *stackLocation)
1128
{
1129
J9Object *objectPtr = *objectIndirect;
1130
1131
UDATA result = checkObjectIndirect(javaVM, objectPtr);
1132
1133
if (J9MODRON_GCCHK_RC_STACK_OBJECT == result) {
1134
result = checkStackObject(javaVM, objectPtr);
1135
}
1136
if (J9MODRON_GCCHK_RC_OK != result) {
1137
GC_CheckError error(vmThread, objectIndirect, stackLocation, _cycle, _currentCheck, result, _cycle->nextErrorCount());
1138
_reporter->report(&error);
1139
1140
return J9MODRON_SLOT_ITERATOR_RECOVERABLE_ERROR;
1141
}
1142
return J9MODRON_SLOT_ITERATOR_OK;
1143
}
1144
1145
/**
1146
* Verify a slot in a pool.
1147
*
1148
* @param objectIndirect the slot to be verified
1149
* @param objectIndirectBase the "object" containing the slot. Maybe or may not
1150
* be an actual <code>J9Object</code> (e.g. could be a <code>J9ClassLoader</code>
1151
* struct). Note that this must be a remote pointer.
1152
*
1153
* @todo objectIndirectBase pointer should probably be changed to void *
1154
*
1155
* @return #J9MODRON_SLOT_ITERATOR_OK
1156
*/
1157
UDATA
1158
GC_CheckEngine::checkSlotPool(J9JavaVM *javaVM, J9Object **objectIndirect, void *objectIndirectBase)
1159
{
1160
J9Object *objectPtr = *objectIndirect;
1161
UDATA result = checkObjectIndirect(javaVM, objectPtr);
1162
if (J9MODRON_GCCHK_RC_OK != result) {
1163
GC_CheckError error(objectIndirectBase, objectIndirect, _cycle, _currentCheck, result, _cycle->nextErrorCount(), check_type_other);
1164
_reporter->report(&error);
1165
}
1166
return J9MODRON_SLOT_ITERATOR_OK;
1167
}
1168
1169
/**
1170
* Verify a remembered set slot.
1171
*
1172
* @param objectIndirect the slot to be verified
1173
* @param objectIndirectBase the sublist puddle which contains the slot
1174
*
1175
* @return #J9MODRON_SLOT_ITERATOR_OK
1176
*/
1177
UDATA
1178
GC_CheckEngine::checkSlotRememberedSet(J9JavaVM *javaVM, J9Object **objectIndirect, MM_SublistPuddle *puddle)
1179
{
1180
MM_GCExtensions* extensions = MM_GCExtensions::getExtensions(javaVM);
1181
J9Object *objectPtr = *objectIndirect;
1182
1183
if (_cycle->getMiscFlags() & J9MODRON_GCCHK_MISC_MIDSCAVENGE) {
1184
/* during a scavenge, some RS entries may be tagged -- remove the tag */
1185
if ( DEFERRED_RS_REMOVE_FLAG == (((UDATA)objectPtr) & DEFERRED_RS_REMOVE_FLAG) ) {
1186
objectPtr = (J9Object*)( ((UDATA)objectPtr) & ~(UDATA)DEFERRED_RS_REMOVE_FLAG );
1187
}
1188
}
1189
1190
UDATA result = checkObjectIndirect(javaVM, objectPtr);
1191
if (J9MODRON_GCCHK_RC_OK != result) {
1192
GC_CheckError error(puddle, objectIndirect, _cycle, _currentCheck, result, _cycle->nextErrorCount());
1193
_reporter->report(&error);
1194
return J9MODRON_SLOT_ITERATOR_OK;
1195
}
1196
1197
/* Additional checks for the remembered set */
1198
if (NULL != objectPtr) {
1199
J9MM_IterateRegionDescriptor objectRegion;
1200
if (!findRegionForPointer(javaVM, objectPtr, &objectRegion)) {
1201
/* shouldn't happen, since checkObjectIndirect() already verified this object */
1202
GC_CheckError error(puddle, objectIndirect, _cycle, _currentCheck, J9MODRON_GCCHK_RC_NOT_FOUND, _cycle->nextErrorCount());
1203
_reporter->report(&error);
1204
return J9MODRON_SLOT_ITERATOR_OK;
1205
}
1206
1207
/* we shouldn't have newspace references in the remembered set */
1208
UDATA regionType = ((MM_HeapRegionDescriptor*)objectRegion.id)->getTypeFlags();
1209
1210
if (regionType & MEMORY_TYPE_NEW) {
1211
GC_CheckError error(puddle, objectIndirect, _cycle, _currentCheck, J9MODRON_GCCHK_RC_REMEMBERED_SET_WRONG_SEGMENT, _cycle->nextErrorCount());
1212
_reporter->report(&error);
1213
return J9MODRON_SLOT_ITERATOR_OK;
1214
}
1215
1216
/* content of Remembered Set should be Old and Remembered */
1217
if ( !(extensions->isOld(objectPtr) && extensions->objectModel.isRemembered(objectPtr))) {
1218
GC_CheckError error(puddle, objectIndirect, _cycle, _currentCheck, J9MODRON_GCCHK_RC_REMEMBERED_SET_FLAGS, _cycle->nextErrorCount());
1219
_reporter->report(&error);
1220
_reporter->reportObjectHeader(&error, objectPtr, NULL);
1221
return J9MODRON_SLOT_ITERATOR_OK;
1222
}
1223
}
1224
1225
return J9MODRON_SLOT_ITERATOR_OK;
1226
}
1227
1228
/**
1229
* Verify a unfinalized object list slot.
1230
*
1231
* @param objectIndirect the slot to be verified
1232
* @param currentList the unfinalizedObjectList which contains the slot
1233
*
1234
* @return #J9MODRON_SLOT_ITERATOR_OK
1235
*/
1236
UDATA
1237
GC_CheckEngine::checkSlotUnfinalizedList(J9JavaVM *javaVM, J9Object **objectIndirect, MM_UnfinalizedObjectList *currentList)
1238
{
1239
J9Object *objectPtr = *objectIndirect;
1240
1241
UDATA result = checkObjectIndirect(javaVM, objectPtr);
1242
if (J9MODRON_GCCHK_RC_OK != result) {
1243
GC_CheckError error(currentList, objectIndirect, _cycle, _currentCheck, result, _cycle->nextErrorCount());
1244
_reporter->report(&error);
1245
return J9MODRON_SLOT_ITERATOR_OK;
1246
}
1247
1248
return J9MODRON_SLOT_ITERATOR_OK;
1249
}
1250
1251
UDATA
1252
GC_CheckEngine::checkSlotOwnableSynchronizerList(J9JavaVM *javaVM, J9Object **objectIndirect, MM_OwnableSynchronizerObjectList *currentList)
1253
{
1254
J9Object *objectPtr = *objectIndirect;
1255
UDATA rc = J9MODRON_SLOT_ITERATOR_OK;
1256
1257
_ownableSynchronizerObjectCountOnList += 1;
1258
1259
UDATA result = checkObjectIndirect(javaVM, objectPtr);
1260
if (J9MODRON_GCCHK_RC_OK != result) {
1261
GC_CheckError error(currentList, objectIndirect, _cycle, _currentCheck, result, _cycle->nextErrorCount());
1262
_reporter->report(&error);
1263
} else {
1264
J9Class *instanceClass = J9GC_J9OBJECT_CLAZZ_VM(objectPtr, javaVM);
1265
if (0 == (J9CLASS_FLAGS(instanceClass) & J9AccClassOwnableSynchronizer)) {
1266
GC_CheckError error(currentList, objectIndirect, _cycle, _currentCheck, J9MODRON_GCCHK_RC_INVALID_FLAGS, _cycle->nextErrorCount());
1267
_reporter->report(&error);
1268
}
1269
J9VMThread* currentThread = javaVM->internalVMFunctions->currentVMThread(javaVM);
1270
J9ClassLoader* classLoader = instanceClass->classLoader;
1271
const char* aosClassName = "java/util/concurrent/locks/AbstractOwnableSynchronizer";
1272
1273
J9Class* castClass = javaVM->internalVMFunctions->internalFindClassUTF8(currentThread, (U_8*) aosClassName, strlen(aosClassName), classLoader, J9_FINDCLASS_FLAG_EXISTING_ONLY);
1274
if (NULL != castClass) {
1275
if (0 == instanceOfOrCheckCast(instanceClass, castClass)) {
1276
GC_CheckError error(currentList, objectIndirect, _cycle, _currentCheck, J9MODRON_GCCHK_RC_OWNABLE_SYNCHRONIZER_INVALID_CLASS, _cycle->nextErrorCount());
1277
_reporter->report(&error);
1278
}
1279
}
1280
}
1281
return rc;
1282
}
1283
1284
/**
1285
* Verify a finalized object queue slot.
1286
*
1287
* @param objectIndirect the slot to be verified
1288
* @param listManager the MM_FinalizeListManager which contains the slot
1289
*
1290
* @return #J9MODRON_SLOT_ITERATOR_OK
1291
*/
1292
UDATA
1293
GC_CheckEngine::checkSlotFinalizableList(J9JavaVM *javaVM, J9Object **objectIndirect, GC_FinalizeListManager *listManager)
1294
{
1295
J9Object *objectPtr = *objectIndirect;
1296
1297
UDATA result = checkObjectIndirect(javaVM, objectPtr);
1298
if (J9MODRON_GCCHK_RC_OK != result) {
1299
GC_CheckError error(listManager, objectIndirect, _cycle, _currentCheck, result, _cycle->nextErrorCount());
1300
_reporter->report(&error);
1301
return J9MODRON_SLOT_ITERATOR_OK;
1302
}
1303
1304
return J9MODRON_SLOT_ITERATOR_OK;
1305
}
1306
1307
/**
1308
* Start of a check
1309
* This function should be called before any of the check functions in
1310
* the engine. It ensures that the heap is walkable and TLHs are flushed
1311
*/
1312
void
1313
GC_CheckEngine::startCheckCycle(J9JavaVM *javaVM, GC_CheckCycle *checkCycle)
1314
{
1315
_cycle = checkCycle;
1316
_currentCheck = NULL;
1317
#if defined(J9VM_GC_MODRON_SCAVENGER)
1318
_scavengerBackout = false;
1319
_rsOverflowState = false;
1320
#endif /* J9VM_GC_MODRON_SCAVENGER */
1321
clearPreviousObjects();
1322
clearRegionDescription(&_regionDesc);
1323
clearCheckedCache();
1324
1325
clearCountsForOwnableSynchronizerObjects();
1326
1327
/* Flush any VM level changes to prepare for a safe slot walk */
1328
TRIGGER_J9HOOK_MM_PRIVATE_WALK_HEAP_START(MM_GCExtensions::getExtensions(javaVM)->privateHookInterface, javaVM->omrVM);
1329
}
1330
1331
/**
1332
* End of a check
1333
* This function should be called at the end of a check. It triggers
1334
* the hook to inform interested parties that a heap walk has occurred.
1335
*/
1336
void GC_CheckEngine::endCheckCycle(J9JavaVM *javaVM)
1337
{
1338
TRIGGER_J9HOOK_MM_PRIVATE_WALK_HEAP_END(MM_GCExtensions::getExtensions(javaVM)->privateHookInterface, javaVM->omrVM);
1339
}
1340
1341
/**
1342
* Advance to the next stage of checking.
1343
* Sets the context for the next stage of checking. Called every time verification
1344
* moves to a new structure.
1345
*
1346
* @param check Pointer to the check we're about to start
1347
*/
1348
void
1349
GC_CheckEngine::startNewCheck(GC_Check *check)
1350
{
1351
_currentCheck = check;
1352
clearPreviousObjects();
1353
}
1354
1355
/**
1356
* Ensure the GC internal scope pointers refer to objects within the scope.
1357
*
1358
* @param scopedMemorySpace The memory space with the internal pointers to verify.
1359
* @param regionDesc The region descriptor which should contain all pointers.
1360
*/
1361
1362
/**
1363
* Create new instance of check object, using the specified reporter object,
1364
* and the specified port library.
1365
*/
1366
GC_CheckEngine *
1367
GC_CheckEngine::newInstance(J9JavaVM *javaVM, GC_CheckReporter *reporter)
1368
{
1369
MM_Forge *forge = MM_GCExtensions::getExtensions(javaVM)->getForge();
1370
1371
GC_CheckEngine *check = (GC_CheckEngine *)forge->allocate(sizeof(GC_CheckEngine), MM_AllocationCategory::DIAGNOSTIC, J9_GET_CALLSITE());
1372
if (check) {
1373
check = new(check) GC_CheckEngine(javaVM, reporter);
1374
if (!check->initialize()) {
1375
check->kill();
1376
check = NULL;
1377
}
1378
}
1379
return check;
1380
}
1381
1382
bool
1383
GC_CheckEngine::initialize()
1384
{
1385
clearPreviousObjects();
1386
clearRegionDescription(&_regionDesc);
1387
clearCheckedCache();
1388
return true;
1389
}
1390
1391
void
1392
GC_CheckEngine::kill()
1393
{
1394
MM_Forge *forge = MM_GCExtensions::getExtensions(_javaVM)->getForge();
1395
if(_reporter) {
1396
_reporter->kill();
1397
}
1398
forge->free(this);
1399
}
1400
1401
/**
1402
* Determine whether or not the a verbose stack dump should always be displayed.
1403
*
1404
* @return true if verbose stack dump is always displayed.
1405
* @return false if verbose stack dump is only displayed on error.
1406
*/
1407
bool
1408
GC_CheckEngine::isStackDumpAlwaysDisplayed()
1409
{
1410
if (NULL == _cycle) {
1411
return false;
1412
}
1413
return (J9MODRON_GCCHK_MISC_ALWAYS_DUMP_STACK == (_cycle->getMiscFlags() & J9MODRON_GCCHK_MISC_ALWAYS_DUMP_STACK));
1414
}
1415
1416
/**
1417
* Copy the information from one regionDescription to the other.
1418
* @param from - the source region
1419
* @param to - the destination region
1420
*
1421
*/
1422
void
1423
GC_CheckEngine::copyRegionDescription(J9MM_IterateRegionDescriptor* from, J9MM_IterateRegionDescriptor* to)
1424
{
1425
to->name = from->name;
1426
to->id = from->id;
1427
to->objectAlignment = from->objectAlignment;
1428
to->objectMinimumSize = from->objectMinimumSize;
1429
to->regionStart = from->regionStart;
1430
to->regionSize = from->regionSize;
1431
}
1432
1433
/**
1434
* Clear the region
1435
* @param toClear - the region to clear
1436
*
1437
*/
1438
void
1439
GC_CheckEngine::clearRegionDescription(J9MM_IterateRegionDescriptor* toClear)
1440
{
1441
memset(toClear, 0, sizeof(J9MM_IterateRegionDescriptor));
1442
}
1443
1444
void
1445
GC_CheckEngine::clearCheckedCache()
1446
{
1447
memset(_checkedClassCache, 0, sizeof(_checkedClassCache));
1448
memset(_checkedClassCacheAllowUndead, 0, sizeof(_checkedClassCacheAllowUndead));
1449
memset(_checkedObjectCache, 0, sizeof(_checkedObjectCache));
1450
}
1451
1452
static jvmtiIterationControl
1453
check_objectSlotsCallback(J9JavaVM *javaVM, J9MM_IterateObjectDescriptor *objectDesc, J9MM_IterateObjectRefDescriptor *refDesc, void *userData)
1454
{
1455
ObjectSlotIteratorCallbackUserData* castUserData = (ObjectSlotIteratorCallbackUserData*)userData;
1456
castUserData->result = castUserData->engine->checkSlotObjectHeap(javaVM, (J9Object *)refDesc->object, (fj9object_t*)refDesc->fieldAddress, castUserData->regionDesc, objectDesc->object);
1457
if (J9MODRON_GCCHK_RC_OK != castUserData->result) {
1458
return JVMTI_ITERATION_ABORT;
1459
}
1460
return JVMTI_ITERATION_CONTINUE;
1461
}
1462
1463
/**
1464
* Determine whether or not the given object is contained in the given region
1465
*
1466
* @return true if objectPtr is contained in the given region
1467
* @return false otherwise
1468
*/
1469
static bool
1470
isPointerInRegion(void *pointer, J9MM_IterateRegionDescriptor *regionDesc)
1471
{
1472
UDATA regionStart = (UDATA)regionDesc->regionStart;
1473
UDATA regionEnd = regionStart + regionDesc->regionSize;
1474
UDATA address = (UDATA)pointer;
1475
1476
return ((address >= regionStart) && (address < regionEnd));
1477
}
1478
1479