diff --git a/runtime/oti/vm_api.h b/runtime/oti/vm_api.h index 0100c7f6f89..e4a8faabc17 100644 --- a/runtime/oti/vm_api.h +++ b/runtime/oti/vm_api.h @@ -847,6 +847,29 @@ internalExceptionDescribe(J9VMThread *vmThread); UDATA iterateStackTrace(J9VMThread * vmThread, j9object_t* exception, UDATA (*callback) (J9VMThread * vmThread, void * userData, UDATA bytecodeOffset, J9ROMClass * romClass, J9ROMMethod * romMethod, J9UTF8 * fileName, UDATA lineNumber, J9ClassLoader* classLoader, J9Class* ramClass), void * userData, UDATA pruneConstructors, UDATA skipHiddenFrames); +/** +* @brief +* @param vmThread +* @param exception +* @param vmThread +* @param userData +* @param bytecodeOffset +* @param romClass +* @param romMethod +* @param fileName +* @param lineNumber +* @param classLoader +* @param ramClass) +* @param userData +* @param pruneConstructors +* @param skipHiddenFrames +* @param sizeOfWalkstateCache +* @param exceptionIsJavaObject +* @return UDATA +*/ +UDATA +iterateStackTraceImpl(J9VMThread * vmThread, j9object_t* exception, UDATA (*callback) (J9VMThread * vmThread, void * userData, UDATA bytecodeOffset, J9ROMClass * romClass, J9ROMMethod * romMethod, J9UTF8 * fileName, UDATA lineNumber, J9ClassLoader* classLoader, J9Class* ramClass), void * userData, UDATA pruneConstructors, UDATA skipHiddenFrames, UDATA sizeOfWalkstateCache, BOOLEAN exceptionIsJavaObject); + /* ---------------- exceptionsupport.c ---------------- */ diff --git a/runtime/vm/BufferWriter.hpp b/runtime/vm/BufferWriter.hpp new file mode 100644 index 00000000000..ea7199603b8 --- /dev/null +++ b/runtime/vm/BufferWriter.hpp @@ -0,0 +1,344 @@ +/******************************************************************************* + * Copyright IBM Corp. and others 2024 + * + * This program and the accompanying materials are made available under + * the terms of the Eclipse Public License 2.0 which accompanies this + * distribution and is available at https://www.eclipse.org/legal/epl-2.0/ + * or the Apache License, Version 2.0 which accompanies this distribution and + * is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * This Source Code may also be made available under the following + * Secondary Licenses when the conditions for such availability set + * forth in the Eclipse Public License, v. 2.0 are satisfied: GNU + * General Public License, version 2 with the GNU Classpath + * Exception [1] and GNU General Public License, version 2 with the + * OpenJDK Assembly Exception [2]. + * + * [1] https://www.gnu.org/software/classpath/license.html + * [2] https://openjdk.org/legal/assembly-exception.html + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0 + *******************************************************************************/ + +#if !defined(BUFFERWRITER_HPP_) +#define BUFFERWRITER_HPP_ + +#include "j9cfg.h" +#include "j9.h" + +class VM_BufferWriter { + /* + * Data members + */ + private: + U_8* _buffer; + U_8* _cursor; + UDATA _size; + + U_8* _maxCursor; + +#if defined(J9VM_ENV_LITTLE_ENDIAN) + static const bool _isLE = true; +#else + static const bool _isLE = false; +#endif + + protected: + + public: + + /* + * Function members + */ + private: + static VMINLINE U_16 + byteSwap(U_16 val) + { + U_16 swapped = ((val & 0xFF00) >> 8); + swapped |= ((val & 0x00FF) << 8); + + return swapped; + } + + static VMINLINE U_32 + byteSwap(U_32 val) + { + U_32 swapped = ((val & 0xFF000000) >> 24); + swapped |= ((val & 0x00FF0000) >> 8); + swapped |= ((val & 0x0000FF00) << 8); + swapped |= ((val & 0x000000FF) << 24); + + return swapped; + } + + static VMINLINE U_64 + byteSwap(U_64 val) + { + U_64 swapped = ((val & 0xFF00000000000000) >> 56); + swapped |= ((val & 0x00FF000000000000) >> 40); + swapped |= ((val & 0x0000FF0000000000) >> 24); + swapped |= ((val & 0x000000FF00000000) >> 8); + swapped |= ((val & 0x00000000FF000000) << 8); + swapped |= ((val & 0x0000000000FF0000) << 24); + swapped |= ((val & 0x000000000000FF00) << 40); + swapped |= ((val & 0x00000000000000FF) << 56); + + return swapped; + } + + protected: + + public: + + VM_BufferWriter(U_8 *buffer, UDATA size) + : _buffer(buffer) + , _cursor(buffer) + , _size(size) + { + _maxCursor = NULL; + } + + U_64 + getFileOffset(U_8* bufferOffset, U_8* from) + { + return (U_64)((UDATA)bufferOffset - (UDATA)from); + } + + U_64 + getFileOffsetFromStart(U_8* bufferOffset) + { + return getFileOffset(bufferOffset, _buffer); + } + + U_8* + getBufferStart() + { + return _buffer; + } + + UDATA + getSize() + { + return getMaxCursor() - _buffer; + } + + void + writeU8(U_8 val) + { + *_cursor = val; + _cursor += sizeof(U_8); + } + + void + writeU16(U_16 val) + { + U_16 newVal = val; + if (_isLE) { + newVal = byteSwap(val); + } + *(U_16*)_cursor = newVal; + _cursor += sizeof(U_16); + } + + void + writeU32(U_32 val) + { + U_32 newVal = val; + if (_isLE) { + newVal = byteSwap(val); + } + *(U_32*)_cursor = newVal; + _cursor += sizeof(U_32); + } + + void + writeU64(U_64 val) + { + U_64 newVal = val; + if (_isLE) { + newVal = byteSwap(val); + } + *(U_64*)_cursor = newVal; + _cursor += sizeof(U_64); + } + + void + writeData(U_8 *data, UDATA size) + { + memcpy(_cursor, data, size); + _cursor += size; + } + + U_8* + getAndIncCursor(UDATA size) + { + U_8 *old = _cursor; + _cursor += size; + + return old; + } + + U_8* + getCursor() + { + return _cursor; + } + U_8* + getMaxCursor() + { + if ((UDATA)_cursor > (UDATA)_maxCursor) { + _maxCursor = _cursor; + } + return _maxCursor; + } + + void + setCursor(U_8* cursor) + { + getMaxCursor(); + _cursor = cursor; + } + + void + writeLEB128(U_64 val) + { + U_64 newVal = val; + if (!_isLE) { + newVal = byteSwap(val); + } + do { + U_8 byte = newVal & 0x7F; + newVal >>= 7; + + if (newVal > 0) { + byte |= 0x80; + } + writeU8(byte); + } while(newVal > 0); + } + + void + writeLEB128PaddedU64(U_8* cursor, U_64 val) + { + U_8* old = _cursor; + _cursor = cursor; + writeLEB128PaddedU64(val); + _cursor = old; + } + + void + writeLEB128PaddedU64(U_64 val) + { + U_64 newVal = val; + if (!_isLE) { + newVal = byteSwap(val); + } + writeU8((newVal & 0x7F) | 0x80); + writeU8(((newVal >> 7) & 0x7F) | 0x80); + writeU8(((newVal >> 14) & 0x7F) | 0x80); + writeU8(((newVal >> 21) & 0x7F) | 0x80); + writeU8(((newVal >> 28) & 0x7F) | 0x80); + writeU8(((newVal >> 35) & 0x7F) | 0x80); + writeU8(((newVal >> 42) & 0x7F) | 0x80); + writeU8(((newVal >> 49) & 0x7F)); + } + + void + writeLEB128PaddedU32(U_8* cursor, U_32 val) + { + U_8* old = _cursor; + _cursor = cursor; + writeLEB128PaddedU32(val); + _cursor = old; + } + + void + writeLEB128PaddedU32(U_32 val) + { + U_64 newVal = val; + if (!_isLE) { + newVal = byteSwap(val); + } + writeU8((newVal & 0x7F) | 0x80); + writeU8(((newVal >> 7) & 0x7F) | 0x80); + writeU8(((newVal >> 14) & 0x7F) | 0x80); + writeU8(((newVal >> 21) & 0x7F)); + } + + static U_32 + convertFromLEB128ToU32(U_8* start) + { + U_32 val = 0; + + val = *start & 0x7F; + + if (J9_ARE_ALL_BITS_SET(*start, 0x80)) { + start++; + val |= (*start & 0X7F) << 7; + } + + if (J9_ARE_ALL_BITS_SET(*start, 0x80)) { + start++; + val |= (*start & 0X7F) << 14; + } + + if (J9_ARE_ALL_BITS_SET(*start, 0x80)) { + start++; + val |= (*start & 0X7F) << 21; + } + if (!_isLE) { + val = byteSwap(val); + } + return val; + } + + static U_64 + convertFromLEB128ToU64(U_8* start) + { + U_64 val = 0; + + val = *start & 0x7F; + + if (J9_ARE_ALL_BITS_SET(*start, 0x80)) { + start++; + val |= (U_64)(*start & 0X7F) << 7; + } + + if (J9_ARE_ALL_BITS_SET(*start, 0x80)) { + start++; + val |= (U_64)(*start & 0X7F) << 14; + } + + if (J9_ARE_ALL_BITS_SET(*start, 0x80)) { + start++; + val |= (U_64)(*start & 0X7F) << 21; + } + + if (J9_ARE_ALL_BITS_SET(*start, 0x80)) { + start++; + val |= (U_64)(*start & 0X7F) << 28; + } + + if (J9_ARE_ALL_BITS_SET(*start, 0x80)) { + start++; + val |= (U_64)(*start & 0X7F) << 35; + } + + if (J9_ARE_ALL_BITS_SET(*start, 0x80)) { + start++; + val |= (U_64)(*start & 0X7F) << 42; + } + + if (J9_ARE_ALL_BITS_SET(*start, 0x80)) { + start++; + val |= (U_64)(*start & 0X7F) << 59; + } + if (!_isLE) { + val = byteSwap(val); + } + return val; + } + +}; + +#endif /* BUFFERWRITER_HPP_ */ diff --git a/runtime/vm/CMakeLists.txt b/runtime/vm/CMakeLists.txt index f4406f8581a..d1c78a9cb2f 100644 --- a/runtime/vm/CMakeLists.txt +++ b/runtime/vm/CMakeLists.txt @@ -198,6 +198,8 @@ endif() if(J9VM_OPT_JFR) list(APPEND main_sources jfr.cpp + JFRConstantPoolTypes.cpp + JFRChunkWriter.cpp ) endif() diff --git a/runtime/vm/JFRChunkWriter.cpp b/runtime/vm/JFRChunkWriter.cpp new file mode 100644 index 00000000000..10c1d5c9db6 --- /dev/null +++ b/runtime/vm/JFRChunkWriter.cpp @@ -0,0 +1,628 @@ +/******************************************************************************* + * Copyright IBM Corp. and others 2024 + * + * This program and the accompanying materials are made available under + * the terms of the Eclipse Public License 2.0 which accompanies this + * distribution and is available at https://www.eclipse.org/legal/epl-2.0/ + * or the Apache License, Version 2.0 which accompanies this distribution and + * is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * This Source Code may also be made available under the following + * Secondary Licenses when the conditions for such availability set + * forth in the Eclipse Public License, v. 2.0 are satisfied: GNU + * General Public License, version 2 with the GNU Classpath + * Exception [1] and GNU General Public License, version 2 with the + * OpenJDK Assembly Exception [2]. + * + * [1] https://www.gnu.org/software/classpath/license.html + * [2] https://openjdk.org/legal/assembly-exception.html + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0 + *******************************************************************************/ +#include "vm_internal.h" + +#if defined(J9VM_OPT_JFR) + +#include "JFRChunkWriter.hpp" + +void +VM_JFRChunkWriter::writeJFRHeader() +{ + _bufferWriter->setCursor(_jfrHeaderCursor); + + /* Magic number "FLR\0" in ASCII */ + _bufferWriter->writeU32(0x464c5200);//0 + + /* Major and Minor version numbers */ + _bufferWriter->writeU16(2);//4 + _bufferWriter->writeU16(1); + + /* chunk size */ + _bufferWriter->writeU64(_bufferWriter->getSize());//8 + + /* checkpoint offset */ + _bufferWriter->writeU64(_bufferWriter->getFileOffsetFromStart(_checkPointEventOffset));//16 + + /* metadata offset */ + _bufferWriter->writeU64(_bufferWriter->getFileOffsetFromStart(_metadataOffset));//24 + + /* start time */ + _bufferWriter->writeU64(VM_JFRUtils::getCurrentTimeNanos(privatePortLibrary, _buildResult));//32 + + /* duration */ + _bufferWriter->writeU64(0);//40 + + /* start ticks */ + _bufferWriter->writeU64(0);//48 + + /* ticks per second */ + _bufferWriter->writeU64(1);//56 //TODO ??? + + /* file state or generation */ + _bufferWriter->writeU8(0);//64 //TODO ??? + + /* pad */ + _bufferWriter->writeU16(0);//65 + + /* flags */ + if (_finalWrite) { + _bufferWriter->writeU8(0x2);//67 //final chunk bit? + } else { + _bufferWriter->writeU8(2); + } +} + +U_8* +VM_JFRChunkWriter::writeJFRMetadata() +{ + /* reserve metadata size field */ + U_8* dataStart = _bufferWriter->getAndIncCursor(sizeof(U_32)); + + if (_debug) { + j9tty_printf(PORTLIB, "Metadata frame start offset = 0x%lX\n", _bufferWriter->getFileOffsetFromStart(dataStart)); + } + + /* write metadata header */ + _bufferWriter->writeU8(EventMetadata); + + /* start time */ + _bufferWriter->writeLEB128(VM_JFRUtils::getCurrentTimeNanos(privatePortLibrary, _buildResult)); + + /* duration */ + _bufferWriter->writeLEB128((U_64)0); + + /* metadata ID */ + _bufferWriter->writeLEB128((U_64)METADATA_ID); + + + U_8* blobStart = _bufferWriter->getCursor(); + if (_debug) { + j9tty_printf(PORTLIB, "Metadata blob start offset = 0x%lX, size = %d\n", _bufferWriter->getFileOffsetFromStart(_bufferWriter->getCursor()), (int) _vm->jfrState.metaDataBlobFileSize); + } + + /* metadata blob file */ + _bufferWriter->writeData(_vm->jfrState.metaDataBlobFile, _vm->jfrState.metaDataBlobFileSize); + + if (_debug) { + j9tty_printf(PORTLIB, "Metadata blob size from LEB128 = %u\n", VM_BufferWriter::convertFromLEB128ToU32(blobStart)); + } + + /* add metadata size */ + _bufferWriter->writeLEB128PaddedU32(dataStart, _bufferWriter->getCursor() - dataStart); + + if (_debug) { + j9tty_printf(PORTLIB, "Metadata size = %d, fromLEB128 =%u\n", (int)(_bufferWriter->getCursor() - dataStart), VM_BufferWriter::convertFromLEB128ToU32(dataStart)); + } + + return dataStart; +} + +U_8* +VM_JFRChunkWriter::writeCheckpointEventHeader(CheckpointTypeMask typeMask, U_32 cpCount) +{ + /* write delta offset in previous checkpoint event */ + if (NULL != _previousCheckpointDelta) { + _bufferWriter->writeLEB128PaddedU64(_previousCheckpointDelta,(U_64) _bufferWriter->getFileOffset(_bufferWriter->getCursor(), _lastDataStart)); + } + + /* reserve size field */ + U_8 *dataStart = _bufferWriter->getAndIncCursor(sizeof(U_32)); + _lastDataStart = dataStart; + + if (_debug) { + j9tty_printf(PORTLIB, "Checkpoint event frame start offset = 0x%lX\n", _bufferWriter->getFileOffsetFromStart(dataStart)); + } + + /* write checkpoint header */ + _bufferWriter->writeU8(EventCheckpoint); + + /* start time */ + _bufferWriter->writeLEB128(VM_JFRUtils::getCurrentTimeNanos(privatePortLibrary, _buildResult)); + + /* duration */ + _bufferWriter->writeLEB128((U_64)0); + + /* reserve delta offset to next checkpoint event */ + _previousCheckpointDelta = _bufferWriter->getCursor(); + _bufferWriter->writeLEB128PaddedU64(0); + + if (_debug) { + j9tty_printf(PORTLIB, "next pointer=%d val=%d\n",(int) _bufferWriter->getFileOffsetFromStart(_previousCheckpointDelta), (int) VM_BufferWriter::convertFromLEB128ToU64(_previousCheckpointDelta)); + } + + /* type mask */ + _bufferWriter->writeU8(typeMask); + + /* constant pool count */ + _bufferWriter->writeLEB128(cpCount); + + return dataStart; +} + +void +VM_JFRChunkWriter::writeUTF8String(J9UTF8* string) +{ + if (NULL == string) { + _bufferWriter->writeLEB128(NullString); + } else { + writeUTF8String(J9UTF8_DATA(string), J9UTF8_LENGTH(string)); + } +} + +void +VM_JFRChunkWriter::writeUTF8String(U_8* data, UDATA len) +{ + _bufferWriter->writeLEB128(UTF8); + _bufferWriter->writeLEB128(len); + _bufferWriter->writeData(data, len); +} + +U_8* +VM_JFRChunkWriter::writeThreadStateCheckpointEvent() +{ + U_8 *dataStart = writeCheckpointEventHeader(Generic, 1); + + /* class ID */ + _bufferWriter->writeLEB128(ThreadStateID); + + /* number of states */ + _bufferWriter->writeLEB128(THREADSTATE_COUNT); + + for (int i = 0; i < THREADSTATE_COUNT; i++) { + /* constant index */ + _bufferWriter->writeLEB128(i); + + /* string encoding */ + _bufferWriter->writeLEB128(UTF8); + + /* string length */ + U_32 len = strlen(threadStateNames[i]); + _bufferWriter->writeLEB128(len); + + /* write string */ //need to think about UTF8 enconding + _bufferWriter->writeData((U_8*)threadStateNames[i], len); + } + + /* write size */ + _bufferWriter->writeLEB128PaddedU32(dataStart, _bufferWriter->getCursor() - dataStart); + + return dataStart; +} + +U_8* +VM_JFRChunkWriter::writePackageCheckpointEvent() +{ + U_8 *dataStart = NULL; + + if (_constantPoolTypes->getPackageCount() > 0) { + dataStart = writeCheckpointEventHeader(Generic, 1); + + /* class ID */ + _bufferWriter->writeLEB128(PackageID); + + /* number of states */ + _bufferWriter->writeLEB128(_constantPoolTypes->getPackageCount()); + + PackageEntry *entry = _constantPoolTypes->getPackageEntry(); + while (NULL != entry) { + /* write index */ + _bufferWriter->writeLEB128(entry->index); + + /* name index */ + _bufferWriter->writeLEB128(_constantPoolTypes->getStringUTF8Count() + entry->index); + + /* module index */ + _bufferWriter->writeLEB128(entry->moduleIndex); + + /* exported index */ + _bufferWriter->writeU8(entry->exported); + + entry = entry->next; + } + + /* write size */ + _bufferWriter->writeLEB128PaddedU32(dataStart, _bufferWriter->getCursor() - dataStart); + } + + return dataStart; +} + +U_8* +VM_JFRChunkWriter::writeMethodCheckpointEvent() +{ + U_8 *dataStart = NULL; + + if (_constantPoolTypes->getMethodCount() > 0) { + dataStart = writeCheckpointEventHeader(Generic, 1); + + /* class ID */ + _bufferWriter->writeLEB128(MethodID); + + /* number of states */ + _bufferWriter->writeLEB128(_constantPoolTypes->getMethodCount()); + + MethodEntry *entry = _constantPoolTypes->getMethodEntry(); + while (NULL != entry) { + /* write index */ + _bufferWriter->writeLEB128(entry->index); + + /* type index */ + _bufferWriter->writeLEB128(entry->classIndex); + + /* name index */ + _bufferWriter->writeLEB128(entry->nameStringUTF8Index); + + /* descriptor index */ + _bufferWriter->writeLEB128(entry->descriptorStringUTF8Index); + + /* modifiers */ + _bufferWriter->writeLEB128(entry->modifiers); + + /* hidden */ + _bufferWriter->writeU8(entry->hidden); + + entry = entry->next; + } + + /* write size */ + _bufferWriter->writeLEB128PaddedU32(dataStart, _bufferWriter->getCursor() - dataStart); + } + + return dataStart; +} + +U_8* +VM_JFRChunkWriter::writeClassloaderCheckpointEvent() +{ + U_8 *dataStart = NULL; + + if (_constantPoolTypes->getClassloaderCount() > 0) { + dataStart = writeCheckpointEventHeader(Generic, 1); + + /* class ID */ + _bufferWriter->writeLEB128(ClassLoaderID); + + /* number of states */ + _bufferWriter->writeLEB128(_constantPoolTypes->getClassloaderCount()); + + ClassloaderEntry *entry = _constantPoolTypes->getClassloaderEntry(); + while (NULL != entry) { + /* write index */ + _bufferWriter->writeLEB128(entry->index); + + /* class index */ + _bufferWriter->writeLEB128(entry->classIndex); + + /* class name index */ + _bufferWriter->writeLEB128(entry->nameIndex); + + entry = entry->next; + } + + /* write size */ + _bufferWriter->writeLEB128PaddedU32(dataStart, _bufferWriter->getCursor() - dataStart); + } + + return dataStart; +} + +U_8* +VM_JFRChunkWriter:: writeClassCheckpointEvent() +{ + U_8 *dataStart = NULL; + + if (_constantPoolTypes->getClassCount() > 0) { + dataStart = writeCheckpointEventHeader(Generic, 1); + + /* class ID */ + _bufferWriter->writeLEB128(ClassID); + + /* number of states */ + _bufferWriter->writeLEB128(_constantPoolTypes->getClassCount()); + + ClassEntry *entry = _constantPoolTypes->getClassEntry(); + while (NULL != entry) { + /* write index */ + _bufferWriter->writeLEB128(entry->index); + + /* classloader index */ + _bufferWriter->writeLEB128(entry->classLoaderIndex); + + /* class name index */ + _bufferWriter->writeLEB128(entry->nameStringUTF8Index); + + /* package index */ + _bufferWriter->writeLEB128(entry->packageIndex); + + /* class modifiers */ + _bufferWriter->writeLEB128(entry->modifiers); + + /* class hidden */ + _bufferWriter->writeU8(entry->hidden); + + entry = entry->next; + } + + /* write size */ + _bufferWriter->writeLEB128PaddedU32(dataStart, _bufferWriter->getCursor() - dataStart); + } + + return dataStart; +} + +U_8* +VM_JFRChunkWriter::writeModuleCheckpointEvent() +{ + U_8 *dataStart = NULL; + + if (_constantPoolTypes->getModuleCount() > 0) { + dataStart = writeCheckpointEventHeader(Generic, 1); + + /* class ID */ + _bufferWriter->writeLEB128(ModuleID); + + /* number of states */ + _bufferWriter->writeLEB128(_constantPoolTypes->getModuleCount()); + + ModuleEntry *entry = _constantPoolTypes->getModuleEntry(); + while (NULL != entry) { + /* write index */ + _bufferWriter->writeLEB128(entry->index); + + /* module name index */ + _bufferWriter->writeLEB128(entry->nameStringIndex); + + /* module version index */ + _bufferWriter->writeLEB128(entry->versionStringIndex); + + /* module location index */ + _bufferWriter->writeLEB128(entry->locationStringUTF8Index); + + /* module location index */ + _bufferWriter->writeLEB128(entry->classLoaderIndex); + + entry = entry->next; + } + + /* write size */ + _bufferWriter->writeLEB128PaddedU32(dataStart, _bufferWriter->getCursor() - dataStart); + } + + return dataStart; +} + +U_8* +VM_JFRChunkWriter::writeThreadCheckpointEvent() +{ + U_8 *dataStart = NULL; + + if (_constantPoolTypes->getThreadCount() > 0) { + dataStart = writeCheckpointEventHeader(Generic, 1); + + /* class ID */ + _bufferWriter->writeLEB128(ThreadID); + + /* number of states */ + _bufferWriter->writeLEB128(_constantPoolTypes->getThreadCount()); + + ThreadEntry *entry = _constantPoolTypes->getThreadEntry(); + while (NULL != entry) { + /* write index */ + _bufferWriter->writeLEB128(entry->index); + + /* write OS thread name */ + writeUTF8String(entry->osThreadName); + + /* write OS Thread ID */ + _bufferWriter->writeLEB128(entry->osTID); + + /* write Java thread name */ + writeUTF8String(entry->javaThreadName); + + /* write Java Thread ID */ + _bufferWriter->writeLEB128(entry->javaTID); + + /* write Thread group index */ + _bufferWriter->writeLEB128(entry->threadGroupIndex); + + entry = entry->next; + } + + /* write size */ + _bufferWriter->writeLEB128PaddedU32(dataStart, _bufferWriter->getCursor() - dataStart); + } + + return dataStart; +} + +U_8* +VM_JFRChunkWriter::writeThreadGroupCheckpointEvent() +{ + U_8 *dataStart = NULL; + + if (_constantPoolTypes->getThreadGroupCount() > 0) { + dataStart = writeCheckpointEventHeader(Generic, 1); + + /* class ID */ + _bufferWriter->writeLEB128(ThreadGroupID); + + /* number of states */ + _bufferWriter->writeLEB128(_constantPoolTypes->getThreadGroupCount()); + + ThreadGroupEntry *entry = _constantPoolTypes->getThreadGroupEntry(); + while (NULL != entry) { + /* write index */ + _bufferWriter->writeLEB128(entry->index); + + /* write parent index */ + _bufferWriter->writeLEB128(entry->parentIndex); + + /* thread group name */ + writeUTF8String(entry->name); + + entry = entry->next; + } + + /* write size */ + _bufferWriter->writeLEB128PaddedU32(dataStart, _bufferWriter->getCursor() - dataStart); + } + + return dataStart; +} + +U_8* +VM_JFRChunkWriter::writeFrameTypeCheckpointEvent() +{ + U_8 *dataStart = writeCheckpointEventHeader(Generic, 1); + + /* class ID */ + _bufferWriter->writeLEB128(FrameTypeID); + + /* number of states */ + _bufferWriter->writeLEB128(FrameTypeCount); + + const char* frameTypeNames[] = { + "INTERPRETED", + "JIT", + "JIT_INLINED", + "NATIVE" + }; + + for (int i = 0; i < FrameTypeCount; i++) { + /* constant index */ + _bufferWriter->writeLEB128(i); + + /* string encoding */ + _bufferWriter->writeLEB128(UTF8); + + /* string length */ + U_32 len = strlen(frameTypeNames[i]); + _bufferWriter->writeLEB128(len); + + /* write string */ + /* TODO need to think about UTF8 enconding */ + _bufferWriter->writeData((U_8*)frameTypeNames[i], len); + } + + /* write size */ + _bufferWriter->writeLEB128PaddedU32(dataStart, _bufferWriter->getCursor() - dataStart); + + return dataStart; +} + +U_8* +VM_JFRChunkWriter::writeSymbolTableCheckpointEvent() +{ + U_8 *dataStart = NULL; + + if (_constantPoolTypes->getPackageCount() > 0) { + dataStart = writeCheckpointEventHeader(Generic, 1); + + /* class ID */ + _bufferWriter->writeLEB128(SymbolID); + + /* number of states */ + UDATA stringCount = _constantPoolTypes->getStringUTF8Count(); + UDATA packageCount = _constantPoolTypes->getPackageCount(); + _bufferWriter->writeLEB128(stringCount + packageCount); + + for (UDATA i = 0; i < stringCount; i++) { + StringUTF8Entry *stringEntry = (StringUTF8Entry*)_constantPoolTypes->getSymbolTableEntry(i); + + /* write index */ + _bufferWriter->writeLEB128(i); + + /* write string */ + writeUTF8String(stringEntry->string); + } + + for (UDATA i = stringCount; i < (stringCount + packageCount); i++) { + PackageEntry *packageEntry = (PackageEntry*)_constantPoolTypes->getSymbolTableEntry(i); + + /* write index */ + _bufferWriter->writeLEB128(i); + + /* write string */ + writeUTF8String(packageEntry->packageName, packageEntry->packageNameLength); + } + + /* write size */ + _bufferWriter->writeLEB128PaddedU32(dataStart, _bufferWriter->getCursor() - dataStart); + } + + return dataStart; +} + +U_8* +VM_JFRChunkWriter::writeStacktraceCheckpointEvent() +{ + U_8 *dataStart = NULL; + + if (_constantPoolTypes->getStackTraceCount() > 0) { + dataStart = writeCheckpointEventHeader(Generic, 1); + + /* class ID */ + _bufferWriter->writeLEB128(StackTraceID); + + /* number of states */ + _bufferWriter->writeLEB128(_constantPoolTypes->getStackTraceCount()); + + StackTraceEntry *entry = _constantPoolTypes->getStackTraceEntry(); + while (NULL != entry) { + /* write index */ + _bufferWriter->writeLEB128(entry->index); + + /* is truncated */ + _bufferWriter->writeU8(entry->truncated); + + /* number of stack frames */ + UDATA framesCount = entry->numOfFrames; + _bufferWriter->writeU8(framesCount); + + for (UDATA i = 0; i < framesCount; i++) { + StackFrame *frame = entry->frames + i; + + /* method index */ + _bufferWriter->writeLEB128(frame->methodIndex); + + /* line number */ + _bufferWriter->writeLEB128(frame->lineNumber); + + /* bytecode index */ + _bufferWriter->writeLEB128(frame->bytecodeIndex); + + /* frame type index */ + _bufferWriter->writeLEB128(frame->frameType); + } + + entry = entry->next; + } + + /* write size */ + _bufferWriter->writeLEB128PaddedU32(dataStart, _bufferWriter->getCursor() - dataStart); + } + + return dataStart; +} + +#endif /* defined(J9VM_OPT_JFR) */ diff --git a/runtime/vm/JFRChunkWriter.hpp b/runtime/vm/JFRChunkWriter.hpp new file mode 100644 index 00000000000..889dd0c3621 --- /dev/null +++ b/runtime/vm/JFRChunkWriter.hpp @@ -0,0 +1,483 @@ +/******************************************************************************* + * Copyright IBM Corp. and others 2024 + * + * This program and the accompanying materials are made available under + * the terms of the Eclipse Public License 2.0 which accompanies this + * distribution and is available at https://www.eclipse.org/legal/epl-2.0/ + * or the Apache License, Version 2.0 which accompanies this distribution and + * is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * This Source Code may also be made available under the following + * Secondary Licenses when the conditions for such availability set + * forth in the Eclipse Public License, v. 2.0 are satisfied: GNU + * General Public License, version 2 with the GNU Classpath + * Exception [1] and GNU General Public License, version 2 with the + * OpenJDK Assembly Exception [2]. + * + * [1] https://www.gnu.org/software/classpath/license.html + * [2] https://openjdk.org/legal/assembly-exception.html + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0 + *******************************************************************************/ +#if !defined(JFRCHUNKWRITER_HPP_) +#define JFRCHUNKWRITER_HPP_ + +#include "j9cfg.h" +#include "j9.h" +#include "omrlinkedlist.h" +#include "vm_api.h" + +#if defined(J9VM_OPT_JFR) + +#include "BufferWriter.hpp" +#include "JFRConstantPoolTypes.hpp" +#include "JFRUtils.hpp" +#include "ObjectAccessBarrierAPI.hpp" +#include "VMHelpers.hpp" + +static constexpr const char* threadStateNames[] = { + "STATE_NEW", + "STATE_TERMINATED", + "STATE_RUNNABLE", + "STATE_SLEEPING", + "STATE_IN_OBJECT_WAIT", + "STATE_IN_OBJECT_WAIT_TIMED", + "STATE_PARKED", + "STATE_PARKED_TIMED", + "STATE_BLOCKED_ON_MONITOR_ENTER" +}; + +enum StringEnconding { + NullString = 0, + EmptyString, + StringConstant, + UTF8, + UTF16, + Latin1, +}; + +enum MetadataTypeID { + ThreadStartID = 2, + ThreadEndID = 3, + ThreadSleepID = 4, + ExecutionSampleID = 108, + ThreadID = 163, + ThreadGroupID = 164, + ClassID = 165, + ClassLoaderID = 166, + MethodID = 167, + SymbolID = 168, + ThreadStateID = 169, + ModuleID = 185, + PackageID = 186, + StackTraceID = 187, + FrameTypeID = 188, + StackFrameID = 196, +}; + +enum ReservedEvent { + EventMetadata = 0, + EventCheckpoint, +}; + +enum CheckpointTypeMask { + Generic = 0, + Flush = 1, + ChunkHeader = 2, + Statics = 4, + Thread = 8, +}; + +class VM_JFRChunkWriter { + /* + * Data members + */ +private: + J9VMThread *_currentThread; + J9JavaVM *_vm; + BuildResult _buildResult; + bool _debug; + J9PortLibrary *privatePortLibrary; //PORT_ACCESS_FROM... + bool _finalWrite; + + /* Constantpool types */ + VM_JFRConstantPoolTypes *_constantPoolTypes; + + /* Processing buffers */ + StackFrame *_currentStackFrameBuffer; + StackTraceEntry *_previousStackTraceEntry; + StackTraceEntry *_firstStackTraceEntry; + U_32 _currentFrameCount; + void **_globalStringTable; + U_8 *_jfrHeaderCursor; + VM_BufferWriter *_bufferWriter; + U_8* _metadataOffset; + U_8* _checkPointEventOffset; + U_8* _previousCheckpointDelta; + U_8* _lastDataStart; + + static constexpr int STRING_BUFFER_LENGTH = 128; + /* JFR CHUNK Header size */ + static constexpr int JFR_CHUNK_HEADER_SIZE = 68; + + /* conservative sizing for JFR chunk */ + static constexpr int STRING_HEADER_LENGTH = sizeof(U_64); + static constexpr int CHECKPOINT_EVENT_HEADER_AND_FOOTER = 68; + static constexpr int THREADSTATE_ENTRY_LENGTH = CHECKPOINT_EVENT_HEADER_AND_FOOTER + sizeof(threadStateNames) + (THREADSTATE_COUNT * STRING_HEADER_LENGTH); + static constexpr int CLASS_ENTRY_ENTRY_SIZE = (5 * sizeof(U_64)) + sizeof(U_8); + static constexpr int CLASSLOADER_ENTRY_SIZE = 3 * sizeof(U_64); + static constexpr int PACKAGE_ENTRY_SIZE = (3 * sizeof(U_64)) + sizeof(U_8); + static constexpr int METHOD_ENTRY_SIZE = (5 * sizeof(U_64)) + sizeof(U_8); + static constexpr int THREAD_ENTRY_SIZE = 2 * sizeof(U_64); + static constexpr int THREADGROUP_ENTRY_SIZE = 2 * sizeof(U_64); + static constexpr int STACKTRACE_ENTRY_SIZE = (1 * sizeof(U_64)) + sizeof(U_8); + static constexpr int MODULE_ENTRY_SIZE = 5 * sizeof(U_64); + static constexpr int STACKFRAME_ENTRY_SIZE = 4 * sizeof(U_64); + static constexpr int METADATA_HEADER_SIZE = 64; + static constexpr int EXECUTION_SAMPLE_EVENT_SIZE = (5 * sizeof(U_64)) + sizeof(U_32); + static constexpr int THREAD_START_EVENT_SIZE = (6 * sizeof(U_64)) + sizeof(U_32); + static constexpr int THREAD_END_EVENT_SIZE = (4 * sizeof(U_64)) + sizeof(U_32); + static constexpr int THREAD_SLEEP_EVENT_SIZE = (7 * sizeof(U_64)) + sizeof(U_32); + + static constexpr int METADATA_ID = 1; + +protected: + +public: + + + /* + * Function members + */ +private: + + bool isResultNotOKay() { + if (!isOkay()) { + if (_debug) { + j9tty_printf(PORTLIB, "Chunk writer operation failed error=%d\n", (int) _buildResult); + } + return true; + } + return false; + } + + + +protected: + +public: + VM_JFRChunkWriter(J9VMThread *currentThread, bool finalWrite) + : _currentThread(currentThread) + , _vm(currentThread->javaVM) + , _buildResult(OK) + , _debug(false) + , privatePortLibrary(_vm->portLibrary) + , _finalWrite(finalWrite) + , _previousCheckpointDelta(NULL) + , _lastDataStart(NULL) + { + _constantPoolTypes = new VM_JFRConstantPoolTypes(currentThread); + + return; + } + void + loadEvents() + { + _constantPoolTypes->loadEvents(); + _buildResult = _constantPoolTypes->getBuildResult(); + } + + bool isOkay() + { + return _buildResult == OK; + } + + BuildResult buildResult() + { + return _buildResult; + } + + void writeJFRChunk() + { + U_8 *buffer = NULL; + UDATA requiredBufferSize = 0; + + if (NULL == _vm->jfrState.metaDataBlobFile) { + _buildResult = MetaDataFileNotLoaded; + goto done; + } + + if (_debug) { + _constantPoolTypes->printTables(); + } + + requiredBufferSize = calculateRequiredBufferSize(); + if (isResultNotOKay()) goto done; + + buffer = (U_8*) j9mem_allocate_memory(requiredBufferSize, J9MEM_CATEGORY_CLASSES); + if (NULL == buffer) { + _buildResult = OutOfMemory; + goto done; + } + + _bufferWriter = new VM_BufferWriter(buffer, requiredBufferSize); + + /* write the header last */ + _jfrHeaderCursor = _bufferWriter->getAndIncCursor(JFR_CHUNK_HEADER_SIZE); + + /* Metadata is always written first, checkpoint events can be in any order */ + _metadataOffset = writeJFRMetadata(); + + _checkPointEventOffset = writeThreadStateCheckpointEvent(); + + writeFrameTypeCheckpointEvent(); + + writeThreadCheckpointEvent(); + + writeThreadGroupCheckpointEvent(); + + writeSymbolTableCheckpointEvent(); + + writeModuleCheckpointEvent(); + + writeClassloaderCheckpointEvent(); + + writeClassCheckpointEvent(); + + writePackageCheckpointEvent(); + + writeMethodCheckpointEvent(); + + writeStacktraceCheckpointEvent(); + + pool_do(_constantPoolTypes->getExecutionSampleTable(), &writeExecutionSampleEvent, _bufferWriter); + + pool_do(_constantPoolTypes->getThreadStartTable(), &writeThreadStartEvent, _bufferWriter); + + pool_do(_constantPoolTypes->getThreadEndTable(), &writeThreadEndEvent, _bufferWriter); + + pool_do(_constantPoolTypes->getThreadSleepTable(), &writeThreadSleepEvent, _bufferWriter); + + writeJFRHeader(); + + writeJFRChunkToFile(); + + j9mem_free_memory(buffer); + +done: + return; + } + + static void + writeExecutionSampleEvent(void *anElement, void *userData) + { + ExecutionSampleEntry *entry = (ExecutionSampleEntry*)anElement; + VM_BufferWriter *_bufferWriter = (VM_BufferWriter*) userData; + + /* reserve size field */ + U_8 *dataStart = _bufferWriter->getAndIncCursor(sizeof(U_32)); + + /* write event type */ + _bufferWriter->writeLEB128(ExecutionSampleID); + + /* write start time */ + _bufferWriter->writeLEB128(entry->time); + + /* write sampling thread index */ + _bufferWriter->writeLEB128(entry->threadIndex); + + /* stacktrace index */ + _bufferWriter->writeLEB128(entry->stackTraceIndex); + + /* thread state */ + _bufferWriter->writeLEB128(RUNNABLE); + + /* write size */ + _bufferWriter->writeLEB128PaddedU32(dataStart, _bufferWriter->getCursor() - dataStart); + } + + static void + writeThreadStartEvent(void *anElement, void *userData) + { + ThreadStartEntry *entry = (ThreadStartEntry*)anElement; + VM_BufferWriter *_bufferWriter = (VM_BufferWriter*) userData; + + /* reserve size field */ + U_8 *dataStart = _bufferWriter->getAndIncCursor(sizeof(U_32)); + + /* write event type */ + _bufferWriter->writeLEB128(ThreadStartID); + + /* write start time */ + _bufferWriter->writeLEB128(entry->time); + + /* write event thread index */ + _bufferWriter->writeLEB128(entry->eventThreadIndex); + + /* stacktrace index */ + _bufferWriter->writeLEB128(entry->stackTraceIndex); + + /* write thread index */ + _bufferWriter->writeLEB128(entry->threadIndex); + + /* write parent thread index */ + _bufferWriter->writeLEB128(entry->parentThreadIndex); + + /* write size */ + _bufferWriter->writeLEB128PaddedU32(dataStart, _bufferWriter->getCursor() - dataStart); + } + + static void + writeThreadEndEvent(void *anElement, void *userData) + { + ThreadEndEntry *entry = (ThreadEndEntry*)anElement; + VM_BufferWriter *_bufferWriter = (VM_BufferWriter*) userData; + + /* reserve size field */ + U_8 *dataStart = _bufferWriter->getAndIncCursor(sizeof(U_32)); + + /* write event type */ + _bufferWriter->writeLEB128(ThreadEndID); + + /* write start time */ + _bufferWriter->writeLEB128(entry->time); + + /* write event thread index */ + _bufferWriter->writeLEB128(entry->eventThreadIndex); + + /* write thread index */ + _bufferWriter->writeLEB128(entry->threadIndex); + + /* write size */ + _bufferWriter->writeLEB128PaddedU32(dataStart, _bufferWriter->getCursor() - dataStart); + } + + static void + writeThreadSleepEvent(void *anElement, void *userData) + { + ThreadSleepEntry *entry = (ThreadSleepEntry*)anElement; + VM_BufferWriter *_bufferWriter = (VM_BufferWriter*) userData; + + /* reserve size field */ + U_8 *dataStart = _bufferWriter->getAndIncCursor(sizeof(U_32)); + + /* write event type */ + _bufferWriter->writeLEB128(ThreadSleepID); + + /* write start time */ + _bufferWriter->writeLEB128(entry->time); + + /* write duration time */ + _bufferWriter->writeLEB128(entry->duration); + + /* write event thread index */ + _bufferWriter->writeLEB128(entry->eventThreadIndex); + + /* stacktrace index */ + _bufferWriter->writeLEB128(entry->stackTraceIndex); + + /* write thread index */ + _bufferWriter->writeLEB128(entry->threadIndex); + + /* write time */ + _bufferWriter->writeLEB128(entry->duration); + + /* write size */ + _bufferWriter->writeLEB128PaddedU32(dataStart, _bufferWriter->getCursor() - dataStart); + } + + void + writeJFRChunkToFile() + { + UDATA len = _bufferWriter->getSize(); + + UDATA written = j9file_write(_vm->jfrState.blobFileDescriptor, _bufferWriter->getBufferStart(), len); + + if (len != written) { + _buildResult = FileIOError; + } + + return; + } + + void writeJFRHeader(); + + U_8* writeJFRMetadata(); + + U_8* writeCheckpointEventHeader(CheckpointTypeMask typeMask, U_32 cpCount); + + void writeUTF8String(J9UTF8* string); + + void writeUTF8String(U_8* data, UDATA len); + + U_8* writeThreadStateCheckpointEvent(); + + U_8* writePackageCheckpointEvent(); + + U_8* writeMethodCheckpointEvent(); + + U_8* writeClassloaderCheckpointEvent(); + + U_8* writeClassCheckpointEvent(); + + U_8* writeModuleCheckpointEvent(); + + U_8* writeThreadCheckpointEvent(); + + U_8* writeThreadGroupCheckpointEvent(); + + U_8* writeFrameTypeCheckpointEvent(); + + U_8* writeSymbolTableCheckpointEvent(); + + U_8* writeStacktraceCheckpointEvent(); + + + UDATA + calculateRequiredBufferSize() + { + UDATA requireBufferSize = _constantPoolTypes->getRequiredBufferSize(); + requireBufferSize += JFR_CHUNK_HEADER_SIZE; + + requireBufferSize += METADATA_HEADER_SIZE; + + requireBufferSize += _vm->jfrState.metaDataBlobFileSize; + + requireBufferSize += THREADSTATE_ENTRY_LENGTH; + + requireBufferSize += CHECKPOINT_EVENT_HEADER_AND_FOOTER + (_constantPoolTypes->getClassCount() * CLASS_ENTRY_ENTRY_SIZE); + + requireBufferSize += CHECKPOINT_EVENT_HEADER_AND_FOOTER + (_constantPoolTypes->getClassloaderCount() * CLASSLOADER_ENTRY_SIZE); + + requireBufferSize += CHECKPOINT_EVENT_HEADER_AND_FOOTER + (_constantPoolTypes->getPackageCount() * PACKAGE_ENTRY_SIZE); + + requireBufferSize += CHECKPOINT_EVENT_HEADER_AND_FOOTER + (_constantPoolTypes->getMethodCount() * METHOD_ENTRY_SIZE); + + requireBufferSize += CHECKPOINT_EVENT_HEADER_AND_FOOTER + (_constantPoolTypes->getThreadCount() * THREAD_ENTRY_SIZE); + + requireBufferSize += CHECKPOINT_EVENT_HEADER_AND_FOOTER + (_constantPoolTypes->getThreadGroupCount() * THREADGROUP_ENTRY_SIZE); + + requireBufferSize += CHECKPOINT_EVENT_HEADER_AND_FOOTER + (_constantPoolTypes->getModuleCount() * MODULE_ENTRY_SIZE); + + requireBufferSize += CHECKPOINT_EVENT_HEADER_AND_FOOTER + (_constantPoolTypes->getStackTraceCount() * STACKTRACE_ENTRY_SIZE); + + requireBufferSize += CHECKPOINT_EVENT_HEADER_AND_FOOTER + (_constantPoolTypes->getStackFrameCount() * STACKFRAME_ENTRY_SIZE); + + requireBufferSize += _constantPoolTypes->getExecutionSampleCount() * EXECUTION_SAMPLE_EVENT_SIZE; + + requireBufferSize += _constantPoolTypes->getThreadStartCount() * THREAD_START_EVENT_SIZE; + + requireBufferSize += _constantPoolTypes->getThreadEndCount() * THREAD_END_EVENT_SIZE; + + requireBufferSize += _constantPoolTypes->getThreadSleepCount() * THREAD_SLEEP_EVENT_SIZE; + + return requireBufferSize; + } + + ~VM_JFRChunkWriter() + { + delete(_constantPoolTypes); + } +}; +#endif /* defined(J9VM_OPT_JFR) */ +#endif /* JFRCHUNKWRITER_HPP_ */ diff --git a/runtime/vm/JFRConstantPoolTypes.cpp b/runtime/vm/JFRConstantPoolTypes.cpp new file mode 100644 index 00000000000..cb7fc974159 --- /dev/null +++ b/runtime/vm/JFRConstantPoolTypes.cpp @@ -0,0 +1,1104 @@ +/******************************************************************************* + * Copyright IBM Corp. and others 2024 + * + * This program and the accompanying materials are made available under + * the terms of the Eclipse Public License 2.0 which accompanies this + * distribution and is available at https://www.eclipse.org/legal/epl-2.0/ + * or the Apache License, Version 2.0 which accompanies this distribution and + * is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * This Source Code may also be made available under the following + * Secondary Licenses when the conditions for such availability set + * forth in the Eclipse Public License, v. 2.0 are satisfied: GNU + * General Public License, version 2 with the GNU Classpath + * Exception [1] and GNU General Public License, version 2 with the + * OpenJDK Assembly Exception [2]. + * + * [1] https://www.gnu.org/software/classpath/license.html + * [2] https://openjdk.org/legal/assembly-exception.html + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0 + *******************************************************************************/ +#include "j9.h" +#include "j9protos.h" +#include "j9consts.h" +#include "j9vmconstantpool.h" + +#if defined(J9VM_OPT_JFR) + +#include "JFRConstantPoolTypes.hpp" + +UDATA +VM_JFRConstantPoolTypes::jfrClassHashFn(void *key, void *userData) +{ + ClassEntry *classEntry = (ClassEntry *) key; + + return (UDATA) classEntry->clazz; +} + +UDATA +VM_JFRConstantPoolTypes::jfrClassHashEqualFn(void *tableNode, void *queryNode, void *userData) +{ + ClassEntry *tableEntry = (ClassEntry *) tableNode; + ClassEntry *queryEntry = (ClassEntry *) queryNode; + + return tableEntry->clazz == queryEntry->clazz; +} + +UDATA +VM_JFRConstantPoolTypes::jfrPackageHashFn(void *key, void *userData) +{ + PackageEntry *packageEntry = (PackageEntry *) key; + + return *(UDATA*)&packageEntry->pkgID; +} + +UDATA +VM_JFRConstantPoolTypes::jfrPackageHashEqualFn(void *tableNode, void *queryNode, void *userData) +{ + PackageEntry *tableEntry = (PackageEntry *) tableNode; + PackageEntry *queryEntry = (PackageEntry *) queryNode; + + return tableEntry->pkgID == queryEntry->pkgID; +} + +UDATA +VM_JFRConstantPoolTypes::classloaderNameHashFn(void *key, void *userData) +{ + ClassloaderEntry *classLoaderEntry = (ClassloaderEntry *) key; + + return (UDATA) classLoaderEntry->classLoader; +} + +UDATA +VM_JFRConstantPoolTypes::VM_JFRConstantPoolTypes::classloaderNameHashEqualFn(void *tableNode, void *queryNode, void *userData) +{ + ClassloaderEntry *tableEntry = (ClassloaderEntry *) tableNode; + ClassloaderEntry *queryEntry = (ClassloaderEntry *) queryNode; + + return tableEntry->classLoader == queryEntry->classLoader; +} + +UDATA +VM_JFRConstantPoolTypes::methodNameHashFn(void *key, void *userData) +{ + MethodEntry *methodEntry = (MethodEntry *) key; + + return (UDATA) methodEntry->romMethod; +} + +UDATA +VM_JFRConstantPoolTypes::methodNameHashEqualFn(void *tableNode, void *queryNode, void *userData) +{ + MethodEntry *tableEntry = (MethodEntry *) tableNode; + MethodEntry *queryEntry = (MethodEntry *) queryNode; + + return tableEntry->romMethod == queryEntry->romMethod; +} + +UDATA +VM_JFRConstantPoolTypes::threadHashFn(void *key, void *userData) +{ + ThreadEntry *threadEntry = (ThreadEntry *) key; + + return (UDATA) threadEntry->vmThread; +} + +UDATA +VM_JFRConstantPoolTypes::threadHashEqualFn(void *tableNode, void *queryNode, void *userData) +{ + ThreadEntry *tableEntry = (ThreadEntry *) tableNode; + ThreadEntry *queryEntry = (ThreadEntry *) queryNode; + + return tableEntry->vmThread == queryEntry->vmThread; +} + +UDATA +VM_JFRConstantPoolTypes::stackTraceHashFn(void *key, void *userData) +{ + StackTraceEntry *entry = (StackTraceEntry*) key; + + return (U_64)(UDATA)entry->vmThread ^ (U_64)entry->time; +} + +UDATA +VM_JFRConstantPoolTypes::stackTraceHashEqualFn(void *tableNode, void *queryNode, void *userData) +{ + StackTraceEntry *tableEntry = (StackTraceEntry *) tableNode; + StackTraceEntry *queryEntry = (StackTraceEntry *) queryNode; + + return tableEntry->vmThread == queryEntry->vmThread && tableEntry->time == queryEntry->time; +} + +UDATA +VM_JFRConstantPoolTypes::threadGroupHashFn(void *key, void *userData) +{ + J9JavaVM *vm = (J9JavaVM*) userData; + ThreadGroupEntry *entry = (ThreadGroupEntry *) key; + + void *name = entry->threadGroupName; + + return vm->memoryManagerFunctions->j9gc_stringHashFn(&name, vm); +} + +UDATA +VM_JFRConstantPoolTypes::threadGroupHashEqualFn(void *tableNode, void *queryNode, void *userData) +{ + J9JavaVM *vm = (J9JavaVM*) userData; + ThreadGroupEntry *tableEntry = (ThreadGroupEntry *) tableNode; + ThreadGroupEntry *queryEntry = (ThreadGroupEntry *) queryNode; + + j9object_t tableName = tableEntry->threadGroupName; + + j9object_t queryName = queryEntry->threadGroupName; + + return vm->memoryManagerFunctions->j9gc_stringHashEqualFn(&tableName, &queryName, vm); +} + + +UDATA +VM_JFRConstantPoolTypes::jfrStringUTF8HashFn(void *key, void *userData) +{ + StringUTF8Entry *stringEntry = (StringUTF8Entry *) key; + + J9UTF8 *name = stringEntry->string; + + return (UDATA) name; +} + +UDATA +VM_JFRConstantPoolTypes::jfrStringUTF8HashEqualFn(void *tableNode, void *queryNode, void *userData) +{ + StringUTF8Entry *tableEntry = (StringUTF8Entry *) tableNode; + StringUTF8Entry *queryEntry = (StringUTF8Entry *) queryNode; + + J9UTF8 *tableName = tableEntry->string; + + J9UTF8 *queryName = queryEntry->string; + + return tableName == queryName; +} + +UDATA +VM_JFRConstantPoolTypes::jfrModuleHashFn(void *key, void *userData) +{ + ModuleEntry *moduleEntry = (ModuleEntry *) key; + + return (UDATA) moduleEntry->module; +} + +UDATA +VM_JFRConstantPoolTypes::jfrModuleHashEqualFn(void *tableNode, void *queryNode, void *userData) +{ + ModuleEntry *tableEntry = (ModuleEntry *) tableNode; + ModuleEntry *queryEntry = (ModuleEntry *) queryNode; + + J9Module *tableName = tableEntry->module; + + J9Module *queryName = queryEntry->module; + + return tableName == queryName; +} + +UDATA +VM_JFRConstantPoolTypes::walkStringUTF8TablePrint(void *entry, void *userData) +{ + StringUTF8Entry *tableEntry = (StringUTF8Entry *) entry; + J9VMThread *currentThread = (J9VMThread *)userData; + PORT_ACCESS_FROM_VMC(currentThread); + + j9tty_printf(PORTLIB, "%u) name=%.*s\n", tableEntry->index, J9UTF8_LENGTH(tableEntry->string), (char*) J9UTF8_DATA(tableEntry->string)); + + return FALSE; +} + +UDATA +VM_JFRConstantPoolTypes::walkClassesTablePrint(void *entry, void *userData) +{ + ClassEntry *tableEntry = (ClassEntry *) entry; + J9VMThread *currentThread = (J9VMThread *)userData; + PORT_ACCESS_FROM_VMC(currentThread); + + j9tty_printf(PORTLIB, "%u) classLoaderIndex=%u nameStringUTF8Index=%u packageIndex=%u modifiers=%i \n", tableEntry->index, tableEntry->classLoaderIndex, tableEntry->nameStringUTF8Index, tableEntry->packageIndex, tableEntry->modifiers); + + return FALSE; +} + +UDATA +VM_JFRConstantPoolTypes::walkClassLoadersTablePrint(void *entry, void *userData) +{ + ClassloaderEntry *tableEntry = (ClassloaderEntry *) entry; + J9VMThread *currentThread = (J9VMThread *)userData; + PORT_ACCESS_FROM_VMC(currentThread); + + j9tty_printf(PORTLIB, "%u) classIndex=%u nameIndex=%u\n", tableEntry->index, tableEntry->classIndex, tableEntry->nameIndex); + + return FALSE; +} + +UDATA +VM_JFRConstantPoolTypes::walkMethodTablePrint(void *entry, void *userData) +{ + MethodEntry *tableEntry = (MethodEntry *) entry; + J9VMThread *currentThread = (J9VMThread *)userData; + PORT_ACCESS_FROM_VMC(currentThread); + + j9tty_printf(PORTLIB, "%u) classIndex=%u nameIndex=%u descriptorStringUTF8Index=%u modifiers=%u\n", tableEntry->index, tableEntry->classIndex, tableEntry->nameStringUTF8Index, tableEntry->descriptorStringUTF8Index, tableEntry->modifiers); + + return FALSE; +} + +UDATA +VM_JFRConstantPoolTypes::walkModuleTablePrint(void *entry, void *userData) +{ + ModuleEntry *tableEntry = (ModuleEntry *) entry; + J9VMThread *currentThread = (J9VMThread *)userData; + PORT_ACCESS_FROM_VMC(currentThread); + + j9tty_printf(PORTLIB, "%u) nameStringIndex=%u versionStringIndex=%u locationStringUTF8Index=%u\n", tableEntry->index, tableEntry->nameStringIndex, tableEntry->versionStringIndex, tableEntry->locationStringUTF8Index); + + return FALSE; +} + +UDATA +VM_JFRConstantPoolTypes::walkPackageTablePrint(void *entry, void *userData) +{ + PackageEntry *tableEntry = (PackageEntry *) entry; + J9VMThread *currentThread = (J9VMThread *)userData; + PORT_ACCESS_FROM_VMC(currentThread); + + j9tty_printf(PORTLIB, "%u) moduleIndex=%u packageName=%.*s exported=%u\n", tableEntry->index, tableEntry->moduleIndex, tableEntry->packageNameLength, (char*)tableEntry->packageName, tableEntry->exported); + + return FALSE; +} + +UDATA +VM_JFRConstantPoolTypes::walkThreadTablePrint(void *entry, void *userData) +{ + ThreadEntry *tableEntry = (ThreadEntry *) entry; + J9VMThread *currentThread = (J9VMThread *)userData; + PORT_ACCESS_FROM_VMC(currentThread); + + j9tty_printf(PORTLIB, "%u) osTID=%lu javaTID=%lu javaThreadName=%.*s osThreadName=%.*s threadGroupIndex=%u\n", tableEntry->index, tableEntry->osTID, tableEntry->javaTID, J9UTF8_LENGTH(tableEntry->javaThreadName), J9UTF8_DATA(tableEntry->javaThreadName), J9UTF8_LENGTH(tableEntry->osThreadName), J9UTF8_DATA(tableEntry->osThreadName), tableEntry->threadGroupIndex); + + return FALSE; +} + +UDATA +VM_JFRConstantPoolTypes::walkThreadGroupTablePrint(void *entry, void *userData) +{ + ThreadGroupEntry *tableEntry = (ThreadGroupEntry *) entry; + J9VMThread *currentThread = (J9VMThread *)userData; + PORT_ACCESS_FROM_VMC(currentThread); + + j9tty_printf(PORTLIB, "%u) parentIndex=%u name=%.*s\n", tableEntry->index, tableEntry->parentIndex, J9UTF8_LENGTH(tableEntry->name), J9UTF8_DATA(tableEntry->name)); + + return FALSE; +} + +UDATA +VM_JFRConstantPoolTypes::walkStackTraceTablePrint(void *entry, void *userData) +{ + StackTraceEntry *tableEntry = (StackTraceEntry *) entry; + J9VMThread *currentThread = (J9VMThread *)userData; + PORT_ACCESS_FROM_VMC(currentThread); + + j9tty_printf(PORTLIB, "%u) time=%li numOfFrames=%u frames=%p curr=%p next=%p \n", tableEntry->index, tableEntry->time, tableEntry->numOfFrames, tableEntry->frames, tableEntry, tableEntry->next); + + return FALSE; +} + +UDATA +VM_JFRConstantPoolTypes::fixupShallowEntries(void *entry, void *userData) +{ + ClassEntry *tableEntry = (ClassEntry *) entry; + VM_JFRConstantPoolTypes *cp = (VM_JFRConstantPoolTypes*) userData; + + cp->getClassEntry(tableEntry->clazz); + + return FALSE; +} + +UDATA +VM_JFRConstantPoolTypes::mergeStringUTF8EntriesToGlobalTable(void *entry, void *userData) +{ + StringUTF8Entry *tableEntry = (StringUTF8Entry *) entry; + VM_JFRConstantPoolTypes *cp = (VM_JFRConstantPoolTypes*) userData; + + cp->_globalStringTable[tableEntry->index] = tableEntry; + cp->_requiredBufferSize += J9UTF8_LENGTH(tableEntry->string); + return FALSE; +} + +UDATA +VM_JFRConstantPoolTypes::mergePackageEntriesToGlobalTable(void *entry, void *userData) +{ + PackageEntry *tableEntry = (PackageEntry *) entry; + VM_JFRConstantPoolTypes *cp = (VM_JFRConstantPoolTypes*) userData; + UDATA packageNameLength = 0; + + getPackageName(tableEntry->pkgID, &packageNameLength); + cp->_globalStringTable[tableEntry->index + cp->_stringUTF8Count] = tableEntry; + cp->_requiredBufferSize += packageNameLength; + return FALSE; +} + +U_32 +VM_JFRConstantPoolTypes::getMethodEntry(J9ROMMethod *romMethod, J9Class *ramClass) +{ + U_32 index = U_32_MAX; + MethodEntry *entry = NULL; + MethodEntry entryBuffer = {0}; + + entry = &entryBuffer; + entry->romMethod = romMethod; + _buildResult = OK; + + entry = (MethodEntry *) hashTableFind(_methodTable, entry); + if (NULL != entry) { + index = entry->index; + goto done; + } else { + entry = &entryBuffer; + } + + entry->classIndex = getClassEntry(ramClass); + if (isResultNotOKay()) goto done; + + entry->nameStringUTF8Index = addStringUTF8Entry(J9ROMMETHOD_NAME(romMethod)); + if (isResultNotOKay()) goto done; + + entry->descriptorStringUTF8Index = addStringUTF8Entry(J9ROMMETHOD_SIGNATURE(romMethod)); + if (isResultNotOKay()) goto done; + + entry->modifiers = romMethod->modifiers; + entry->index = _methodCount; + entry->hidden = FALSE; + + entry = (MethodEntry*) hashTableAdd(_methodTable, entry); + if (NULL == entry) { + _buildResult = OutOfMemory; + goto done; + } + + if (NULL == _firstMethodEntry) { + _firstMethodEntry = entry; + } + + if (NULL != _previousMethodEntry) { + _previousMethodEntry->next = entry; + } + _previousMethodEntry = entry; + + index = entry->index; + _methodCount++; + +done: + return index; +} + +U_32 +VM_JFRConstantPoolTypes::getClassEntry(J9Class *clazz) +{ + U_32 index = U_32_MAX; + ClassEntry *entry = NULL; + ClassEntry entryBuffer = {0}; + + entry = &entryBuffer; + entry->clazz = clazz; + _buildResult = OK; + + entry = (ClassEntry *) hashTableFind(_classTable, entry); + if (NULL != entry) { + index = entry->index; + if (!entry->shallow) { + goto done; + } + } else { + entry = &entryBuffer; + } + + entry->nameStringUTF8Index = addStringUTF8Entry(J9ROMCLASS_CLASSNAME(clazz->romClass)); + if (isResultNotOKay()) goto done; + + entry->classLoaderIndex = addClassLoaderEntry(clazz->classLoader); + if (isResultNotOKay()) goto done; + + entry->packageIndex = addPackageEntry(clazz); + if (isResultNotOKay()) goto done; + + entry->modifiers = clazz->romClass->modifiers; + entry->hidden = FALSE; //TODO + + if (NULL != entry && !entry->shallow) { + entry->index = _classCount; + _classCount++; + + entry = (ClassEntry*) hashTableAdd(_classTable, entry); + if (NULL == entry) { + _buildResult = OutOfMemory; + goto done; + } + + if (NULL == _firstClassEntry) { + _firstClassEntry = entry; + } + + if (NULL != _previousClassEntry) { + _previousClassEntry->next = entry; + } + _previousClassEntry = entry; + } + + entry->shallow = NULL; + + index = entry->index; + +done: + return index; +} + +U_32 +VM_JFRConstantPoolTypes::addPackageEntry(J9Class *clazz) +{ + U_32 index = U_32_MAX; + J9PackageIDTableEntry *pkgID = NULL; + PackageEntry *entry = NULL; + UDATA packageNameLength = 0; + const char *packageName = NULL; + PackageEntry entryBuffer = {0}; + + entry = &entryBuffer; + _buildResult = OK; + + pkgID = hashPkgTableAt(clazz->classLoader, clazz->romClass); + entry->pkgID = pkgID; + + if (NULL == pkgID) { + /* default pacakge */ + index = 0; + goto done; + } + + entry = (PackageEntry *) hashTableFind(_packageTable, entry); + if (NULL != entry) { + index = entry->index; + goto done; + } else { + entry = &entryBuffer; + } + + entry->moduleIndex = addModuleEntry(clazz->module); + if (isResultNotOKay()) goto done; + + packageName = (const char *) getPackageName(entry->pkgID, &packageNameLength); + if (NULL == packageName) { + _buildResult = InternalVMError; + goto done; + } + + entry->packageName = (U_8*) packageName; + entry->packageNameLength = packageNameLength; + + entry->exported = FALSE; //TODO + + entry->index = _packageCount; + _packageCount++; + + entry = (PackageEntry*) hashTableAdd(_packageTable, entry); + if (NULL == entry) { + _buildResult = OutOfMemory; + goto done; + } + + if (NULL == _firstPackageEntry) { + _firstPackageEntry = entry; + } + + if (NULL != _previousPackageEntry) { + _previousPackageEntry->next = entry; + } + _previousPackageEntry = entry; + + index = entry->index; +done: + return index; +} + +U_32 +VM_JFRConstantPoolTypes::addModuleEntry(J9Module *module) +{ + U_32 index = U_32_MAX; + ModuleEntry *entry = NULL; + ModuleEntry entryBuffer = {0}; + + entry = &entryBuffer; + entry->module = module; + _buildResult = OK; + + if (NULL == entry->module) { + /* unnamed module */ + index = 0; + goto done; + } + + entry = (ModuleEntry *) hashTableFind(_moduleTable, entry); + if (NULL != entry) { + index = entry->index; + goto done; + } else { + entry = &entryBuffer; + } + + entry->nameStringIndex = addStringEntry(entry->module->moduleName); + if (isResultNotOKay()) goto done; + + entry->versionStringIndex = addStringEntry(entry->module->version); + if (isResultNotOKay()) goto done; + + entry->locationStringUTF8Index = addStringUTF8Entry(getModuleJRTURL(_currentThread, entry->module->classLoader, entry->module)); + if (isResultNotOKay()) goto done; + + entry->classLoaderIndex = addClassLoaderEntry(entry->module->classLoader); + if (isResultNotOKay()) goto done; + + entry->index = _moduleCount; + _moduleCount++; + + entry = (ModuleEntry*) hashTableAdd(_moduleTable, entry); + if (NULL == entry) { + _buildResult = OutOfMemory; + goto done; + } + + if (NULL == _firstModuleEntry) { + _firstModuleEntry = entry; + } + + if (NULL != _previousModuleEntry) { + _previousModuleEntry->next = entry; + } + _previousModuleEntry = entry; + + index = entry->index; +done: + return index; +} + +U_32 +VM_JFRConstantPoolTypes::addClassLoaderEntry(J9ClassLoader *classLoader) +{ + U_32 index = U_32_MAX; + ClassloaderEntry *entry = NULL; + ClassloaderEntry entryBuffer = {0}; + j9object_t loaderName = NULL; + + entry = &entryBuffer; + entry->classLoader = classLoader; + _buildResult = OK; + + entry = (ClassloaderEntry *) hashTableFind(_classLoaderTable, entry); + if (NULL != entry) { + index = entry->index; + goto done; + } else { + entry = &entryBuffer; + } + + entry->classIndex = getShallowClassEntry(J9OBJECT_CLAZZ(_currentThread, classLoader->classLoaderObject)); + if (isResultNotOKay()) goto done; + +#if JAVA_SPEC_VERSION > 8 + loaderName = J9VMJAVALANGCLASSLOADER_CLASSLOADERNAME(_currentThread, classLoader->classLoaderObject); + if (NULL == loaderName) { + entry->nameIndex = addStringUTF8Entry((J9UTF8*)&bootLoaderName); + } else +#endif /* JAVA_SPEC_VERSION > 8 */ + { + entry->nameIndex = addStringEntry(loaderName); + } + + if (isResultNotOKay()) goto done; + + entry->index = _classLoaderCount; + _classLoaderCount++; + + entry = (ClassloaderEntry*) hashTableAdd(_classLoaderTable, entry); + if (NULL == entry) { + _buildResult = OutOfMemory; + goto done; + } + + if (NULL == _firstClassloaderEntry) { + _firstClassloaderEntry = entry; + } + + if (NULL != _previousClassloaderEntry) { + _previousClassloaderEntry->next = entry; + } + _previousClassloaderEntry = entry; + + index = entry->index; + +done: + return index; +} + +U_32 +VM_JFRConstantPoolTypes::getShallowClassEntry(J9Class *clazz) +{ + U_32 index = U_32_MAX; + ClassEntry *entry = NULL; + ClassEntry entryBuffer = {0}; + + entry = &entryBuffer; + entry->clazz = clazz; + _buildResult = OK; + + entry = (ClassEntry *) hashTableFind(_classTable, entry); + if (NULL != entry) { + index = entry->index; + goto done; + } else { + entry = &entryBuffer; + } + + entry->index = _classCount; + _classCount++; + + entry = (ClassEntry*)hashTableAdd(_classTable, entry); + if (NULL == entry) { + _buildResult = OutOfMemory; + goto done; + } + index = entry->index; + + entry->classLoaderIndex = U_32_MAX; + entry->nameStringUTF8Index = U_32_MAX; + entry->packageIndex = U_32_MAX; + entry->modifiers = U_32_MAX; + entry->hidden = FALSE; + entry->shallow = clazz; + + if (NULL == _firstClassEntry) { + _firstClassEntry = entry; + } + + if (NULL != _previousClassEntry) { + _previousClassEntry->next = entry; + } + _previousClassEntry = entry; + +done: + return index; +} + +U_32 +VM_JFRConstantPoolTypes::addStringUTF8Entry(J9UTF8 *string, bool free) +{ + U_32 index = U_32_MAX; + StringUTF8Entry *entry = NULL; + StringUTF8Entry entryBuffer = {0}; + + entry = &entryBuffer; + entry->string = string; + _buildResult = OK; + + if (NULL == string) { + /* default null string */ + index = 0; + goto done; + } + + entry = (StringUTF8Entry *) hashTableFind(_stringUTF8Table, entry); + if (NULL != entry) { + index = entry->index; + if (free) { + j9mem_free_memory(string); + } + goto done; + } else { + entry = &entryBuffer; + } + + entry->index = _stringUTF8Count; + entry->free = free; + _stringUTF8Count++; + + if (NULL == hashTableAdd(_stringUTF8Table, &entryBuffer)) { + _buildResult = OutOfMemory; + goto done; + } + index = entry->index; + +done: + return index; +} + +U_32 +VM_JFRConstantPoolTypes::addStringUTF8Entry(J9UTF8 *string) +{ + return addStringUTF8Entry(string, false); +} + +U_32 +VM_JFRConstantPoolTypes::addStringEntry(j9object_t string) +{ + if (NULL == string) { + /* default NULL string entry */ + return 0; + } else { + J9UTF8 *stringUTF8 = copyStringToJ9UTF8WithMemAlloc(_currentThread, string, J9_STR_NONE, "", 0, NULL, 0); + return addStringUTF8Entry(stringUTF8, true); + } +} + +U_32 +VM_JFRConstantPoolTypes::addThreadEntry(J9VMThread *vmThread) +{ + U_32 index = U_32_MAX; + ThreadEntry *entry = NULL; + ThreadEntry entryBuffer = {0}; + + entry = &entryBuffer; + entry->vmThread = vmThread; + _buildResult = OK; + omrthread_t osThread = vmThread->osThread; + + entry = (ThreadEntry *) hashTableFind(_threadTable, entry); + if (NULL != entry) { + index = entry->index; + goto done; + } else { + entry = &entryBuffer; + } + + entry->osTID = ((J9AbstractThread*)osThread)->tid; + entry->javaTID = J9VMJAVALANGTHREAD_TID(_currentThread, vmThread->threadObject); + + entry->javaThreadName = copyStringToJ9UTF8WithMemAlloc(_currentThread, J9VMJAVALANGTHREAD_NAME(_currentThread, vmThread->threadObject), J9_STR_NONE, "", 0, NULL, 0); + + /* TODO is this always true? */ + entry->osThreadName = entry->javaThreadName; + if (isResultNotOKay()) goto done; +#if JAVA_SPEC_VERSION >= 19 + entry->threadGroupIndex = addThreadGroupEntry(J9VMJAVALANGTHREADFIELDHOLDER_GROUP(_currentThread, (J9VMJAVALANGTHREAD_HOLDER(_currentThread, vmThread->threadObject)))); +#else /* JAVA_SPEC_VERSION >= 19 */ + entry->threadGroupIndex = addThreadGroupEntry(J9VMJAVALANGTHREAD_GROUP(_currentThread, vmThread->threadObject)); +#endif /* JAVA_SPEC_VERSION >= 19 */ + if (isResultNotOKay()) goto done; + + entry->index = _threadCount; + _threadCount++; + + entry = (ThreadEntry*) hashTableAdd(_threadTable, &entryBuffer); + if (NULL == entry) { + _buildResult = OutOfMemory; + goto done; + } + + if (NULL == _firstThreadEntry) { + _firstThreadEntry = entry; + } + + if (NULL != _previousThreadEntry) { + _previousThreadEntry->next = entry; + } + _previousThreadEntry = entry; + + index = entry->index; + +done: + return index; +} + +U_32 +VM_JFRConstantPoolTypes::addThreadGroupEntry(j9object_t threadGroup) +{ + U_32 index = U_32_MAX; + ThreadGroupEntry *entry = NULL; + ThreadGroupEntry entryBuffer = {0}; + + entry = &entryBuffer; + + if (NULL == threadGroup) { + return 0; + } + + entry->threadGroupName = J9VMJAVALANGTHREADGROUP_NAME(_currentThread, threadGroup); + _buildResult = OK; + + entry = (ThreadGroupEntry *) hashTableFind(_threadGroupTable, entry); + if (NULL != entry) { + index = entry->index; + goto done; + } else { + entry = &entryBuffer; + } + + entry->parentIndex = addThreadGroupEntry(J9VMJAVALANGTHREADGROUP_PARENT(_currentThread, threadGroup)); + if (isResultNotOKay()) goto done; + + entry->name = copyStringToJ9UTF8WithMemAlloc(_currentThread, J9VMJAVALANGTHREADGROUP_NAME(_currentThread, threadGroup), J9_STR_NONE, "", 0, NULL, 0); + + entry->index = _threadGroupCount; + _threadGroupCount++; + + entry = (ThreadGroupEntry*) hashTableAdd(_threadGroupTable, &entryBuffer); + if (NULL == entry) { + _buildResult = OutOfMemory; + goto done; + } + + if (NULL == _firstThreadGroupEntry) { + _firstThreadGroupEntry = entry; + } + + if (NULL != _previousThreadGroupEntry) { + _previousThreadGroupEntry->next = entry; + } + _previousThreadGroupEntry = entry; + + index = entry->index; + +done: + return index; +} + +U_32 +VM_JFRConstantPoolTypes::addStackTraceEntry(J9VMThread *vmThread, I_64 time, U_32 numOfFrames) +{ + U_32 index = U_32_MAX; + StackTraceEntry *entry = NULL; + StackTraceEntry entryBuffer = {0}; + + entry = &entryBuffer; + entry->vmThread = vmThread; + entry->time = time; + _buildResult = OK; + + entry = (StackTraceEntry *) hashTableFind(_stackTraceTable, entry); + if (NULL != entry) { + index = entry->index; + goto done; + } else { + entry = &entryBuffer; + } + + entry->frames = _currentStackFrameBuffer; + entry->numOfFrames = numOfFrames; + + _currentStackFrameBuffer = NULL; + + entry->index = _stackTraceCount; + entry->truncated = FALSE; + + entry->next = NULL; + _stackTraceCount++; + + entry = (StackTraceEntry*) hashTableAdd(_stackTraceTable, entry); + if (NULL == entry) { + _buildResult = OutOfMemory; + goto done; + } + + if (NULL == _firstStackTraceEntry) { + _firstStackTraceEntry = entry; + } + + if (NULL != _previousStackTraceEntry) { + _previousStackTraceEntry->next = entry; + } + _previousStackTraceEntry = entry; + + index = entry->index; + +done: + return index; +} + +U_32 +VM_JFRConstantPoolTypes::addExecutionSampleEntry(J9JFRExecutionSample *executionSampleData) +{ + ExecutionSampleEntry *entry = (ExecutionSampleEntry*)pool_newElement(_executionSampleTable); + U_32 index = U_32_MAX; + + if (NULL == entry) { + _buildResult = OutOfMemory; + goto done; + } + + entry->vmThread = executionSampleData->vmThread; + entry->time = executionSampleData->startTime; + entry->state = RUNNABLE; //TODO + + entry->threadIndex = addThreadEntry(entry->vmThread); + if (isResultNotOKay()) goto done; + + entry->stackTraceIndex = consumeStackTrace(entry->vmThread, (UDATA*) (executionSampleData + 1), executionSampleData->stackTraceSize); + if (isResultNotOKay()) goto done; + + index = _executionSampleCount++; + entry->index = index; + +done: + return index; +} + +U_32 +VM_JFRConstantPoolTypes::addThreadStartEntry(J9JFRThreadStart *threadStartData) +{ + ThreadStartEntry *entry = (ThreadStartEntry*)pool_newElement(_threadStartTable); + U_32 index = U_32_MAX; + + if (NULL == entry) { + _buildResult = OutOfMemory; + goto done; + } + + entry->time = threadStartData->startTime; + + entry->threadIndex = addThreadEntry(threadStartData->thread); + if (isResultNotOKay()) goto done; + + entry->eventThreadIndex = addThreadEntry(threadStartData->thread); + if (isResultNotOKay()) goto done; + + entry->parentThreadIndex = addThreadEntry(threadStartData->parentThread); + if (isResultNotOKay()) goto done; + + entry->stackTraceIndex = consumeStackTrace(threadStartData->parentThread, (UDATA*)(threadStartData + 1), threadStartData->stackTraceSize); + if (isResultNotOKay()) goto done; + + index = _threadStartCount++; + +done: + return index; +} + +U_32 +VM_JFRConstantPoolTypes::addThreadEndEntry(J9JFREvent *threadEndData) +{ + ThreadEndEntry *entry = (ThreadEndEntry*)pool_newElement(_threadEndTable); + U_32 index = U_32_MAX; + + if (NULL == entry) { + _buildResult = OutOfMemory; + goto done; + } + + entry->time = threadEndData->startTime; + + entry->threadIndex = addThreadEntry(threadEndData->vmThread); + if (isResultNotOKay()) goto done; + + entry->eventThreadIndex = addThreadEntry(threadEndData->vmThread); + if (isResultNotOKay()) goto done; + + index = _threadEndCount++; + +done: + return index; +} + +U_32 +VM_JFRConstantPoolTypes::addThreadSleepEntry(J9JFRThreadSleep *threadSleepData) +{ + ThreadSleepEntry *entry = (ThreadSleepEntry*)pool_newElement(_threadSleepTable); + U_32 index = U_32_MAX; + + if (NULL == entry) { + _buildResult = OutOfMemory; + goto done; + } + + entry->time = threadSleepData->startTime; + entry->duration = threadSleepData->time; + + entry->threadIndex = addThreadEntry(threadSleepData->vmThread); + if (isResultNotOKay()) goto done; + + entry->eventThreadIndex = addThreadEntry(threadSleepData->vmThread); + if (isResultNotOKay()) goto done; + + entry->stackTraceIndex = consumeStackTrace(threadSleepData->vmThread, (UDATA*)(threadSleepData + 1), threadSleepData->stackTraceSize); + if (isResultNotOKay()) goto done; + + index = _threadEndCount++; + +done: + return index; +} + + +void +VM_JFRConstantPoolTypes::printTables() +{ + j9tty_printf(PORTLIB, "--------------- StringUTF8Table ---------------\n"); + hashTableForEachDo(_stringUTF8Table, &walkStringUTF8TablePrint, _currentThread); + + j9tty_printf(PORTLIB, "--------------- Classes Table ---------------\n"); + hashTableForEachDo(_classTable, &walkClassesTablePrint, _currentThread); + + j9tty_printf(PORTLIB, "--------------- ClassLoader Table ---------------\n"); + hashTableForEachDo(_classLoaderTable, &walkClassLoadersTablePrint, _currentThread); + + j9tty_printf(PORTLIB, "--------------- Method Table ---------------\n"); + hashTableForEachDo(_methodTable, &walkMethodTablePrint, _currentThread); + + j9tty_printf(PORTLIB, "--------------- Module Table ---------------\n"); + hashTableForEachDo(_moduleTable, &walkModuleTablePrint, _currentThread); + + j9tty_printf(PORTLIB, "--------------- Package Table ---------------\n"); + hashTableForEachDo(_packageTable, &walkPackageTablePrint, _currentThread); + + j9tty_printf(PORTLIB, "--------------- Thread Table ---------------\n"); + hashTableForEachDo(_threadTable, &walkThreadTablePrint, _currentThread); + + j9tty_printf(PORTLIB, "--------------- ThreadGroup Table ---------------\n"); + hashTableForEachDo(_threadGroupTable, &walkThreadGroupTablePrint, _currentThread); + + j9tty_printf(PORTLIB, "--------------- StackTrace Table ---------------\n"); + hashTableForEachDo(_stackTraceTable, &walkStackTraceTablePrint, _currentThread); +} + +void +VM_JFRConstantPoolTypes::printMergedStringTables() +{ + IDATA i = 1; + IDATA max = 0; + + j9tty_printf(PORTLIB, "--------------- Global String Table ---------------\n"); + + max += _stringUTF8Count; + for (; i < max; i++) { + StringUTF8Entry *tableEntry = (StringUTF8Entry *) _globalStringTable[i]; + + j9tty_printf(PORTLIB, "%li -> ", i); + j9tty_printf(PORTLIB, "%u) name=%.*s\n", tableEntry->index, J9UTF8_LENGTH(tableEntry->string), (char*) J9UTF8_DATA(tableEntry->string)); + } + + max += _packageCount; + for (; i < max; i++) { + PackageEntry *tableEntry = (PackageEntry *) _globalStringTable[i]; + + j9tty_printf(PORTLIB, "%li -> ", i); + j9tty_printf(PORTLIB, "%u) moduleIndex=%u packageName=%.*s exported=%u\n", tableEntry->index, tableEntry->moduleIndex, tableEntry->packageNameLength, (char*)tableEntry->packageName, tableEntry->exported); + } +} + +UDATA +VM_JFRConstantPoolTypes::freeUTF8Strings(void *entry, void *userData) +{ + StringUTF8Entry *tableEntry = (StringUTF8Entry *) entry; + J9VMThread *currentThread = (J9VMThread *)userData; + PORT_ACCESS_FROM_VMC(currentThread); + + if (tableEntry->free) { + j9mem_free_memory(tableEntry->string); + tableEntry->string = NULL; + } + return FALSE; +} + +#endif /* defined(J9VM_OPT_JFR) */ diff --git a/runtime/vm/JFRConstantPoolTypes.hpp b/runtime/vm/JFRConstantPoolTypes.hpp new file mode 100644 index 00000000000..2edf4a51476 --- /dev/null +++ b/runtime/vm/JFRConstantPoolTypes.hpp @@ -0,0 +1,807 @@ +/******************************************************************************* + * Copyright IBM Corp. and others 2024 + * + * This program and the accompanying materials are made available under + * the terms of the Eclipse Public License 2.0 which accompanies this + * distribution and is available at https://www.eclipse.org/legal/epl-2.0/ + * or the Apache License, Version 2.0 which accompanies this distribution and + * is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * This Source Code may also be made available under the following + * Secondary Licenses when the conditions for such availability set + * forth in the Eclipse Public License, v. 2.0 are satisfied: GNU + * General Public License, version 2 with the GNU Classpath + * Exception [1] and GNU General Public License, version 2 with the + * OpenJDK Assembly Exception [2]. + * + * [1] https://www.gnu.org/software/classpath/license.html + * [2] https://openjdk.org/legal/assembly-exception.html + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0 + *******************************************************************************/ +#if !defined(JFRCONSTANTPOOLTYPES_HPP_) +#define JFRCONSTANTPOOLTYPES_HPP_ + +#include "j9cfg.h" +#include "j9.h" +#include "omrlinkedlist.h" +#include "vm_api.h" +#include "vm_internal.h" +#include "ut_j9vm.h" + +#if defined(J9VM_OPT_JFR) + +#include "BufferWriter.hpp" +#include "JFRUtils.hpp" +#include "ObjectAccessBarrierAPI.hpp" +#include "VMHelpers.hpp" + +J9_DECLARE_CONSTANT_UTF8(nullString, "(nullString)"); +J9_DECLARE_CONSTANT_UTF8(defaultPackage, "(defaultPackage)"); +J9_DECLARE_CONSTANT_UTF8(bootLoaderName, "boostrapClassLoader"); + +enum FrameType { + Interpreted = 0, + JIT, + JIT_Inline, + Native, + FrameTypeCount, +}; + +enum ThreadState { + NEW = 0, + TERMINATED, + RUNNABLE, + SLEEPING, + WAITING, + TIMED_WAITING, + PARKED, + TIMED_PARKED, + BLOCKED, + THREADSTATE_COUNT, +}; + +struct ClassEntry { + J9Class *clazz; + U_32 classLoaderIndex; + U_32 nameStringUTF8Index; + U_32 packageIndex; + I_32 modifiers; + BOOLEAN hidden; + U_32 index; + J9Class *shallow; + ClassEntry *next; +}; + +struct PackageEntry { + J9PackageIDTableEntry *pkgID; + U_32 moduleIndex; + BOOLEAN exported; + U_32 packageNameLength; + U_8* packageName; + U_32 index; + PackageEntry *next; +}; + +struct ModuleEntry { + J9Module *module; + U_32 nameStringIndex; + U_32 versionStringIndex; + U_32 locationStringUTF8Index; + U_32 classLoaderIndex; + U_32 index; + ModuleEntry *next; +}; + +struct ClassloaderEntry { + J9ClassLoader *classLoader; + U_32 classIndex; + U_32 nameIndex; + U_32 index; + ClassloaderEntry *next; +}; + +struct MethodEntry { + J9ROMMethod *romMethod; + U_32 classIndex; + U_32 nameStringUTF8Index; + U_32 descriptorStringUTF8Index; + U_32 modifiers; + BOOLEAN hidden; + U_32 index; + MethodEntry *next; +}; + +struct StringUTF8Entry { + J9UTF8 *string; + BOOLEAN free; + U_32 index; +}; + +struct ThreadEntry { + J9VMThread *vmThread; + U_32 index; + U_64 osTID; + U_64 javaTID; + J9UTF8 *javaThreadName; + J9UTF8 *osThreadName; + U_32 threadGroupIndex; + ThreadEntry *next; +}; + +struct ThreadGroupEntry { + j9object_t threadGroupName; + U_32 index; + U_32 parentIndex; + J9UTF8* name; + ThreadGroupEntry *next; +}; + +struct StackFrame { + U_32 methodIndex; + I_32 lineNumber; + I_32 bytecodeIndex; + FrameType frameType; +}; + +struct ExecutionSampleEntry { + J9VMThread *vmThread; + I_64 time; + ThreadState state; + U_32 stackTraceIndex; + U_32 threadIndex; + U_32 index; +}; + +struct ThreadStartEntry { + I_64 time; + U_32 stackTraceIndex; + U_32 threadIndex; + U_32 eventThreadIndex; + U_32 parentThreadIndex; +}; + +struct ThreadEndEntry { + I_64 time; + U_32 threadIndex; + U_32 eventThreadIndex; +}; + +struct ThreadSleepEntry { + I_64 time; + I_64 duration; + U_32 threadIndex; + U_32 eventThreadIndex; + U_32 stackTraceIndex; +}; + +struct StackTraceEntry { + J9VMThread *vmThread; + I_64 time; + U_32 numOfFrames; + U_32 index; + StackFrame *frames; + BOOLEAN truncated; + StackTraceEntry *next; +}; + +class VM_JFRConstantPoolTypes { + /* + * Data members + */ +private: + J9VMThread *_currentThread; + J9JavaVM *_vm; + BuildResult _buildResult; + bool _debug; + J9PortLibrary *privatePortLibrary; + + /* Constantpool types */ + J9HashTable *_classTable; + J9HashTable *_packageTable; + J9HashTable *_moduleTable; + J9HashTable *_classLoaderTable; + J9HashTable *_methodTable; + J9HashTable *_stackTraceTable; + J9HashTable *_stringUTF8Table; + J9HashTable *_threadTable; + J9HashTable *_threadGroupTable; + U_32 _classCount; + U_32 _packageCount; + U_32 _moduleCount; + U_32 _classLoaderCount; + U_32 _methodCount; + U_32 _stackTraceCount; + U_32 _stackFrameCount; + U_32 _stringUTF8Count; + U_32 _threadCount; + U_32 _threadGroupCount; + U_32 _packageNameCount; + + /* Event Types */ + J9Pool *_executionSampleTable; + UDATA _executionSampleCount; + J9Pool *_threadStartTable; + UDATA _threadStartCount; + J9Pool *_threadEndTable; + UDATA _threadEndCount; + J9Pool *_threadSleepTable; + UDATA _threadSleepCount; + + /* Processing buffers */ + StackFrame *_currentStackFrameBuffer; + StackTraceEntry *_previousStackTraceEntry; + StackTraceEntry *_firstStackTraceEntry; + ThreadEntry *_previousThreadEntry; + ThreadEntry *_firstThreadEntry; + ThreadGroupEntry *_previousThreadGroupEntry; + ThreadGroupEntry *_firstThreadGroupEntry; + ModuleEntry *_previousModuleEntry; + ModuleEntry *_firstModuleEntry; + MethodEntry *_previousMethodEntry; + MethodEntry *_firstMethodEntry; + ClassEntry *_previousClassEntry; + ClassEntry *_firstClassEntry; + ClassloaderEntry *_previousClassloaderEntry; + ClassloaderEntry *_firstClassloaderEntry; + PackageEntry *_previousPackageEntry; + PackageEntry *_firstPackageEntry; + + /* default values */ + ThreadGroupEntry _defaultThreadGroup; + StringUTF8Entry _defaultStringUTF8Entry; + PackageEntry _defaultPackageEntry; + ModuleEntry _defaultModuleEntry; + + UDATA _requiredBufferSize; + U_32 _currentFrameCount; + void **_globalStringTable; + +protected: + +public: + static constexpr int STRING_BUFFER_LENGTH = 128; + + /* + * Function members + */ +private: + static UDATA classloaderNameHashFn(void *key, void *userData); + + static UDATA classloaderNameHashEqualFn(void *tableNode, void *queryNode, void *userData); + + static UDATA methodNameHashFn(void *key, void *userData); + + static UDATA methodNameHashEqualFn(void *tableNode, void *queryNode, void *userData); + + static UDATA jfrStringHashFn(void *key, void *userData); + + static UDATA jfrStringHashEqualFn(void *tableNode, void *queryNode, void *userData); + + static UDATA threadHashFn(void *key, void *userData); + + static UDATA threadHashEqualFn(void *tableNode, void *queryNode, void *userData); + + static UDATA jfrClassHashFn(void *key, void *userData); + + static UDATA jfrClassHashEqualFn(void *tableNode, void *queryNode, void *userData); + + static UDATA jfrPackageHashFn(void *key, void *userData); + + static UDATA jfrPackageHashEqualFn(void *tableNode, void *queryNode, void *userData); + + static UDATA jfrStringUTF8HashEqualFn(void *tableNode, void *queryNode, void *userData); + + static UDATA jfrStringUTF8HashFn(void *key, void *userData); + + static UDATA jfrModuleHashEqualFn(void *tableNode, void *queryNode, void *userData); + + static UDATA jfrModuleHashFn(void *key, void *userData); + + static UDATA stackTraceHashEqualFn(void *tableNode, void *queryNode, void *userData); + + static UDATA stackTraceHashFn(void *key, void *userData); + + static UDATA threadGroupHashEqualFn(void *tableNode, void *queryNode, void *userData); + + static UDATA threadGroupHashFn(void *key, void *userData); + + static UDATA walkStringTablePrint(void *entry, void *userData); + + static UDATA walkStringUTF8TablePrint(void *entry, void *userData); + + static UDATA walkClassesTablePrint(void *entry, void *userData); + + static UDATA walkClassLoadersTablePrint(void *entry, void *userData); + + static UDATA walkThreadTablePrint(void *entry, void *userData); + + static UDATA walkThreadGroupTablePrint(void *entry, void *userData); + + static UDATA walkStackTraceTablePrint(void *entry, void *userData); + + static UDATA fixupShallowEntries(void *entry, void *userData); + + static UDATA walkMethodTablePrint(void *entry, void *userData); + + static UDATA walkModuleTablePrint(void *entry, void *userData); + + static UDATA walkPackageTablePrint(void *entry, void *userData); + + static UDATA mergeStringEntriesToGlobalTable(void *entry, void *userData); + + static UDATA mergeStringUTF8EntriesToGlobalTable(void *entry, void *userData); + + static UDATA mergePackageEntriesToGlobalTable(void *entry, void *userData); + + static UDATA freeUTF8Strings(void *entry, void *userData); + + U_32 getMethodEntry(J9ROMMethod *romMethod, J9Class *ramClass); + + U_32 getClassEntry(J9Class *clazz); + + U_32 addPackageEntry(J9Class *clazz); + + U_32 addModuleEntry(J9Module *module); + + U_32 addClassLoaderEntry(J9ClassLoader *classLoader); + + /* + * Adds class to the table but doesnt fill out fields to avoid + * circularities. + */ + U_32 getShallowClassEntry(J9Class *clazz); + + U_32 addStringEntry(j9object_t string); + + U_32 addStringUTF8Entry(J9UTF8 *string); + + U_32 addStringUTF8Entry(J9UTF8 *string, bool free); + + U_32 addThreadEntry(J9VMThread *vmThread); + + U_32 addThreadGroupEntry(j9object_t threadGroup); + + U_32 addStackTraceEntry(J9VMThread *vmThread, I_64 time, U_32 numOfFrames); + + void printMergedStringTables(); + + bool isResultNotOKay() { + if (_buildResult != OK) { + if (_debug) { + printf("failure!!!\n"); + } + return true; + } + return false; + } + + static UDATA stackTraceCallback(J9VMThread *vmThread, void *userData, UDATA bytecodeOffset, J9ROMClass *romClass, J9ROMMethod *romMethod, J9UTF8 *fileName, UDATA lineNumber, J9ClassLoader *classLoader, J9Class *ramClass) + { + VM_JFRConstantPoolTypes *cp = (VM_JFRConstantPoolTypes*) userData; + StackFrame *frame = &cp->_currentStackFrameBuffer[cp->_currentFrameCount]; + + if ((UDATA)-1 != bytecodeOffset) { + cp->_currentFrameCount++; + + frame->methodIndex = cp->getMethodEntry(romMethod, ramClass); + frame->lineNumber = lineNumber; + frame->bytecodeIndex = bytecodeOffset; + frame->frameType = Interpreted; /* TODO need a way to know if its JIT'ed and inlined */ + } + return J9_STACKWALK_KEEP_ITERATING; + } + + void mergeStringTables() { + _buildResult = OK; + + _globalStringTable = (void**)j9mem_allocate_memory(sizeof(void*) * (_stringUTF8Count + _packageCount), J9MEM_CATEGORY_CLASSES); + if (NULL == _globalStringTable) { + _buildResult = OutOfMemory; + goto done; + } + _globalStringTable[0] = &_defaultStringUTF8Entry; + _globalStringTable[_stringUTF8Count] = &_defaultPackageEntry; + + hashTableForEachDo(_stringUTF8Table, &mergeStringUTF8EntriesToGlobalTable, this); + hashTableForEachDo(_packageTable, &mergePackageEntriesToGlobalTable, this); + + if (_debug) { + printMergedStringTables(); + } +done: + return; + } + +protected: + +public: + + U_32 addExecutionSampleEntry(J9JFRExecutionSample *executionSampleData); + + U_32 addThreadStartEntry(J9JFRThreadStart *threadStartData); + + U_32 addThreadEndEntry(J9JFREvent *threadEndData); + + U_32 addThreadSleepEntry(J9JFRThreadSleep *threadSleepData); + + J9Pool *getExecutionSampleTable() + { + return _executionSampleTable; + } + + J9Pool *getThreadStartTable() + { + return _threadStartTable; + } + + J9Pool *getThreadEndTable() + { + return _threadEndTable; + } + + J9Pool *getThreadSleepTable() + { + return _threadSleepTable; + } + + UDATA getExecutionSampleCount() + { + return _executionSampleCount; + } + + UDATA getThreadStartCount() + { + return _threadStartCount; + } + + UDATA getThreadEndCount() + { + return _threadEndCount; + } + + UDATA getThreadSleepCount() + { + return _threadSleepCount; + } + + ClassloaderEntry *getClassloaderEntry() + { + return _firstClassloaderEntry; + } + + UDATA getClassloaderCount() + { + return _classLoaderCount; + } + + ClassEntry *getClassEntry() + { + return _firstClassEntry; + } + + UDATA getClassCount() + { + return _classCount; + } + + ModuleEntry *getModuleEntry() + { + return _firstModuleEntry; + } + + UDATA getModuleCount() + { + return _moduleCount; + } + + MethodEntry *getMethodEntry() + { + return _firstMethodEntry; + } + + UDATA getMethodCount() + { + return _methodCount; + } + + void *getSymbolTableEntry(UDATA index) + { + return _globalStringTable[index]; + } + + UDATA getSymbolTableCount() + { + return _stringUTF8Count + _packageCount; + } + + UDATA getStringUTF8Count() + { + return _stringUTF8Count; + } + + PackageEntry *getPackageEntry() + { + return _firstPackageEntry; + } + + UDATA getPackageCount() + { + return _packageCount; + } + + UDATA getRequiredBufferSize() + { + return _requiredBufferSize; + } + + UDATA getThreadGroupCount() + { + return _threadGroupCount; + } + + ThreadGroupEntry *getThreadGroupEntry() + { + return _firstThreadGroupEntry; + } + + UDATA getThreadCount() + { + return _threadCount; + } + + ThreadEntry *getThreadEntry() + { + return _firstThreadEntry; + } + + UDATA getStackTraceCount() + { + return _stackTraceCount; + } + + StackTraceEntry *getStackTraceEntry() + { + return _firstStackTraceEntry; + } + + UDATA getStackFrameCount() + { + return _stackFrameCount; + } + + void printTables(); + + BuildResult getBuildResult() { return _buildResult; }; + + void loadEvents() + { + J9JFRBufferWalkState walkstate = {0}; + J9JFREvent *event = jfrBufferStartDo(&_vm->jfrBuffer, &walkstate); + + while (NULL != event) { + switch (event->eventType) { + case J9JFR_EVENT_TYPE_EXECUTION_SAMPLE: + addExecutionSampleEntry((J9JFRExecutionSample*) event); + break; + case J9JFR_EVENT_TYPE_THREAD_START: + addThreadStartEntry((J9JFRThreadStart*) event); + break; + case J9JFR_EVENT_TYPE_THREAD_END: + addThreadEndEntry((J9JFREvent*) event); + break; + case J9JFR_EVENT_TYPE_THREAD_SLEEP: + addThreadSleepEntry((J9JFRThreadSleep*) event); + break; + default: + Assert_VM_unreachable(); + break; + } + event = jfrBufferNextDo(&walkstate); + } + + + hashTableForEachDo(_classTable, &fixupShallowEntries, this); + mergeStringTables(); + } + + U_32 consumeStackTrace(J9VMThread *walkThread, UDATA *walkStateCache, UDATA numberOfFrames) { + U_32 index = U_32_MAX; + _currentStackFrameBuffer = (StackFrame*) j9mem_allocate_memory(sizeof(StackFrame) * numberOfFrames, J9MEM_CATEGORY_CLASSES); + _currentFrameCount = 0; + if (NULL == _currentStackFrameBuffer) { + _buildResult = OutOfMemory; + goto done; + } + + iterateStackTraceImpl(_currentThread, (j9object_t*)walkStateCache, &stackTraceCallback, this, FALSE, FALSE, numberOfFrames, FALSE); + + index = addStackTraceEntry(walkThread, VM_JFRUtils::getCurrentTimeNanos(privatePortLibrary, _buildResult), _currentFrameCount); + _stackFrameCount += numberOfFrames; + +done: + return index; + } + + VM_JFRConstantPoolTypes(J9VMThread *currentThread) + : _currentThread(currentThread) + , _vm(currentThread->javaVM) + , _buildResult(OK) + , _debug(false) + , privatePortLibrary(_vm->portLibrary) + , _classTable(NULL) + , _packageTable(NULL) + , _moduleTable(NULL) + , _classLoaderTable(NULL) + , _methodTable(NULL) + , _stackTraceTable(NULL) + , _stringUTF8Table(NULL) + , _threadTable(NULL) + , _threadGroupTable(NULL) + , _classCount(0) + , _packageCount(0) + , _moduleCount(0) + , _classLoaderCount(0) + , _methodCount(0) + , _stackTraceCount(0) + , _stackFrameCount(0) + , _stringUTF8Count(0) + , _threadCount(0) + , _threadGroupCount(0) + , _executionSampleTable(NULL) + , _executionSampleCount(0) + , _threadStartTable(NULL) + , _threadStartCount(0) + , _threadEndTable(NULL) + , _threadEndCount(0) + , _threadSleepTable(NULL) + , _threadSleepCount(0) + , _previousStackTraceEntry(NULL) + , _firstStackTraceEntry(NULL) + , _previousThreadEntry(NULL) + , _firstThreadEntry(NULL) + , _previousThreadGroupEntry(NULL) + , _firstThreadGroupEntry(NULL) + , _previousModuleEntry(NULL) + , _firstModuleEntry(NULL) + , _previousMethodEntry(NULL) + , _firstMethodEntry(NULL) + , _previousClassEntry(NULL) + , _firstClassEntry(NULL) + , _previousClassloaderEntry(NULL) + , _firstClassloaderEntry(NULL) + , _previousPackageEntry(NULL) + , _firstPackageEntry(NULL) + , _requiredBufferSize(0) + { + _classTable = hashTableNew(OMRPORT_FROM_J9PORT(privatePortLibrary), J9_GET_CALLSITE(), 0, sizeof(ClassEntry), sizeof(ClassEntry *), J9HASH_TABLE_ALLOW_SIZE_OPTIMIZATION, J9MEM_CATEGORY_CLASSES, jfrClassHashFn, jfrClassHashEqualFn, NULL, _vm); + if (NULL == _classTable) { + _buildResult = OutOfMemory; + goto done; + } + + _packageTable = hashTableNew(OMRPORT_FROM_J9PORT(privatePortLibrary), J9_GET_CALLSITE(), 0, sizeof(PackageEntry), sizeof(PackageEntry *), J9HASH_TABLE_ALLOW_SIZE_OPTIMIZATION, J9MEM_CATEGORY_CLASSES, jfrPackageHashFn, jfrPackageHashEqualFn, NULL, _vm); + if (NULL == _packageTable) { + _buildResult = OutOfMemory; + goto done; + } + + _classLoaderTable = hashTableNew(OMRPORT_FROM_J9PORT(privatePortLibrary), J9_GET_CALLSITE(), 0, sizeof(ClassloaderEntry), sizeof(J9ClassLoader*), J9HASH_TABLE_ALLOW_SIZE_OPTIMIZATION, J9MEM_CATEGORY_CLASSES, classloaderNameHashFn, classloaderNameHashEqualFn, NULL, _vm); + if (NULL == _classLoaderTable) { + _buildResult = OutOfMemory; + goto done; + } + + _methodTable = hashTableNew(OMRPORT_FROM_J9PORT(privatePortLibrary), J9_GET_CALLSITE(), 0, sizeof(MethodEntry), sizeof(J9ROMMethod*), J9HASH_TABLE_ALLOW_SIZE_OPTIMIZATION, J9MEM_CATEGORY_CLASSES, methodNameHashFn, methodNameHashEqualFn, NULL, _vm); + if (NULL == _methodTable) { + _buildResult = OutOfMemory; + goto done; + } + + _stringUTF8Table = hashTableNew(OMRPORT_FROM_J9PORT(privatePortLibrary), J9_GET_CALLSITE(), 0, sizeof(StringUTF8Entry), sizeof(StringUTF8Entry*), J9HASH_TABLE_ALLOW_SIZE_OPTIMIZATION, J9MEM_CATEGORY_CLASSES, jfrStringUTF8HashFn, jfrStringUTF8HashEqualFn, NULL, _vm); + if (NULL == _stringUTF8Table) { + _buildResult = OutOfMemory; + goto done; + } + + _moduleTable = hashTableNew(OMRPORT_FROM_J9PORT(privatePortLibrary), J9_GET_CALLSITE(), 0, sizeof(ModuleEntry), sizeof(ModuleEntry*), J9HASH_TABLE_ALLOW_SIZE_OPTIMIZATION, J9MEM_CATEGORY_CLASSES, jfrModuleHashFn, jfrModuleHashEqualFn, NULL, _vm); + if (NULL == _moduleTable) { + _buildResult = OutOfMemory; + goto done; + } + + _threadTable = hashTableNew(OMRPORT_FROM_J9PORT(privatePortLibrary), J9_GET_CALLSITE(), 0, sizeof(ThreadEntry), sizeof(U_64), J9HASH_TABLE_ALLOW_SIZE_OPTIMIZATION, J9MEM_CATEGORY_CLASSES, threadHashFn, threadHashEqualFn, NULL, _currentThread); + if (NULL == _threadTable) { + _buildResult = OutOfMemory; + goto done; + } + + _stackTraceTable = hashTableNew(OMRPORT_FROM_J9PORT(privatePortLibrary), J9_GET_CALLSITE(), 0, sizeof(StackTraceEntry), sizeof(U_64), J9HASH_TABLE_ALLOW_SIZE_OPTIMIZATION, J9MEM_CATEGORY_CLASSES, stackTraceHashFn, stackTraceHashEqualFn, NULL, _vm); + if (NULL == _stackTraceTable) { + _buildResult = OutOfMemory; + goto done; + } + + _threadGroupTable = hashTableNew(OMRPORT_FROM_J9PORT(privatePortLibrary), J9_GET_CALLSITE(), 0, sizeof(ThreadGroupEntry), sizeof(U_64), J9HASH_TABLE_ALLOW_SIZE_OPTIMIZATION, J9MEM_CATEGORY_CLASSES, threadGroupHashFn, threadGroupHashEqualFn, NULL, _vm); + if (NULL == _threadGroupTable) { + _buildResult = OutOfMemory; + goto done; + } + + _executionSampleTable = pool_new(sizeof(ExecutionSampleEntry), 0, sizeof(U_64), 0, J9_GET_CALLSITE(), OMRMEM_CATEGORY_VM, POOL_FOR_PORT(privatePortLibrary)); + if (NULL == _executionSampleTable) { + _buildResult = OutOfMemory; + goto done; + } + + _threadStartTable = pool_new(sizeof(ThreadStartEntry), 0, sizeof(U_64), 0, J9_GET_CALLSITE(), OMRMEM_CATEGORY_VM, POOL_FOR_PORT(privatePortLibrary)); + if (NULL == _threadStartTable) { + _buildResult = OutOfMemory; + goto done; + } + + _threadEndTable = pool_new(sizeof(ThreadEndEntry), 0, sizeof(U_64), 0, J9_GET_CALLSITE(), OMRMEM_CATEGORY_VM, POOL_FOR_PORT(privatePortLibrary)); + if (NULL == _threadEndTable) { + _buildResult = OutOfMemory; + goto done; + } + + _threadSleepTable = pool_new(sizeof(ThreadSleepEntry), 0, sizeof(U_64), 0, J9_GET_CALLSITE(), OMRMEM_CATEGORY_VM, POOL_FOR_PORT(privatePortLibrary)); + if (NULL == _threadEndTable) { + _buildResult = OutOfMemory; + goto done; + } + + /* Add reserved index for default entries. For strings zero is the empty or NUll string. + * For package zero is the deafult package, for Module zero is the unnamed module. ThreadGroup + * zero is NULL threadGroup. + */ + _stringUTF8Count++; + _defaultStringUTF8Entry = {0}; + _defaultStringUTF8Entry.string = (J9UTF8*)&nullString; + + _moduleCount++; + _defaultModuleEntry = {0}; + _firstModuleEntry = &_defaultModuleEntry; + _previousModuleEntry = _firstModuleEntry; + + _packageCount++; + _defaultPackageEntry = {0}; + _defaultPackageEntry.exported = TRUE; + _defaultPackageEntry.packageName = J9UTF8_DATA((J9UTF8*) &defaultPackage); + _defaultPackageEntry.packageNameLength = J9UTF8_LENGTH((J9UTF8*) &defaultPackage); + _firstPackageEntry = &_defaultPackageEntry; + _previousPackageEntry = _firstPackageEntry; + + _threadGroupCount++; + _defaultThreadGroup = {0}; + _firstThreadGroupEntry = &_defaultThreadGroup; + _previousThreadGroupEntry = _firstThreadGroupEntry; + + +done: + return; + } + + ~VM_JFRConstantPoolTypes() + { + hashTableForEachDo(_stringUTF8Table, &freeUTF8Strings, _currentThread); + hashTableFree(_classTable); + hashTableFree(_packageTable); + hashTableFree(_moduleTable); + hashTableFree(_classLoaderTable); + hashTableFree(_methodTable); + hashTableFree(_stackTraceTable); + hashTableFree(_threadTable); + hashTableFree(_threadGroupTable); + hashTableFree(_stringUTF8Table); + pool_kill(_executionSampleTable); + pool_kill(_threadStartTable); + pool_kill(_threadEndTable); + pool_kill(_threadSleepTable); + j9mem_free_memory(_globalStringTable); + } + +}; +#endif /* defined(J9VM_OPT_JFR) */ +#endif /* JFRCONSTANTPOOLTYPES_HPP_ */ diff --git a/runtime/vm/JFRUtils.hpp b/runtime/vm/JFRUtils.hpp new file mode 100644 index 00000000000..3b852aa2056 --- /dev/null +++ b/runtime/vm/JFRUtils.hpp @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright IBM Corp. and others 2024 + * + * This program and the accompanying materials are made available under + * the terms of the Eclipse Public License 2.0 which accompanies this + * distribution and is available at https://www.eclipse.org/legal/epl-2.0/ + * or the Apache License, Version 2.0 which accompanies this distribution and + * is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * This Source Code may also be made available under the following + * Secondary Licenses when the conditions for such availability set + * forth in the Eclipse Public License, v. 2.0 are satisfied: GNU + * General Public License, version 2 with the GNU Classpath + * Exception [1] and GNU General Public License, version 2 with the + * OpenJDK Assembly Exception [2]. + * + * [1] https://www.gnu.org/software/classpath/license.html + * [2] https://openjdk.org/legal/assembly-exception.html + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0 + *******************************************************************************/ + +#if !defined(JFRUTILS_HPP_) +#define JFRUTILS_HPP_ + +#include "j9.h" +#include "vm_api.h" + +#if defined(J9VM_OPT_JFR) + +enum BuildResult { + OK, + OutOfMemory, + InternalVMError, + FileIOError, + EnvVarsNotSet, + TimeFailure, + MetaDataFileNotLoaded, +}; + +class VM_JFRUtils { + /* + * Data members + */ +private: + +protected: + +public: + + /* + * Function members + */ +private: + +protected: + +public: + + static U_64 getCurrentTimeNanos(J9PortLibrary *privatePortLibrary, BuildResult &buildResult) { + UDATA success = 0; + U_64 result = (U_64) j9time_current_time_nanos(&success); + + if (success == 0) { + buildResult = TimeFailure; + + } + + return result; + } + +}; + +#endif /* defined(J9VM_OPT_JFR) */ + +#endif /* JFRCHUNKWRITER_HPP_ */ diff --git a/runtime/vm/JFRWriter.hpp b/runtime/vm/JFRWriter.hpp index cbfc9114c4c..4aa455ca893 100644 --- a/runtime/vm/JFRWriter.hpp +++ b/runtime/vm/JFRWriter.hpp @@ -31,6 +31,10 @@ #if defined(J9VM_OPT_JFR) +#include "JFRChunkWriter.hpp" + +#undef DEBUG + extern "C" { class VM_JFRWriter { /* @@ -158,8 +162,33 @@ class VM_JFRWriter { flushJFRDataToFile(J9VMThread *currentThread, bool finalWrite) { bool result = true; + VM_JFRChunkWriter chunkWriter(currentThread, finalWrite); + if (!chunkWriter.isOkay()) { + result = false; + goto fail; + } + + chunkWriter.loadEvents(); + if (!chunkWriter.isOkay()) { + result = false; + goto fail; + } + + chunkWriter.writeJFRChunk(); + if (!chunkWriter.isOkay()) { + result = false; + goto fail; + } + +done: return result; + +fail: +#if defined(DEBUG) + j9tty_printf(PORTLIB, "Failed to write chunk to file error code=%d\n", (int) chunkWriter.buildResult()); +#endif /* defined(DEBUG) */ + goto done; } }; diff --git a/runtime/vm/exceptiondescribe.c b/runtime/vm/exceptiondescribe.c index b0d982a925a..4b6fbd94474 100644 --- a/runtime/vm/exceptiondescribe.c +++ b/runtime/vm/exceptiondescribe.c @@ -43,16 +43,16 @@ static void printExceptionMessage (J9VMThread* vmThread, j9object_t exception); /* assumes VM access */ -static void -printExceptionInThread(J9VMThread* vmThread) +static void +printExceptionInThread(J9VMThread* vmThread) { char* name; const char* format; PORT_ACCESS_FROM_VMC(vmThread); - + format = j9nls_lookup_message( - J9NLS_INFO | J9NLS_DO_NOT_PRINT_MESSAGE_TAG | J9NLS_DO_NOT_APPEND_NEWLINE, + J9NLS_INFO | J9NLS_DO_NOT_PRINT_MESSAGE_TAG | J9NLS_DO_NOT_APPEND_NEWLINE, J9NLS_VM_STACK_TRACE_EXCEPTION_IN, "Exception in thread \"%s\""); @@ -60,7 +60,7 @@ printExceptionInThread(J9VMThread* vmThread) j9tty_err_printf(PORTLIB, format, name); j9tty_err_printf(PORTLIB, " "); - + releaseOMRVMThreadName(vmThread->omrVMThread); } @@ -90,7 +90,7 @@ printExceptionMessage(J9VMThread* vmThread, j9object_t exception) { (UDATA)J9UTF8_LENGTH(exceptionClassName), J9UTF8_DATA(exceptionClassName), separator, - length, + length, buf); if (buf != stackBuffer) { @@ -220,7 +220,7 @@ printStackTraceEntry(J9VMThread * vmThread, void * voidUserData, UDATA bytecodeO } -/* +/* * Walks the backtrace of an exception instance, invoking a user-supplied callback function for * each frame on the call stack. * @@ -229,40 +229,54 @@ printStackTraceEntry(J9VMThread * vmThread, void * voidUserData, UDATA bytecodeO * @param callback The callback function to be invoked for each stack frame. * @param userData Opaque data pointer passed to the callback function. * @param pruneConstructors Non-zero if constructors should be pruned from the stack trace. + * @param sizeOfWalkstateCache Non-zero if exception is walkstate cache instead of exception object. + * Indicatest the size of cache. * @return The number of times the callback function was invoked. * * @note Assumes VM access **/ UDATA -iterateStackTrace(J9VMThread * vmThread, j9object_t* exception, callback_func_t callback, void * userData, UDATA pruneConstructors, UDATA skipHiddenFrames) +iterateStackTraceImpl(J9VMThread * vmThread, j9object_t* exception, callback_func_t callback, void * userData, UDATA pruneConstructors, UDATA skipHiddenFrames, UDATA sizeOfWalkstateCache, BOOLEAN exceptionIsJavaObject) { J9JavaVM * vm = vmThread->javaVM; UDATA totalEntries = 0; - j9object_t walkback = J9VMJAVALANGTHROWABLE_WALKBACK(vmThread, (*exception)); + void *walkback = NULL; + + if (exceptionIsJavaObject) { + walkback = J9VMJAVALANGTHROWABLE_WALKBACK(vmThread, (*exception)); + } else { + walkback = exception; + } /* Note that exceptionAddr might be a pointer into the current thread's stack, so no java code is allowed to run (nothing which could cause the stack to grow). */ if (walkback) { - U_32 arraySize = J9INDEXABLEOBJECT_SIZE(vmThread, walkback); + U_32 arraySize = 0; + U_32 currentElement = 0; UDATA callbackResult = TRUE; #ifndef J9VM_INTERP_NATIVE_SUPPORT pruneConstructors = FALSE; #endif + if (exceptionIsJavaObject) { + arraySize = J9INDEXABLEOBJECT_SIZE(vmThread, walkback); - /* A zero terminates the stack trace - search backwards through the array to determine the correct size */ + /* A zero terminates the stack trace - search backwards through the array to determine the correct size */ - while ((arraySize != 0) && (J9JAVAARRAYOFUDATA_LOAD(vmThread, walkback, arraySize-1)) == 0) { - --arraySize; + while ((arraySize != 0) && (J9JAVAARRAYOFUDATA_LOAD(vmThread, walkback, arraySize-1)) == 0) { + --arraySize; + } + } else { + arraySize = (U_32)sizeOfWalkstateCache; } /* Loop over the stack trace */ while (currentElement != arraySize) { - /* write as for or move currentElement++ to very end */ - UDATA methodPC = J9JAVAARRAYOFUDATA_LOAD(vmThread, J9VMJAVALANGTHROWABLE_WALKBACK(vmThread, (*exception)), currentElement); + /* write as for or move currentElement++ to very end */ + UDATA methodPC = 0; J9ROMMethod * romMethod = NULL; J9ROMClass *romClass = NULL; UDATA lineNumber = 0; @@ -276,6 +290,12 @@ iterateStackTrace(J9VMThread * vmThread, j9object_t* exception, callback_func_t void * inlineMap = NULL; J9JITConfig * jitConfig = vm->jitConfig; + if (exceptionIsJavaObject) { + methodPC = J9JAVAARRAYOFUDATA_LOAD(vmThread, J9VMJAVALANGTHROWABLE_WALKBACK(vmThread, (*exception)), currentElement); + } else { + methodPC = ((UDATA *)exception)[currentElement]; + } + if (jitConfig) { metaData = jitConfig->jitGetExceptionTableFromPC(vmThread, methodPC); if (metaData) { @@ -335,7 +355,7 @@ iterateStackTrace(J9VMThread * vmThread, j9object_t* exception, callback_func_t J9UTF8 const *utfClassName = J9ROMCLASS_CLASSNAME(romClass); ramClass = peekClassHashTable(vmThread, classLoader, J9UTF8_DATA(utfClassName), J9UTF8_LENGTH(utfClassName)); - if (j9shr_Query_IsAddressInCache(vm, romClass, romClass->romSize)) { + if (j9shr_Query_IsAddressInCache(vm, romClass, romClass->romSize)) { if (ramClass == NULL) { /* Probe the application loader to determine if it has the J9Class for the current class. * This secondary probe is required as all ROMClasses from the SCC appear to be owned @@ -444,16 +464,35 @@ foundROMMethod: ; return totalEntries; } +/* + * Walks the backtrace of an exception instance, invoking a user-supplied callback function for + * each frame on the call stack. + * + * @param vmThread + * @param exception The exception object that contains the backtrace. + * @param callback The callback function to be invoked for each stack frame. + * @param userData Opaque data pointer passed to the callback function. + * @param pruneConstructors Non-zero if constructors should be pruned from the stack trace. + * @return The number of times the callback function was invoked. + * + * @note Assumes VM access + **/ +UDATA +iterateStackTrace(J9VMThread * vmThread, j9object_t* exception, callback_func_t callback, void * userData, UDATA pruneConstructors, UDATA skipHiddenFrames) +{ + return iterateStackTraceImpl(vmThread, exception, callback, userData, pruneConstructors, skipHiddenFrames, 0, TRUE); +} + /** * This is an helper function to call exceptionDescribe indirectly from gpProtectAndRun function. - * + * * @param entryArg Current VM Thread (JNIEnv * env) */ static UDATA gpProtectedExceptionDescribe(void *entryArg) { JNIEnv * env = (JNIEnv *)entryArg; - + exceptionDescribe(env); return 0; /* return value required to conform to port library definition */ @@ -461,13 +500,13 @@ gpProtectedExceptionDescribe(void *entryArg) /** * Assumes VM access - * - * Builds the exception + * + * Builds the exception * * @param env J9VMThread * * */ -void +void internalExceptionDescribe(J9VMThread * vmThread) { /* If the exception is NULL, do nothing. Do not fetch the exception value into a local here as we do not have VM access yet. */ @@ -536,17 +575,17 @@ internalExceptionDescribe(J9VMThread * vmThread) break; } } while (exception != NULL); - + done: ; } } /** * This function makes sure that call to "internalExceptionDescribe" is gpProtected - * + * * @param env Current VM thread (J9VMThread *) */ -void JNICALL +void JNICALL exceptionDescribe(JNIEnv * env) { J9VMThread * vmThread = (J9VMThread *) env; diff --git a/runtime/vm/jfr.cpp b/runtime/vm/jfr.cpp index 3092e41e2c7..2a846c4486c 100644 --- a/runtime/vm/jfr.cpp +++ b/runtime/vm/jfr.cpp @@ -19,16 +19,15 @@ * * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0 *******************************************************************************/ - #include "j9protos.h" #include "omrlinkedlist.h" #include "ut_j9vm.h" #include "vm_internal.h" -#include "JFRWriter.hpp" - #if defined(J9VM_OPT_JFR) +#include "JFRWriter.hpp" + extern "C" { #undef DEBUG @@ -42,7 +41,7 @@ static void tearDownJFR(J9JavaVM *vm); static bool flushBufferToGlobal(J9VMThread *currentThread, J9VMThread *flushThread); static bool flushAllThreadBuffers(J9VMThread *currentThread, bool freeBuffers, bool threadEnd); static U_8* reserveBuffer(J9VMThread *currentThread, UDATA size); -static J9JFREvent* reserveBufferWithStackTrace(J9VMThread *currentThread, UDATA eventType, UDATA eventFixedSize); +static J9JFREvent* reserveBufferWithStackTrace(J9VMThread *currentThread, J9VMThread *sampleThread, UDATA eventType, UDATA eventFixedSize); static void jfrThreadCreated(J9HookInterface **hook, UDATA eventNum, void *eventData, void *userData); static void jfrThreadDestroy(J9HookInterface **hook, UDATA eventNum, void *eventData, void *userData); static void jfrClassesUnload(J9HookInterface **hook, UDATA eventNum, void *eventData, void *userData); @@ -263,11 +262,11 @@ reserveBuffer(J9VMThread *currentThread, UDATA size) * @returns pointer to the start of the reserved space or NULL if the space could not be reserved */ static J9JFREvent* -reserveBufferWithStackTrace(J9VMThread *currentThread, UDATA eventType, UDATA eventFixedSize) +reserveBufferWithStackTrace(J9VMThread *currentThread, J9VMThread *sampleThread, UDATA eventType, UDATA eventFixedSize) { J9JFREvent *jfrEvent = NULL; J9StackWalkState *walkState = currentThread->stackWalkState; - walkState->walkThread = currentThread; + walkState->walkThread = sampleThread; walkState->flags = J9_STACKWALK_CACHE_PCS | J9_STACKWALK_WALK_TRANSLATE_PC | J9_STACKWALK_VISIBLE_ONLY | J9_STACKWALK_INCLUDE_NATIVES | J9_STACKWALK_SKIP_INLINES; walkState->skipCount = 0; @@ -276,9 +275,9 @@ reserveBufferWithStackTrace(J9VMThread *currentThread, UDATA eventType, UDATA ev UDATA framesWalked = walkState->framesWalked; UDATA stackTraceBytes = framesWalked * sizeof(UDATA); UDATA eventSize = eventFixedSize + stackTraceBytes; - jfrEvent = (J9JFREvent*)reserveBuffer(currentThread, eventSize); + jfrEvent = (J9JFREvent*)reserveBuffer(sampleThread, eventSize); if (NULL != jfrEvent) { - initializeEventFields(currentThread, jfrEvent, eventType); + initializeEventFields(sampleThread, jfrEvent, eventType); ((J9JFREventWithStackTrace*)jfrEvent)->stackTraceSize = framesWalked; memcpy(((U_8*)jfrEvent) + eventFixedSize, walkState->cache, stackTraceBytes); } @@ -439,7 +438,7 @@ jfrThreadStarting(J9HookInterface **hook, UDATA eventNum, void *eventData, void j9tty_printf(PORTLIB, "\n!!! thread starting %p %p\n", currentThread, startedThread); #endif /* defined(DEBUG) */ - J9JFRThreadStart *jfrEvent = (J9JFRThreadStart*)reserveBufferWithStackTrace(currentThread, J9JFR_EVENT_TYPE_THREAD_START, sizeof(*jfrEvent)); + J9JFRThreadStart *jfrEvent = (J9JFRThreadStart*)reserveBufferWithStackTrace(currentThread, currentThread, J9JFR_EVENT_TYPE_THREAD_START, sizeof(*jfrEvent)); if (NULL != jfrEvent) { jfrEvent->thread = startedThread; jfrEvent->parentThread = currentThread; @@ -490,7 +489,7 @@ jfrVMSleep(J9HookInterface **hook, UDATA eventNum, void *eventData, void *userDa j9tty_printf(PORTLIB, "\n!!! thread sleep %p\n", currentThread); #endif /* defined(DEBUG) */ - J9JFRThreadSleep *jfrEvent = (J9JFRThreadSleep*)reserveBufferWithStackTrace(currentThread, J9JFR_EVENT_TYPE_THREAD_SLEEP, sizeof(*jfrEvent)); + J9JFRThreadSleep *jfrEvent = (J9JFRThreadSleep*)reserveBufferWithStackTrace(currentThread, currentThread, J9JFR_EVENT_TYPE_THREAD_SLEEP, sizeof(*jfrEvent)); if (NULL != jfrEvent) { // TODO: worry about overflow? jfrEvent->time = (event->millis * 1000) + event->nanos; @@ -604,9 +603,9 @@ initializeEventFields(J9VMThread *currentThread, J9JFREvent *event, UDATA eventT } void -jfrExecutionSample(J9VMThread *currentThread) +jfrExecutionSample(J9VMThread *currentThread, J9VMThread *sampleThread) { - J9JFRExecutionSample *jfrEvent = (J9JFRExecutionSample*)reserveBufferWithStackTrace(currentThread, J9JFR_EVENT_TYPE_EXECUTION_SAMPLE, sizeof(*jfrEvent)); + J9JFRExecutionSample *jfrEvent = (J9JFRExecutionSample*)reserveBufferWithStackTrace(currentThread, sampleThread, J9JFR_EVENT_TYPE_EXECUTION_SAMPLE, sizeof(*jfrEvent)); if (NULL != jfrEvent) { jfrEvent->threadState = J9JFR_THREAD_STATE_RUNNING; } diff --git a/runtime/vm/vm_internal.h b/runtime/vm/vm_internal.h index eb2794ca3aa..c4ffcafa405 100644 --- a/runtime/vm/vm_internal.h +++ b/runtime/vm/vm_internal.h @@ -389,7 +389,7 @@ fixBadUtf8(const U_8 * original, U_8 *corrected, size_t length); * * @returns the array of interfaces, or NULL on failure (in which case an exception will be pending) */ -j9object_t +j9object_t getInterfacesHelper(J9VMThread *currentThread, j9object_t clazz); /** @@ -614,9 +614,10 @@ initializeJFR(J9JavaVM *vm); * Take an execution sample of the current thread. * * @param currentThread[in] the current J9VMThread + * @param currentThread[in] the thread being walked */ void -jfrExecutionSample(J9VMThread *currentThread); +jfrExecutionSample(J9VMThread *currentThread, J9VMThread *sampleThread); /** * Begin event iteration in a JFR buffer.