Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openj9
Path: blob/master/runtime/compiler/env/J9KnownObjectTable.cpp
6000 views
1
/*******************************************************************************
2
* Copyright (c) 2000, 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
#include "compile/Compilation.hpp"
24
#include "env/j9fieldsInfo.h"
25
#include "env/KnownObjectTable.hpp"
26
#include "env/StackMemoryRegion.hpp"
27
#include "env/TRMemory.hpp"
28
#include "env/VMAccessCriticalSection.hpp"
29
#include "env/VMJ9.h"
30
#include "infra/Assert.hpp"
31
#include "j9.h"
32
#if defined(J9VM_OPT_JITSERVER)
33
#include "control/CompilationRuntime.hpp"
34
#endif /* defined(J9VM_OPT_JITSERVER) */
35
36
37
J9::KnownObjectTable::KnownObjectTable(TR::Compilation *comp) :
38
OMR::KnownObjectTableConnector(comp),
39
_references(comp->trMemory()),
40
_stableArrayRanks(comp->trMemory())
41
{
42
_references.add(NULL); // Reserve index zero for NULL
43
}
44
45
46
TR::KnownObjectTable *
47
J9::KnownObjectTable::self()
48
{
49
return static_cast<TR::KnownObjectTable *>(this);
50
}
51
52
TR::KnownObjectTable::Index
53
J9::KnownObjectTable::getEndIndex()
54
{
55
return _references.size();
56
}
57
58
59
bool
60
J9::KnownObjectTable::isNull(Index index)
61
{
62
return index == 0;
63
}
64
65
66
TR::KnownObjectTable::Index
67
J9::KnownObjectTable::getOrCreateIndex(uintptr_t objectPointer)
68
{
69
if (objectPointer == 0)
70
return 0; // Special Index value for NULL
71
72
uint32_t nextIndex = self()->getEndIndex();
73
#if defined(J9VM_OPT_JITSERVER)
74
if (self()->comp()->isOutOfProcessCompilation())
75
{
76
TR_ASSERT_FATAL(false, "It is not safe to call getOrCreateIndex() at the server. The object pointer could have become stale at the client.");
77
auto stream = TR::CompilationInfo::getStream();
78
stream->write(JITServer::MessageType::KnownObjectTable_getOrCreateIndex, objectPointer);
79
auto recv = stream->read<TR::KnownObjectTable::Index, uintptr_t *>();
80
81
TR::KnownObjectTable::Index index = std::get<0>(recv);
82
uintptr_t *objectReferenceLocation = std::get<1>(recv);
83
TR_ASSERT_FATAL(index <= nextIndex, "The KOT index %d at the client is greater than the KOT index %d at the server", index, nextIndex);
84
85
if (index < nextIndex)
86
{
87
return index;
88
}
89
else
90
{
91
updateKnownObjectTableAtServer(index, objectReferenceLocation);
92
}
93
}
94
else
95
#endif /* defined(J9VM_OPT_JITSERVER) */
96
{
97
TR_J9VMBase *fej9 = (TR_J9VMBase *)(self()->fe());
98
TR_ASSERT(fej9->haveAccess(), "Must haveAccess in J9::KnownObjectTable::getOrCreateIndex");
99
100
// Search for existing matching entry
101
//
102
for (uint32_t i = 1; i < nextIndex; i++)
103
if (*_references.element(i) == objectPointer)
104
return i;
105
106
// No luck -- allocate a new one
107
//
108
J9VMThread *thread = getJ9VMThreadFromTR_VM(self()->fe());
109
TR_ASSERT(thread, "assertion failure");
110
_references.setSize(nextIndex+1);
111
_references[nextIndex] = (uintptr_t*)thread->javaVM->internalVMFunctions->j9jni_createLocalRef((JNIEnv*)thread, (j9object_t)objectPointer);
112
}
113
114
return nextIndex;
115
}
116
117
TR::KnownObjectTable::Index
118
J9::KnownObjectTable::getOrCreateIndex(uintptr_t objectPointer, bool isArrayWithConstantElements)
119
{
120
TR::KnownObjectTable::Index index = self()->getOrCreateIndex(objectPointer);
121
if (isArrayWithConstantElements)
122
{
123
self()->addArrayWithConstantElements(index);
124
}
125
return index;
126
}
127
128
129
TR::KnownObjectTable::Index
130
J9::KnownObjectTable::getOrCreateIndexAt(uintptr_t *objectReferenceLocation)
131
{
132
TR::KnownObjectTable::Index result = UNKNOWN;
133
#if defined(J9VM_OPT_JITSERVER)
134
if (self()->comp()->isOutOfProcessCompilation())
135
{
136
auto stream = TR::CompilationInfo::getStream();
137
stream->write(JITServer::MessageType::KnownObjectTable_getOrCreateIndexAt, objectReferenceLocation);
138
auto recv = stream->read<TR::KnownObjectTable::Index, uintptr_t *>();
139
140
result = std::get<0>(recv);
141
uintptr_t *objectReferenceLocationClient = std::get<1>(recv);
142
143
updateKnownObjectTableAtServer(result, objectReferenceLocationClient);
144
}
145
else
146
#endif /* defined(J9VM_OPT_JITSERVER) */
147
{
148
TR::VMAccessCriticalSection getOrCreateIndexAtCriticalSection(self()->comp());
149
uintptr_t objectPointer = *objectReferenceLocation; // Note: object references held as uintptr_t must never be compressed refs
150
result = self()->getOrCreateIndex(objectPointer);
151
}
152
return result;
153
}
154
155
TR::KnownObjectTable::Index
156
J9::KnownObjectTable::getOrCreateIndexAt(uintptr_t *objectReferenceLocation, bool isArrayWithConstantElements)
157
{
158
Index result = self()->getOrCreateIndexAt(objectReferenceLocation);
159
if (isArrayWithConstantElements)
160
self()->addArrayWithConstantElements(result);
161
return result;
162
}
163
164
165
TR::KnownObjectTable::Index
166
J9::KnownObjectTable::getExistingIndexAt(uintptr_t *objectReferenceLocation)
167
{
168
TR::KnownObjectTable::Index result = UNKNOWN;
169
#if defined(J9VM_OPT_JITSERVER)
170
if (self()->comp()->isOutOfProcessCompilation())
171
{
172
auto stream = TR::CompilationInfo::getStream();
173
stream->write(JITServer::MessageType::KnownObjectTable_getExistingIndexAt, objectReferenceLocation);
174
result = std::get<0>(stream->read<TR::KnownObjectTable::Index>());
175
}
176
else
177
#endif /* defined(J9VM_OPT_JITSERVER) */
178
{
179
TR::VMAccessCriticalSection getExistingIndexAtCriticalSection(self()->comp());
180
181
uintptr_t objectPointer = *objectReferenceLocation;
182
for (Index i = 0; i < self()->getEndIndex() && (result == UNKNOWN); i++)
183
{
184
if (self()->getPointer(i) == objectPointer)
185
{
186
result = i;
187
break;
188
}
189
}
190
}
191
return result;
192
}
193
194
195
uintptr_t
196
J9::KnownObjectTable::getPointer(Index index)
197
{
198
if (self()->isNull(index))
199
{
200
return 0; // Assumes host and target representations of null match each other
201
}
202
else
203
{
204
#if defined(J9VM_OPT_JITSERVER)
205
if (self()->comp()->isOutOfProcessCompilation())
206
{
207
TR_ASSERT_FATAL(false, "It is not safe to call getPointer() at the server. The object pointer could have become stale at the client.");
208
auto stream = TR::CompilationInfo::getStream();
209
stream->write(JITServer::MessageType::KnownObjectTable_getPointer, index);
210
return std::get<0>(stream->read<uintptr_t>());
211
}
212
else
213
#endif /* defined(J9VM_OPT_JITSERVER) */
214
{
215
TR_J9VMBase *fej9 = (TR_J9VMBase *)(self()->fe());
216
TR_ASSERT(fej9->haveAccess(), "Must haveAccess in J9::KnownObjectTable::getPointer");
217
return *self()->getPointerLocation(index);
218
}
219
}
220
}
221
222
223
uintptr_t *
224
J9::KnownObjectTable::getPointerLocation(Index index)
225
{
226
TR_ASSERT(index != UNKNOWN && 0 <= index && index < _references.size(), "getPointerLocation(%d): index must be in range 0..%d", (int)index, _references.size());
227
return _references[index];
228
}
229
230
231
#if defined(J9VM_OPT_JITSERVER)
232
void
233
J9::KnownObjectTable::updateKnownObjectTableAtServer(Index index, uintptr_t *objectReferenceLocationClient)
234
{
235
TR_ASSERT_FATAL(self()->comp()->isOutOfProcessCompilation(), "updateKnownObjectTableAtServer should only be called at the server");
236
if (index == TR::KnownObjectTable::UNKNOWN)
237
return;
238
239
TR_ASSERT(objectReferenceLocationClient || (index == 0), "objectReferenceLocationClient should not be NULL (index=%d)", index);
240
241
uint32_t nextIndex = self()->getEndIndex();
242
243
if (index == nextIndex)
244
{
245
_references.setSize(nextIndex+1);
246
_references[nextIndex] = objectReferenceLocationClient;
247
}
248
else if (index < nextIndex)
249
{
250
TR_ASSERT((objectReferenceLocationClient == _references[index]),
251
"comp %p: server _references[%d]=%p is not the same as the client _references[%d]=%p (total size = %u)",
252
self()->comp(), index, _references[index], index, objectReferenceLocationClient, nextIndex);
253
_references[index] = objectReferenceLocationClient;
254
}
255
else
256
{
257
TR_ASSERT_FATAL(false, "index %d from the client is greater than the KOT nextIndex %d at the server", index, nextIndex);
258
}
259
}
260
#endif /* defined(J9VM_OPT_JITSERVER) */
261
262
263
static int32_t simpleNameOffset(const char *className, int32_t len)
264
{
265
int32_t result;
266
for (result = len; result > 0 && className[result-1] != '/'; result--)
267
{}
268
return result;
269
}
270
271
void
272
J9::KnownObjectTable::dumpObjectTo(TR::FILE *file, Index i, const char *fieldName, const char *sep, TR::Compilation *comp, TR_BitVector &visited, TR_VMFieldsInfo **fieldsInfoByIndex, int32_t depth)
273
{
274
TR_ASSERT_FATAL(!comp->isOutOfProcessCompilation(), "dumpObjectTo() should not be executed at the server.");
275
276
TR_J9VMBase *j9fe = (TR_J9VMBase*)self()->fe();
277
int32_t indent = 2*depth;
278
if (comp->getKnownObjectTable()->isNull(i))
279
{
280
// Usually don't care about null fields
281
// trfprintf(file, "%*s%s%snull\n", indent, "", fieldName, sep);
282
return;
283
}
284
else if (visited.isSet(i))
285
{
286
trfprintf(file, "%*s%s%sobj%d\n", indent, "", fieldName, sep, i);
287
return;
288
}
289
else
290
{
291
visited.set(i);
292
293
uintptr_t *ref = self()->getPointerLocation(i);
294
int32_t len; char *className = TR::Compiler->cls.classNameChars(comp, j9fe->getObjectClass(*ref), len);
295
J9MemoryManagerFunctions * mmf = jitConfig->javaVM->memoryManagerFunctions;
296
int32_t hashCode = mmf->j9gc_objaccess_getObjectHashCode(jitConfig->javaVM, (J9Object*)(*ref));
297
298
// Shorten the class name for legibility. The full name is still in the ordinary known-object table dump.
299
//
300
int32_t offs = simpleNameOffset(className, len);
301
trfprintf(file, "%*s%s%sobj%d @ %p hash %8x %.*s", indent, "", fieldName, sep, i, *ref, hashCode, len-offs, className+offs);
302
303
#if defined(J9VM_OPT_METHOD_HANDLE)
304
if (len == 29 && !strncmp("java/lang/invoke/DirectHandle", className, 29))
305
{
306
J9Method *j9method = (J9Method*)J9VMJAVALANGINVOKEPRIMITIVEHANDLE_VMSLOT(j9fe->vmThread(), (J9Object*)(*ref));
307
J9UTF8 *className = J9ROMCLASS_CLASSNAME(J9_CLASS_FROM_METHOD(j9method)->romClass);
308
J9UTF8 *methName = J9ROMMETHOD_NAME(static_cast<TR_J9VM *>(j9fe)->getROMMethodFromRAMMethod(j9method));
309
int32_t offs = simpleNameOffset(utf8Data(className), J9UTF8_LENGTH(className));
310
trfprintf(file, " vmSlot: %.*s.%.*s",
311
J9UTF8_LENGTH(className)-offs, utf8Data(className)+offs,
312
J9UTF8_LENGTH(methName), utf8Data(methName));
313
}
314
#endif /* defined(J9VM_OPT_METHOD_HANDLE) */
315
316
TR_VMFieldsInfo *fieldsInfo = fieldsInfoByIndex[i];
317
if (fieldsInfo)
318
{
319
ListIterator<TR_VMField> primitiveIter(fieldsInfo->getFields());
320
for (TR_VMField *field = primitiveIter.getFirst(); field; field = primitiveIter.getNext())
321
{
322
if (field->isReference())
323
continue;
324
if (!strcmp(field->signature, "I"))
325
trfprintf(file, " %s: %d", field->name, j9fe->getInt32Field(*ref, field->name));
326
}
327
trfprintf(file, "\n");
328
ListIterator<TR_VMField> refIter(fieldsInfo->getFields());
329
for (TR_VMField *field = refIter.getFirst(); field; field = refIter.getNext())
330
{
331
if (field->isReference())
332
{
333
uintptr_t target = j9fe->getReferenceField(*ref, field->name, field->signature);
334
Index targetIndex = self()->getExistingIndexAt(&target);
335
if (targetIndex != UNKNOWN)
336
self()->dumpObjectTo(file, targetIndex, field->name, (field->modifiers & J9AccFinal)? " is " : " = ", comp, visited, fieldsInfoByIndex, depth+1);
337
}
338
}
339
}
340
else
341
{
342
trfprintf(file, "\n");
343
}
344
}
345
}
346
347
348
#if defined(J9VM_OPT_JITSERVER)
349
void
350
J9::KnownObjectTable::getKnownObjectTableDumpInfo(std::vector<TR_KnownObjectTableDumpInfo> &knotDumpInfoList)
351
{
352
TR_ASSERT_FATAL(!self()->comp()->isOutOfProcessCompilation(), "getKnownObjectTableDumpInfo() can not be executed at the server.");
353
354
TR_J9VMBase *j9fe = (TR_J9VMBase*)self()->fe();
355
J9MemoryManagerFunctions * mmf = jitConfig->javaVM->memoryManagerFunctions;
356
TR::KnownObjectTable::Index endIndex = self()->getEndIndex();
357
TR::VMAccessCriticalSection j9KnownObjectTableDumpToCriticalSection(j9fe,
358
TR::VMAccessCriticalSection::tryToAcquireVMAccess,
359
self()->comp());
360
if (j9KnownObjectTableDumpToCriticalSection.hasVMAccess())
361
{
362
uintptr_t *ref = NULL;
363
int32_t len = 0;
364
char *className = NULL;
365
int32_t hashCode = 0;
366
367
for (uint32_t i = 0; i < endIndex; i++)
368
{
369
if (self()->isNull(i))
370
{
371
knotDumpInfoList.push_back(make_tuple(TR_KnownObjectTableDumpInfoStruct(NULL, 0, 0), std::string("")));
372
}
373
else
374
{
375
ref = self()->getPointerLocation(i);
376
hashCode = mmf->j9gc_objaccess_getObjectHashCode(jitConfig->javaVM, (J9Object*)(*ref));
377
className = j9fe->getClassNameChars(j9fe->getObjectClass(*ref), len);
378
379
knotDumpInfoList.push_back(make_tuple(TR_KnownObjectTableDumpInfoStruct(ref, *ref, hashCode), std::string(className, len)));
380
}
381
}
382
}
383
}
384
#endif /* defined(J9VM_OPT_JITSERVER) */
385
386
387
void
388
J9::KnownObjectTable::dumpTo(TR::FILE *file, TR::Compilation *comp)
389
{
390
TR::KnownObjectTable::Index endIndex = self()->getEndIndex();
391
#if defined(J9VM_OPT_JITSERVER)
392
if (comp->isOutOfProcessCompilation())
393
{
394
auto stream = TR::CompilationInfo::getStream();
395
stream->write(JITServer::MessageType::KnownObjectTable_getKnownObjectTableDumpInfo, JITServer::Void());
396
397
auto recv = stream->read<std::vector<TR_KnownObjectTableDumpInfo>>();
398
auto &knotDumpInfoList = std::get<0>(recv);
399
400
uint32_t numOfEntries = knotDumpInfoList.size();
401
TR_ASSERT_FATAL((numOfEntries == endIndex), "The client table size %u is different from the server table size %u", numOfEntries, endIndex);
402
403
trfprintf(file, "<knownObjectTable size=\"%u\"> // ", numOfEntries);
404
int32_t pointerLen = trfprintf(file, "%p", this);
405
trfprintf(file, "\n %-6s %-*s %-*s %-8s Class\n", "id", pointerLen, "JNI Ref", pointerLen, "Address", "Hash");
406
407
for (uint32_t i = 0; i < numOfEntries; i++)
408
{
409
trfprintf(file, " obj%-3d", i);
410
if (!std::get<0>(knotDumpInfoList[i]).ref)
411
trfprintf(file, " %*s NULL\n", pointerLen, "");
412
else
413
{
414
trfprintf(file, " %p %p %8x %.*s\n",
415
std::get<0>(knotDumpInfoList[i]).ref,
416
std::get<0>(knotDumpInfoList[i]).objectPointer,
417
std::get<0>(knotDumpInfoList[i]).hashCode,
418
std::get<1>(knotDumpInfoList[i]).size(),
419
std::get<1>(knotDumpInfoList[i]).c_str());
420
}
421
}
422
trfprintf(file, "</knownObjectTable>\n");
423
424
if (comp->getOption(TR_TraceKnownObjectGraph))
425
{
426
trfprintf(file, "<knownObjectGraph>\n");
427
// JITServer KOT TODO
428
trfprintf(file, "</knownObjectGraph>\n");
429
}
430
}
431
else
432
#endif /* defined(J9VM_OPT_JITSERVER) */
433
{
434
TR_J9VMBase *j9fe = (TR_J9VMBase*)self()->fe();
435
J9MemoryManagerFunctions * mmf = jitConfig->javaVM->memoryManagerFunctions;
436
TR::VMAccessCriticalSection j9KnownObjectTableDumpToCriticalSection(j9fe,
437
TR::VMAccessCriticalSection::tryToAcquireVMAccess,
438
comp);
439
440
if (j9KnownObjectTableDumpToCriticalSection.hasVMAccess())
441
{
442
trfprintf(file, "<knownObjectTable size=\"%d\"> // ", endIndex);
443
int32_t pointerLen = trfprintf(file, "%p", this);
444
trfprintf(file, "\n %-6s %-*s %-*s %-8s Class\n", "id", pointerLen, "JNI Ref", pointerLen, "Address", "Hash");
445
for (Index i = 0; i < endIndex; i++)
446
{
447
trfprintf(file, " obj%-3d", i);
448
if (self()->isNull(i))
449
trfprintf(file, " %*s NULL\n", pointerLen, "");
450
else
451
{
452
uintptr_t *ref = self()->getPointerLocation(i);
453
int32_t len; char *className = TR::Compiler->cls.classNameChars(comp, j9fe->getObjectClass(*ref), len);
454
int32_t hashCode = mmf->j9gc_objaccess_getObjectHashCode(jitConfig->javaVM, (J9Object*)(*ref));
455
trfprintf(file, " %p %p %8x %.*s %s\n", ref, *ref, hashCode, len, className,
456
isArrayWithStableElements(i) ? "(stable array)" : "");
457
}
458
}
459
trfprintf(file, "</knownObjectTable>\n");
460
461
if (comp->getOption(TR_TraceKnownObjectGraph))
462
{
463
trfprintf(file, "<knownObjectGraph>\n");
464
465
Index i;
466
467
{
468
TR::StackMemoryRegion stackMemoryRegion(*comp->trMemory());
469
470
// Collect field info and determine which objects are reachable from other objects
471
//
472
TR_BitVector reachable(endIndex, comp->trMemory(), stackAlloc, notGrowable);
473
TR_VMFieldsInfo **fieldsInfoByIndex = (TR_VMFieldsInfo**)alloca(endIndex * sizeof(TR_VMFieldsInfo*));
474
for (i = 1; i < endIndex; i++)
475
{
476
uintptr_t object = self()->getPointer(i);
477
J9Class *clazz = (J9Class*)j9fe->getObjectClass(object);
478
if (clazz->romClass->modifiers & J9AccClassArray)
479
{
480
fieldsInfoByIndex[i] = NULL;
481
continue; // TODO: Print out what reference arrays contain?
482
}
483
fieldsInfoByIndex[i] = new (comp->trStackMemory()) TR_VMFieldsInfo(comp, clazz, 1, stackAlloc);
484
ListIterator<TR_VMField> fieldIter(fieldsInfoByIndex[i]->getFields());
485
for (TR_VMField *field = fieldIter.getFirst(); field; field = fieldIter.getNext())
486
{
487
// For the purpose of "reachability", we only look at final
488
// fields. The intent is to reduce nondeterminism in the object
489
// graph log.
490
//
491
if (field->isReference() && (field->modifiers & J9AccFinal))
492
{
493
uintptr_t target = j9fe->getReferenceField(object, field->name, field->signature);
494
Index targetIndex = self()->getExistingIndexAt(&target);
495
if (targetIndex != UNKNOWN)
496
reachable.set(targetIndex);
497
}
498
}
499
}
500
501
// At the top level, walk objects not reachable from other objects
502
//
503
TR_BitVector visited(endIndex, comp->trMemory(), stackAlloc, notGrowable);
504
for (i = 1; i < endIndex; i++)
505
{
506
if (!reachable.isSet(i) && !visited.isSet(i))
507
self()->dumpObjectTo(file, i, "", "", comp, visited, fieldsInfoByIndex, 0);
508
}
509
510
} // scope of the stack memory region
511
512
trfprintf(file, "</knownObjectGraph>\n");
513
}
514
}
515
else
516
{
517
trfprintf(file, "<knownObjectTable size=\"%d\"/> // unable to acquire VM access to print table contents\n", endIndex);
518
}
519
}
520
}
521
522
void
523
J9::KnownObjectTable::addStableArray(Index index, int32_t stableArrayRank)
524
{
525
uintptr_t object = self()->getPointer(index);
526
TR_J9VMBase *j9fe = (TR_J9VMBase*)self()->fe();
527
J9Class *clazz = (J9Class*)j9fe->getObjectClass(object);
528
TR_ASSERT_FATAL((clazz->romClass->modifiers & J9AccClassArray), "addStableArray can only be called for arrays\n");
529
530
if (_stableArrayRanks[index] < stableArrayRank)
531
{
532
_stableArrayRanks[index] = stableArrayRank;
533
}
534
}
535
536
bool
537
J9::KnownObjectTable::isArrayWithStableElements(Index index)
538
{
539
TR_ASSERT_FATAL(index != UNKNOWN && 0 <= index && index < self()->getEndIndex(), "isArrayWithStableElements(%d): index must be in range 0..%d", index, self()->getEndIndex());
540
541
return (index < _stableArrayRanks.size() && _stableArrayRanks[index] > 0);
542
}
543
544