Path: blob/master/runtime/jcl/common/jclexception.cpp
6000 views
/*******************************************************************************1* Copyright (c) 1998, 2021 IBM Corp. and others2*3* This program and the accompanying materials are made available under4* the terms of the Eclipse Public License 2.0 which accompanies this5* distribution and is available at https://www.eclipse.org/legal/epl-2.0/6* or the Apache License, Version 2.0 which accompanies this distribution and7* is available at https://www.apache.org/licenses/LICENSE-2.0.8*9* This Source Code may also be made available under the following10* Secondary Licenses when the conditions for such availability set11* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU12* General Public License, version 2 with the GNU Classpath13* Exception [1] and GNU General Public License, version 2 with the14* OpenJDK Assembly Exception [2].15*16* [1] https://www.gnu.org/software/classpath/license.html17* [2] http://openjdk.java.net/legal/assembly-exception.html18*19* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception20*******************************************************************************/2122#include "j2sever.h"23#include "j9.h"24#include "j9cp.h"25#include "jclexception.h"26#include "jvminit.h"27#include "objhelp.h"28#include "omrgcconsts.h"29#include "rommeth.h"30#include "vm_api.h"31#include "ut_j9jcl.h"3233#include "VMHelpers.hpp"3435extern "C" {3637#if JAVA_SPEC_VERSION >= 1138static void setStackTraceElementFields(J9VMThread *vmThread, j9object_t element, J9ClassLoader *classLoader);39#endif /* JAVA_SPEC_VERSION >= 11 */4041static UDATA getStackTraceIterator(J9VMThread * vmThread, void * voidUserData, UDATA bytecodeOffset, J9ROMClass * romClass, J9ROMMethod * romMethod, J9UTF8 * fileName, UDATA lineNumber, J9ClassLoader* classLoader, J9Class* ramClass);4243/**44* Saves enough context into the StackTraceElement to allow printing later. For45* bootstrap classes we store a java/lang/String, for application classes a46* ProtectionDomain.47* @param vmThread48* @param stackTraceElement The StackTraceElement to update.49* @param classLoader The loader in which the ROM class is defined.50* @param romClass The ROM class whose path is to be determined.51* @return TRUE if the element was updated, FALSE otherwise.52* @note Assumes VM access53*/54static BOOLEAN55setStackTraceElementSource(J9VMThread* vmThread, j9object_t stackTraceElement, J9ClassLoader* classLoader, J9ROMClass* romClass)56{57J9InternalVMFunctions * vmFuncs = vmThread->javaVM->internalVMFunctions;58J9UTF8* name = J9ROMCLASS_CLASSNAME(romClass);59j9object_t element = stackTraceElement;60j9object_t heapClass, protectionDomain;61j9object_t string = NULL;62U_8 *path = NULL;63UDATA pathLen = 0;6465J9Class* clazz = vmFuncs->internalFindClassUTF8(vmThread, J9UTF8_DATA(name), J9UTF8_LENGTH(name), classLoader, J9_FINDCLASS_FLAG_EXISTING_ONLY);66if (NULL == clazz) {67return FALSE;68}6970/* For bootstrap loaders we can consult the classpath entries */71path = getClassLocation(vmThread, clazz, &pathLen);72if (NULL != path) {73PUSH_OBJECT_IN_SPECIAL_FRAME(vmThread, element);74string = vmThread->javaVM->memoryManagerFunctions->j9gc_createJavaLangString(vmThread, path, pathLen, 0);75element = POP_OBJECT_IN_SPECIAL_FRAME(vmThread);76if (NULL == string) {77/* exception is pending from the call */78return FALSE;79}80J9VMJAVALANGSTACKTRACEELEMENT_SET_SOURCE(vmThread, element, string);81return TRUE;82}8384/* For application loaders we must consult the protection domain */85heapClass = J9VM_J9CLASS_TO_HEAPCLASS(clazz);86protectionDomain = J9VMJAVALANGCLASS_PROTECTIONDOMAIN(vmThread, heapClass);87J9VMJAVALANGSTACKTRACEELEMENT_SET_SOURCE(vmThread, element, protectionDomain);88return TRUE;89}909192static UDATA93getStackTraceIterator(J9VMThread * vmThread, void * voidUserData, UDATA bytecodeOffset, J9ROMClass * romClass, J9ROMMethod * romMethod, J9UTF8 * fileName, UDATA lineNumber, J9ClassLoader* classLoader, J9Class* ramClass)94{95J9GetStackTraceUserData *userData = (J9GetStackTraceUserData*)voidUserData;96J9JavaVM * vm = vmThread->javaVM;97J9InternalVMFunctions const * vmFuncs = vm->internalVMFunctions;98J9MemoryManagerFunctions const * mmfns = vm->memoryManagerFunctions;99j9object_t element = NULL;100UDATA rc = TRUE;101const I_32 currentIndex = (I_32)userData->index;102103/* If the stack trace is larger than the array, bail */104105if (userData->index == userData->maxFrames) {106userData->index += 1; /* Indicate error */107return FALSE;108}109110/* Prevent the current class from being unloaded during allocation */111PUSH_OBJECT_IN_SPECIAL_FRAME(vmThread, (NULL == classLoader) ? NULL : classLoader->classLoaderObject);112113/* Create the new StackTraceElement and put it in the array at the correct index */114115element = mmfns->J9AllocateObject(vmThread, userData->elementClass, J9_GC_ALLOCATE_OBJECT_NON_INSTRUMENTABLE);116if (NULL == element) {117rc = FALSE;118vmFuncs->setHeapOutOfMemoryError(vmThread);119} else {120j9array_t result = (j9array_t) PEEK_OBJECT_IN_SPECIAL_FRAME(vmThread, 1);121J9JAVAARRAYOFOBJECT_STORE(vmThread, result, currentIndex, element);122userData->index += 1;123124/* If there is a valid method at this frame, fill in the information for it in the StackTraceElement */125if (NULL != romMethod) {126J9UTF8 const * utfClassName = J9ROMCLASS_CLASSNAME(romClass);127j9object_t string = NULL;128129PUSH_OBJECT_IN_SPECIAL_FRAME(vmThread, element);130131/* Lookup the J9Class for this method if it can be found as it makes132* a number of the remaining operations faster. Code still needs to be133* able to handle the case where the J9Class cannot be found134*/135if (NULL != classLoader) {136if (NULL == ramClass) {137ramClass = vmFuncs->peekClassHashTable(vmThread, classLoader, J9UTF8_DATA(utfClassName), J9UTF8_LENGTH(utfClassName));138}139if (NULL != ramClass) {140/* ramClass can never be an array here as arrays can't define methods so we don't need to141* take them into account in the code below when writing the interned string back to142* the Class object.143*/144Assert_JCL_false(J9CLASS_IS_ARRAY(ramClass));145}146}147148/* Fill in module name and version */149#if JAVA_SPEC_VERSION >= 11150if (NULL != classLoader) {151j9object_t classLoaderName = J9VMJAVALANGCLASSLOADER_CLASSLOADERNAME(vmThread, classLoader->classLoaderObject);152J9VMJAVALANGSTACKTRACEELEMENT_SET_CLASSLOADERNAME(vmThread, element, classLoaderName);153}154if (J9_ARE_NO_BITS_SET(vm->runtimeFlags, J9_RUNTIME_JAVA_BASE_MODULE_CREATED)) {155string = mmfns->j9gc_createJavaLangString(vmThread, (U_8 *)JAVA_BASE_MODULE, LITERAL_STRLEN(JAVA_BASE_MODULE), J9_STR_XLAT | J9_STR_TENURE);156if (NULL == string) {157rc = FALSE;158/* exception is pending from the call */159goto done;160}161element = PEEK_OBJECT_IN_SPECIAL_FRAME(vmThread, 0);162J9VMJAVALANGSTACKTRACEELEMENT_SET_MODULENAME(vmThread, element, string);163164string = mmfns->j9gc_createJavaLangString(vmThread, (U_8 *)JAVA_SPEC_VERSION_STRING, LITERAL_STRLEN(JAVA_SPEC_VERSION_STRING), J9_STR_XLAT | J9_STR_TENURE);165if (NULL == string) {166rc = FALSE;167/* exception is pending from the call */168goto done;169}170element = PEEK_OBJECT_IN_SPECIAL_FRAME(vmThread, 0);171J9VMJAVALANGSTACKTRACEELEMENT_SET_MODULEVERSION(vmThread, element, string);172} else {173/* Fetch the J9Module from the j.l.Class->j.l.Module field if we have a class.174* Otherwise the more painful package-based lookup must be performed175*/176J9Module *module = NULL;177if (NULL != ramClass) {178j9object_t moduleObject = J9VMJAVALANGCLASS_MODULE(vmThread, ramClass->classObject);179module = (J9Module*)J9OBJECT_ADDRESS_LOAD(vmThread, moduleObject, vm->modulePointerOffset);180} else {181UDATA length = packageNameLength(romClass);182omrthread_monitor_enter(vm->classLoaderModuleAndLocationMutex);183module = vmFuncs->findModuleForPackage(vmThread, classLoader, J9UTF8_DATA(utfClassName), (U_32) length);184omrthread_monitor_exit(vm->classLoaderModuleAndLocationMutex);185}186if (NULL != module) {187J9VMJAVALANGSTACKTRACEELEMENT_SET_MODULENAME(vmThread, element, module->moduleName);188J9VMJAVALANGSTACKTRACEELEMENT_SET_MODULEVERSION(vmThread, element, module->version);189}190}191setStackTraceElementFields(vmThread, element, classLoader);192#endif /* JAVA_SPEC_VERSION >= 11 */193194if (NULL != ramClass) {195/* Fill in method class */196string = VM_VMHelpers::getClassNameString(vmThread, J9VM_J9CLASS_TO_HEAPCLASS(ramClass), JNI_TRUE);197if (NULL == string) {198rc = FALSE;199/* exception is pending from the call */200goto done;201}202} else {203/* Can't peek all classes. Ie. anon/hidden classes or cases where classloader is NULL */204UDATA flags = J9_STR_XLAT | J9_STR_INTERN;205206if (J9ROMCLASS_IS_ANON_OR_HIDDEN(romClass)) {207flags |= J9_STR_ANON_CLASS_NAME;208}209string = mmfns->j9gc_createJavaLangString(vmThread, J9UTF8_DATA(utfClassName), J9UTF8_LENGTH(utfClassName), flags);210}211element = PEEK_OBJECT_IN_SPECIAL_FRAME(vmThread, 0);212J9VMJAVALANGSTACKTRACEELEMENT_SET_DECLARINGCLASS(vmThread, element, string);213214/* Fill in method name */215string = mmfns->j9gc_createJavaLangStringWithUTFCache(vmThread, J9ROMMETHOD_NAME(romMethod));216if (NULL == string) {217rc = FALSE;218/* exception is pending from the call */219goto done;220}221element = PEEK_OBJECT_IN_SPECIAL_FRAME(vmThread, 0);222J9VMJAVALANGSTACKTRACEELEMENT_SET_METHODNAME(vmThread, element, string);223224/* Fill in file name, if any.225* Attempt to reuse the cached string if it is available. It may be found226* either in the Class object if it's be previous cached, or it may be found227* in the StackTraceElement[] if the previous filename is the same.228*229* The previous filename cache covers the case where multiple classes were230* defined in the same file.231*232* This avoids additional allocations during stack trace generation233*/234string = NULL;235if (NULL != ramClass) {236string = J9VMJAVALANGCLASS_FILENAMESTRING(vmThread, J9VM_J9CLASS_TO_HEAPCLASS(ramClass));237}238if ((NULL == string) && (NULL != fileName)) {239J9UTF8 *previousFileName = userData->previousFileName;240/* Use an == comparison here as the previousFileName may have been from241* a classloader that was unloaded. We can safely do an == comparison242* as we know the current class is deeper in the stack and can't have243* incorrectly been loaded into the space the previous loader was removed244* from. We can't do a string compare here but the == should be sufficient245* provided the utf8 interning is hitting on common strings.246*/247if (previousFileName == fileName) {248j9array_t result = (j9array_t) PEEK_OBJECT_IN_SPECIAL_FRAME(vmThread, 2);249element = J9JAVAARRAYOFOBJECT_LOAD(vmThread, result, currentIndex - 1);250string = J9VMJAVALANGSTACKTRACEELEMENT_FILENAME(vmThread, element);251} else {252string = mmfns->j9gc_createJavaLangString(vmThread, J9UTF8_DATA(fileName), (U_32) J9UTF8_LENGTH(fileName), J9_STR_TENURE);253if (NULL == string) {254rc = FALSE;255/* exception is pending from the call */256goto done;257}258}259Assert_JCL_notNull(string);260if (NULL != ramClass) {261/* Update the cached fileNameString on the class so subsequent calls will find it */262J9VMJAVALANGCLASS_SET_FILENAMESTRING(vmThread, J9VM_J9CLASS_TO_HEAPCLASS(ramClass), string);263}264}265/* Update previous filename as it must always match the contents of the StackTraceElement[n-1]'s266* value. This means it must be null if the previous filename was null or we'll copy the wrong267* name into the StackTraceElement268*/269userData->previousFileName = fileName;270if (NULL != string) {271element = PEEK_OBJECT_IN_SPECIAL_FRAME(vmThread, 0);272J9VMJAVALANGSTACKTRACEELEMENT_SET_FILENAME(vmThread, element, string);273}274275/* Fill in line number - Java wants -2 for natives, -1 for no line number (which will be 0 coming in from the iterator) */276if (J9_ARE_ANY_BITS_SET(romMethod->modifiers, J9AccNative)) {277lineNumber = -2;278} else if (0 == lineNumber) {279lineNumber = -1;280}281J9VMJAVALANGSTACKTRACEELEMENT_SET_LINENUMBER(vmThread, element, (I_32) lineNumber);282283if (J9_ARE_ANY_BITS_SET(vm->verboseLevel, VERBOSE_STACKTRACE)) {284setStackTraceElementSource(vmThread, element, classLoader, romClass);285}286287done:288DROP_OBJECT_IN_SPECIAL_FRAME(vmThread);289} else {290/* Update previous filename as it must always match the contents of the StackTraceElement[n-1]'s291* value. This means it must be null if the previous filename was null or we'll copy the wrong292* name into the StackTraceElement. As we didn't have a ROMMethod here, nothing to fill in / process293* and so we reset the previousFileName.294*/295userData->previousFileName = NULL;296}297}298DROP_OBJECT_IN_SPECIAL_FRAME(vmThread);299300return rc;301}302303J9IndexableObject *304getStackTrace(J9VMThread * vmThread, j9object_t * exceptionAddr, UDATA pruneConstructors)305{306J9JavaVM * vm = vmThread->javaVM;307J9InternalVMFunctions * vmFuncs = vm->internalVMFunctions;308J9MemoryManagerFunctions * mmfns = vm->memoryManagerFunctions;309UDATA numberOfFrames;310J9Class * elementClass;311J9Class * arrayClass;312J9GetStackTraceUserData userData;313J9IndexableObject * result;314315/* Note that exceptionAddr might be a pointer into the current thread's stack, so no java code is allowed to run316(nothing which could cause the stack to grow).317*/318319retry:320321/* Get the total number of entries in the trace */322323numberOfFrames = vmFuncs->iterateStackTrace(vmThread, exceptionAddr, NULL, NULL, pruneConstructors);324325/* Create the result array */326327elementClass = J9VMJAVALANGSTACKTRACEELEMENT_OR_NULL(vm);328arrayClass = elementClass->arrayClass;329if (arrayClass == NULL) {330/* the first class in vm->arrayROMClasses is the array class for Objects */331arrayClass = vmFuncs->internalCreateArrayClass(vmThread,332(J9ROMArrayClass *) J9ROMIMAGEHEADER_FIRSTCLASS(vm->arrayROMClasses),333elementClass);334if (arrayClass == NULL) {335/* exception is pending from the call */336return NULL;337}338}339result = (j9array_t) mmfns->J9AllocateIndexableObject(340vmThread, arrayClass, (U_32)numberOfFrames, J9_GC_ALLOCATE_OBJECT_NON_INSTRUMENTABLE);341if (result == NULL) {342vmFuncs->setHeapOutOfMemoryError(vmThread);343return NULL;344}345346/* Fill in the stack trace */347348userData.elementClass = elementClass;349userData.index = 0;350userData.maxFrames = numberOfFrames;351userData.previousFileName = NULL;352PUSH_OBJECT_IN_SPECIAL_FRAME(vmThread, (j9object_t) result);353vmFuncs->iterateStackTrace(vmThread, exceptionAddr, getStackTraceIterator, &userData, pruneConstructors);354result = (j9array_t) POP_OBJECT_IN_SPECIAL_FRAME(vmThread);355356/* If the stack trace sizes are inconsistent between pass 1 and 2, start again */357358if (vmThread->currentException == NULL) {359if (userData.index != numberOfFrames) {360goto retry;361}362}363364/* Return the result - any pending exception will be checked by the caller and the result discarded */365366return result;367}368369#if JAVA_SPEC_VERSION >= 11370/**371* Set the includeClassLoaderName and includeModuleVersion fields for a StackTraceElement.372*373* @param vmThread The VM thread.374* @param element The element to set fields for.375* @param classLoader The classloader to check.376*/377static void378setStackTraceElementFields(J9VMThread *vmThread, j9object_t element, J9ClassLoader *classLoader) {379J9JavaVM *vm = vmThread->javaVM;380BOOLEAN includeClassLoaderName = TRUE;381BOOLEAN includeModuleVersion = TRUE;382383/**384* If the classloader is one of the Platform or Bootstrap built-in classloaders,385* don't include its name or module version in the stack trace. If it is the386* Application/System built-in classloader, don't include the class name, but387* include the module version.388*/389if ((NULL == classLoader)390|| (vm->extensionClassLoader == classLoader) // JRE: Platform ClassLoader391|| (vm->systemClassLoader == classLoader) // JRE: Bootstrap ClassLoader392) {393includeClassLoaderName = FALSE;394includeModuleVersion = FALSE;395} else if (vm->applicationClassLoader == classLoader) { // JRE: System ClassLoader396includeClassLoaderName = FALSE;397}398399J9VMJAVALANGSTACKTRACEELEMENT_SET_INCLUDECLASSLOADERNAME(vmThread, element, (U_32) includeClassLoaderName);400J9VMJAVALANGSTACKTRACEELEMENT_SET_INCLUDEMODULEVERSION(vmThread, element, (U_32) includeModuleVersion);401}402#endif /* JAVA_SPEC_VERSION >= 11 */403404} /* extern "C" */405406407