Path: blob/master/runtime/compiler/env/J9KnownObjectTable.cpp
6000 views
/*******************************************************************************1* Copyright (c) 2000, 2021 IBM Corp. and others2*3* This program and the accompanying materials are made available under4* the terms of the Eclipse Public License 2.0 which accompanies this5* distribution and is available at https://www.eclipse.org/legal/epl-2.0/6* or the Apache License, Version 2.0 which accompanies this distribution and7* is available at https://www.apache.org/licenses/LICENSE-2.0.8*9* This Source Code may also be made available under the following10* Secondary Licenses when the conditions for such availability set11* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU12* General Public License, version 2 with the GNU Classpath13* Exception [1] and GNU General Public License, version 2 with the14* OpenJDK Assembly Exception [2].15*16* [1] https://www.gnu.org/software/classpath/license.html17* [2] http://openjdk.java.net/legal/assembly-exception.html18*19* 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-exception20*******************************************************************************/2122#include "compile/Compilation.hpp"23#include "env/j9fieldsInfo.h"24#include "env/KnownObjectTable.hpp"25#include "env/StackMemoryRegion.hpp"26#include "env/TRMemory.hpp"27#include "env/VMAccessCriticalSection.hpp"28#include "env/VMJ9.h"29#include "infra/Assert.hpp"30#include "j9.h"31#if defined(J9VM_OPT_JITSERVER)32#include "control/CompilationRuntime.hpp"33#endif /* defined(J9VM_OPT_JITSERVER) */343536J9::KnownObjectTable::KnownObjectTable(TR::Compilation *comp) :37OMR::KnownObjectTableConnector(comp),38_references(comp->trMemory()),39_stableArrayRanks(comp->trMemory())40{41_references.add(NULL); // Reserve index zero for NULL42}434445TR::KnownObjectTable *46J9::KnownObjectTable::self()47{48return static_cast<TR::KnownObjectTable *>(this);49}5051TR::KnownObjectTable::Index52J9::KnownObjectTable::getEndIndex()53{54return _references.size();55}565758bool59J9::KnownObjectTable::isNull(Index index)60{61return index == 0;62}636465TR::KnownObjectTable::Index66J9::KnownObjectTable::getOrCreateIndex(uintptr_t objectPointer)67{68if (objectPointer == 0)69return 0; // Special Index value for NULL7071uint32_t nextIndex = self()->getEndIndex();72#if defined(J9VM_OPT_JITSERVER)73if (self()->comp()->isOutOfProcessCompilation())74{75TR_ASSERT_FATAL(false, "It is not safe to call getOrCreateIndex() at the server. The object pointer could have become stale at the client.");76auto stream = TR::CompilationInfo::getStream();77stream->write(JITServer::MessageType::KnownObjectTable_getOrCreateIndex, objectPointer);78auto recv = stream->read<TR::KnownObjectTable::Index, uintptr_t *>();7980TR::KnownObjectTable::Index index = std::get<0>(recv);81uintptr_t *objectReferenceLocation = std::get<1>(recv);82TR_ASSERT_FATAL(index <= nextIndex, "The KOT index %d at the client is greater than the KOT index %d at the server", index, nextIndex);8384if (index < nextIndex)85{86return index;87}88else89{90updateKnownObjectTableAtServer(index, objectReferenceLocation);91}92}93else94#endif /* defined(J9VM_OPT_JITSERVER) */95{96TR_J9VMBase *fej9 = (TR_J9VMBase *)(self()->fe());97TR_ASSERT(fej9->haveAccess(), "Must haveAccess in J9::KnownObjectTable::getOrCreateIndex");9899// Search for existing matching entry100//101for (uint32_t i = 1; i < nextIndex; i++)102if (*_references.element(i) == objectPointer)103return i;104105// No luck -- allocate a new one106//107J9VMThread *thread = getJ9VMThreadFromTR_VM(self()->fe());108TR_ASSERT(thread, "assertion failure");109_references.setSize(nextIndex+1);110_references[nextIndex] = (uintptr_t*)thread->javaVM->internalVMFunctions->j9jni_createLocalRef((JNIEnv*)thread, (j9object_t)objectPointer);111}112113return nextIndex;114}115116TR::KnownObjectTable::Index117J9::KnownObjectTable::getOrCreateIndex(uintptr_t objectPointer, bool isArrayWithConstantElements)118{119TR::KnownObjectTable::Index index = self()->getOrCreateIndex(objectPointer);120if (isArrayWithConstantElements)121{122self()->addArrayWithConstantElements(index);123}124return index;125}126127128TR::KnownObjectTable::Index129J9::KnownObjectTable::getOrCreateIndexAt(uintptr_t *objectReferenceLocation)130{131TR::KnownObjectTable::Index result = UNKNOWN;132#if defined(J9VM_OPT_JITSERVER)133if (self()->comp()->isOutOfProcessCompilation())134{135auto stream = TR::CompilationInfo::getStream();136stream->write(JITServer::MessageType::KnownObjectTable_getOrCreateIndexAt, objectReferenceLocation);137auto recv = stream->read<TR::KnownObjectTable::Index, uintptr_t *>();138139result = std::get<0>(recv);140uintptr_t *objectReferenceLocationClient = std::get<1>(recv);141142updateKnownObjectTableAtServer(result, objectReferenceLocationClient);143}144else145#endif /* defined(J9VM_OPT_JITSERVER) */146{147TR::VMAccessCriticalSection getOrCreateIndexAtCriticalSection(self()->comp());148uintptr_t objectPointer = *objectReferenceLocation; // Note: object references held as uintptr_t must never be compressed refs149result = self()->getOrCreateIndex(objectPointer);150}151return result;152}153154TR::KnownObjectTable::Index155J9::KnownObjectTable::getOrCreateIndexAt(uintptr_t *objectReferenceLocation, bool isArrayWithConstantElements)156{157Index result = self()->getOrCreateIndexAt(objectReferenceLocation);158if (isArrayWithConstantElements)159self()->addArrayWithConstantElements(result);160return result;161}162163164TR::KnownObjectTable::Index165J9::KnownObjectTable::getExistingIndexAt(uintptr_t *objectReferenceLocation)166{167TR::KnownObjectTable::Index result = UNKNOWN;168#if defined(J9VM_OPT_JITSERVER)169if (self()->comp()->isOutOfProcessCompilation())170{171auto stream = TR::CompilationInfo::getStream();172stream->write(JITServer::MessageType::KnownObjectTable_getExistingIndexAt, objectReferenceLocation);173result = std::get<0>(stream->read<TR::KnownObjectTable::Index>());174}175else176#endif /* defined(J9VM_OPT_JITSERVER) */177{178TR::VMAccessCriticalSection getExistingIndexAtCriticalSection(self()->comp());179180uintptr_t objectPointer = *objectReferenceLocation;181for (Index i = 0; i < self()->getEndIndex() && (result == UNKNOWN); i++)182{183if (self()->getPointer(i) == objectPointer)184{185result = i;186break;187}188}189}190return result;191}192193194uintptr_t195J9::KnownObjectTable::getPointer(Index index)196{197if (self()->isNull(index))198{199return 0; // Assumes host and target representations of null match each other200}201else202{203#if defined(J9VM_OPT_JITSERVER)204if (self()->comp()->isOutOfProcessCompilation())205{206TR_ASSERT_FATAL(false, "It is not safe to call getPointer() at the server. The object pointer could have become stale at the client.");207auto stream = TR::CompilationInfo::getStream();208stream->write(JITServer::MessageType::KnownObjectTable_getPointer, index);209return std::get<0>(stream->read<uintptr_t>());210}211else212#endif /* defined(J9VM_OPT_JITSERVER) */213{214TR_J9VMBase *fej9 = (TR_J9VMBase *)(self()->fe());215TR_ASSERT(fej9->haveAccess(), "Must haveAccess in J9::KnownObjectTable::getPointer");216return *self()->getPointerLocation(index);217}218}219}220221222uintptr_t *223J9::KnownObjectTable::getPointerLocation(Index index)224{225TR_ASSERT(index != UNKNOWN && 0 <= index && index < _references.size(), "getPointerLocation(%d): index must be in range 0..%d", (int)index, _references.size());226return _references[index];227}228229230#if defined(J9VM_OPT_JITSERVER)231void232J9::KnownObjectTable::updateKnownObjectTableAtServer(Index index, uintptr_t *objectReferenceLocationClient)233{234TR_ASSERT_FATAL(self()->comp()->isOutOfProcessCompilation(), "updateKnownObjectTableAtServer should only be called at the server");235if (index == TR::KnownObjectTable::UNKNOWN)236return;237238TR_ASSERT(objectReferenceLocationClient || (index == 0), "objectReferenceLocationClient should not be NULL (index=%d)", index);239240uint32_t nextIndex = self()->getEndIndex();241242if (index == nextIndex)243{244_references.setSize(nextIndex+1);245_references[nextIndex] = objectReferenceLocationClient;246}247else if (index < nextIndex)248{249TR_ASSERT((objectReferenceLocationClient == _references[index]),250"comp %p: server _references[%d]=%p is not the same as the client _references[%d]=%p (total size = %u)",251self()->comp(), index, _references[index], index, objectReferenceLocationClient, nextIndex);252_references[index] = objectReferenceLocationClient;253}254else255{256TR_ASSERT_FATAL(false, "index %d from the client is greater than the KOT nextIndex %d at the server", index, nextIndex);257}258}259#endif /* defined(J9VM_OPT_JITSERVER) */260261262static int32_t simpleNameOffset(const char *className, int32_t len)263{264int32_t result;265for (result = len; result > 0 && className[result-1] != '/'; result--)266{}267return result;268}269270void271J9::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)272{273TR_ASSERT_FATAL(!comp->isOutOfProcessCompilation(), "dumpObjectTo() should not be executed at the server.");274275TR_J9VMBase *j9fe = (TR_J9VMBase*)self()->fe();276int32_t indent = 2*depth;277if (comp->getKnownObjectTable()->isNull(i))278{279// Usually don't care about null fields280// trfprintf(file, "%*s%s%snull\n", indent, "", fieldName, sep);281return;282}283else if (visited.isSet(i))284{285trfprintf(file, "%*s%s%sobj%d\n", indent, "", fieldName, sep, i);286return;287}288else289{290visited.set(i);291292uintptr_t *ref = self()->getPointerLocation(i);293int32_t len; char *className = TR::Compiler->cls.classNameChars(comp, j9fe->getObjectClass(*ref), len);294J9MemoryManagerFunctions * mmf = jitConfig->javaVM->memoryManagerFunctions;295int32_t hashCode = mmf->j9gc_objaccess_getObjectHashCode(jitConfig->javaVM, (J9Object*)(*ref));296297// Shorten the class name for legibility. The full name is still in the ordinary known-object table dump.298//299int32_t offs = simpleNameOffset(className, len);300trfprintf(file, "%*s%s%sobj%d @ %p hash %8x %.*s", indent, "", fieldName, sep, i, *ref, hashCode, len-offs, className+offs);301302#if defined(J9VM_OPT_METHOD_HANDLE)303if (len == 29 && !strncmp("java/lang/invoke/DirectHandle", className, 29))304{305J9Method *j9method = (J9Method*)J9VMJAVALANGINVOKEPRIMITIVEHANDLE_VMSLOT(j9fe->vmThread(), (J9Object*)(*ref));306J9UTF8 *className = J9ROMCLASS_CLASSNAME(J9_CLASS_FROM_METHOD(j9method)->romClass);307J9UTF8 *methName = J9ROMMETHOD_NAME(static_cast<TR_J9VM *>(j9fe)->getROMMethodFromRAMMethod(j9method));308int32_t offs = simpleNameOffset(utf8Data(className), J9UTF8_LENGTH(className));309trfprintf(file, " vmSlot: %.*s.%.*s",310J9UTF8_LENGTH(className)-offs, utf8Data(className)+offs,311J9UTF8_LENGTH(methName), utf8Data(methName));312}313#endif /* defined(J9VM_OPT_METHOD_HANDLE) */314315TR_VMFieldsInfo *fieldsInfo = fieldsInfoByIndex[i];316if (fieldsInfo)317{318ListIterator<TR_VMField> primitiveIter(fieldsInfo->getFields());319for (TR_VMField *field = primitiveIter.getFirst(); field; field = primitiveIter.getNext())320{321if (field->isReference())322continue;323if (!strcmp(field->signature, "I"))324trfprintf(file, " %s: %d", field->name, j9fe->getInt32Field(*ref, field->name));325}326trfprintf(file, "\n");327ListIterator<TR_VMField> refIter(fieldsInfo->getFields());328for (TR_VMField *field = refIter.getFirst(); field; field = refIter.getNext())329{330if (field->isReference())331{332uintptr_t target = j9fe->getReferenceField(*ref, field->name, field->signature);333Index targetIndex = self()->getExistingIndexAt(&target);334if (targetIndex != UNKNOWN)335self()->dumpObjectTo(file, targetIndex, field->name, (field->modifiers & J9AccFinal)? " is " : " = ", comp, visited, fieldsInfoByIndex, depth+1);336}337}338}339else340{341trfprintf(file, "\n");342}343}344}345346347#if defined(J9VM_OPT_JITSERVER)348void349J9::KnownObjectTable::getKnownObjectTableDumpInfo(std::vector<TR_KnownObjectTableDumpInfo> &knotDumpInfoList)350{351TR_ASSERT_FATAL(!self()->comp()->isOutOfProcessCompilation(), "getKnownObjectTableDumpInfo() can not be executed at the server.");352353TR_J9VMBase *j9fe = (TR_J9VMBase*)self()->fe();354J9MemoryManagerFunctions * mmf = jitConfig->javaVM->memoryManagerFunctions;355TR::KnownObjectTable::Index endIndex = self()->getEndIndex();356TR::VMAccessCriticalSection j9KnownObjectTableDumpToCriticalSection(j9fe,357TR::VMAccessCriticalSection::tryToAcquireVMAccess,358self()->comp());359if (j9KnownObjectTableDumpToCriticalSection.hasVMAccess())360{361uintptr_t *ref = NULL;362int32_t len = 0;363char *className = NULL;364int32_t hashCode = 0;365366for (uint32_t i = 0; i < endIndex; i++)367{368if (self()->isNull(i))369{370knotDumpInfoList.push_back(make_tuple(TR_KnownObjectTableDumpInfoStruct(NULL, 0, 0), std::string("")));371}372else373{374ref = self()->getPointerLocation(i);375hashCode = mmf->j9gc_objaccess_getObjectHashCode(jitConfig->javaVM, (J9Object*)(*ref));376className = j9fe->getClassNameChars(j9fe->getObjectClass(*ref), len);377378knotDumpInfoList.push_back(make_tuple(TR_KnownObjectTableDumpInfoStruct(ref, *ref, hashCode), std::string(className, len)));379}380}381}382}383#endif /* defined(J9VM_OPT_JITSERVER) */384385386void387J9::KnownObjectTable::dumpTo(TR::FILE *file, TR::Compilation *comp)388{389TR::KnownObjectTable::Index endIndex = self()->getEndIndex();390#if defined(J9VM_OPT_JITSERVER)391if (comp->isOutOfProcessCompilation())392{393auto stream = TR::CompilationInfo::getStream();394stream->write(JITServer::MessageType::KnownObjectTable_getKnownObjectTableDumpInfo, JITServer::Void());395396auto recv = stream->read<std::vector<TR_KnownObjectTableDumpInfo>>();397auto &knotDumpInfoList = std::get<0>(recv);398399uint32_t numOfEntries = knotDumpInfoList.size();400TR_ASSERT_FATAL((numOfEntries == endIndex), "The client table size %u is different from the server table size %u", numOfEntries, endIndex);401402trfprintf(file, "<knownObjectTable size=\"%u\"> // ", numOfEntries);403int32_t pointerLen = trfprintf(file, "%p", this);404trfprintf(file, "\n %-6s %-*s %-*s %-8s Class\n", "id", pointerLen, "JNI Ref", pointerLen, "Address", "Hash");405406for (uint32_t i = 0; i < numOfEntries; i++)407{408trfprintf(file, " obj%-3d", i);409if (!std::get<0>(knotDumpInfoList[i]).ref)410trfprintf(file, " %*s NULL\n", pointerLen, "");411else412{413trfprintf(file, " %p %p %8x %.*s\n",414std::get<0>(knotDumpInfoList[i]).ref,415std::get<0>(knotDumpInfoList[i]).objectPointer,416std::get<0>(knotDumpInfoList[i]).hashCode,417std::get<1>(knotDumpInfoList[i]).size(),418std::get<1>(knotDumpInfoList[i]).c_str());419}420}421trfprintf(file, "</knownObjectTable>\n");422423if (comp->getOption(TR_TraceKnownObjectGraph))424{425trfprintf(file, "<knownObjectGraph>\n");426// JITServer KOT TODO427trfprintf(file, "</knownObjectGraph>\n");428}429}430else431#endif /* defined(J9VM_OPT_JITSERVER) */432{433TR_J9VMBase *j9fe = (TR_J9VMBase*)self()->fe();434J9MemoryManagerFunctions * mmf = jitConfig->javaVM->memoryManagerFunctions;435TR::VMAccessCriticalSection j9KnownObjectTableDumpToCriticalSection(j9fe,436TR::VMAccessCriticalSection::tryToAcquireVMAccess,437comp);438439if (j9KnownObjectTableDumpToCriticalSection.hasVMAccess())440{441trfprintf(file, "<knownObjectTable size=\"%d\"> // ", endIndex);442int32_t pointerLen = trfprintf(file, "%p", this);443trfprintf(file, "\n %-6s %-*s %-*s %-8s Class\n", "id", pointerLen, "JNI Ref", pointerLen, "Address", "Hash");444for (Index i = 0; i < endIndex; i++)445{446trfprintf(file, " obj%-3d", i);447if (self()->isNull(i))448trfprintf(file, " %*s NULL\n", pointerLen, "");449else450{451uintptr_t *ref = self()->getPointerLocation(i);452int32_t len; char *className = TR::Compiler->cls.classNameChars(comp, j9fe->getObjectClass(*ref), len);453int32_t hashCode = mmf->j9gc_objaccess_getObjectHashCode(jitConfig->javaVM, (J9Object*)(*ref));454trfprintf(file, " %p %p %8x %.*s %s\n", ref, *ref, hashCode, len, className,455isArrayWithStableElements(i) ? "(stable array)" : "");456}457}458trfprintf(file, "</knownObjectTable>\n");459460if (comp->getOption(TR_TraceKnownObjectGraph))461{462trfprintf(file, "<knownObjectGraph>\n");463464Index i;465466{467TR::StackMemoryRegion stackMemoryRegion(*comp->trMemory());468469// Collect field info and determine which objects are reachable from other objects470//471TR_BitVector reachable(endIndex, comp->trMemory(), stackAlloc, notGrowable);472TR_VMFieldsInfo **fieldsInfoByIndex = (TR_VMFieldsInfo**)alloca(endIndex * sizeof(TR_VMFieldsInfo*));473for (i = 1; i < endIndex; i++)474{475uintptr_t object = self()->getPointer(i);476J9Class *clazz = (J9Class*)j9fe->getObjectClass(object);477if (clazz->romClass->modifiers & J9AccClassArray)478{479fieldsInfoByIndex[i] = NULL;480continue; // TODO: Print out what reference arrays contain?481}482fieldsInfoByIndex[i] = new (comp->trStackMemory()) TR_VMFieldsInfo(comp, clazz, 1, stackAlloc);483ListIterator<TR_VMField> fieldIter(fieldsInfoByIndex[i]->getFields());484for (TR_VMField *field = fieldIter.getFirst(); field; field = fieldIter.getNext())485{486// For the purpose of "reachability", we only look at final487// fields. The intent is to reduce nondeterminism in the object488// graph log.489//490if (field->isReference() && (field->modifiers & J9AccFinal))491{492uintptr_t target = j9fe->getReferenceField(object, field->name, field->signature);493Index targetIndex = self()->getExistingIndexAt(&target);494if (targetIndex != UNKNOWN)495reachable.set(targetIndex);496}497}498}499500// At the top level, walk objects not reachable from other objects501//502TR_BitVector visited(endIndex, comp->trMemory(), stackAlloc, notGrowable);503for (i = 1; i < endIndex; i++)504{505if (!reachable.isSet(i) && !visited.isSet(i))506self()->dumpObjectTo(file, i, "", "", comp, visited, fieldsInfoByIndex, 0);507}508509} // scope of the stack memory region510511trfprintf(file, "</knownObjectGraph>\n");512}513}514else515{516trfprintf(file, "<knownObjectTable size=\"%d\"/> // unable to acquire VM access to print table contents\n", endIndex);517}518}519}520521void522J9::KnownObjectTable::addStableArray(Index index, int32_t stableArrayRank)523{524uintptr_t object = self()->getPointer(index);525TR_J9VMBase *j9fe = (TR_J9VMBase*)self()->fe();526J9Class *clazz = (J9Class*)j9fe->getObjectClass(object);527TR_ASSERT_FATAL((clazz->romClass->modifiers & J9AccClassArray), "addStableArray can only be called for arrays\n");528529if (_stableArrayRanks[index] < stableArrayRank)530{531_stableArrayRanks[index] = stableArrayRank;532}533}534535bool536J9::KnownObjectTable::isArrayWithStableElements(Index index)537{538TR_ASSERT_FATAL(index != UNKNOWN && 0 <= index && index < self()->getEndIndex(), "isArrayWithStableElements(%d): index must be in range 0..%d", index, self()->getEndIndex());539540return (index < _stableArrayRanks.size() && _stableArrayRanks[index] > 0);541}542543544