diff --git a/make/autoconf/configure.ac b/make/autoconf/configure.ac index df50899a83008..5d48bd9aadda5 100644 --- a/make/autoconf/configure.ac +++ b/make/autoconf/configure.ac @@ -219,6 +219,9 @@ JDKOPT_SETUP_ADDRESS_SANITIZER # UndefinedBehaviorSanitizer JDKOPT_SETUP_UNDEFINED_BEHAVIOR_SANITIZER +# LeakSanitizer +JDKOPT_SETUP_LEAK_SANITIZER + ############################################################################### # # Check dependencies for external and internal libraries. diff --git a/make/autoconf/jdk-options.m4 b/make/autoconf/jdk-options.m4 index babe4639ccb3b..6adcc1e460d14 100644 --- a/make/autoconf/jdk-options.m4 +++ b/make/autoconf/jdk-options.m4 @@ -440,6 +440,39 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_ADDRESS_SANITIZER], AC_SUBST(ASAN_ENABLED) ]) +############################################################################### +# +# LeakSanitizer +# +AC_DEFUN_ONCE([JDKOPT_SETUP_LEAK_SANITIZER], +[ + UTIL_ARG_ENABLE(NAME: lsan, DEFAULT: false, RESULT: LSAN_ENABLED, + DESC: [enable LeakSanitizer], + CHECK_AVAILABLE: [ + AC_MSG_CHECKING([if LeakSanitizer (lsan) is available]) + if test "x$TOOLCHAIN_TYPE" = "xgcc" || + test "x$TOOLCHAIN_TYPE" = "xclang"; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + AVAILABLE=false + fi + ], + IF_ENABLED: [ + LSAN_CFLAGS="-fsanitize=leak -fno-omit-frame-pointer -DLEAK_SANITIZER" + LSAN_LDFLAGS="-fsanitize=leak" + JVM_CFLAGS="$JVM_CFLAGS $LSAN_CFLAGS" + JVM_LDFLAGS="$JVM_LDFLAGS $LSAN_LDFLAGS" + CFLAGS_JDKLIB="$CFLAGS_JDKLIB $LSAN_CFLAGS" + CFLAGS_JDKEXE="$CFLAGS_JDKEXE $LSAN_CFLAGS" + CXXFLAGS_JDKLIB="$CXXFLAGS_JDKLIB $LSAN_CFLAGS" + CXXFLAGS_JDKEXE="$CXXFLAGS_JDKEXE $LSAN_CFLAGS" + LDFLAGS_JDKLIB="$LDFLAGS_JDKLIB $LSAN_LDFLAGS" + LDFLAGS_JDKEXE="$LDFLAGS_JDKEXE $LSAN_LDFLAGS" + ]) + AC_SUBST(LSAN_ENABLED) +]) + ############################################################################### # # UndefinedBehaviorSanitizer diff --git a/make/autoconf/spec.gmk.in b/make/autoconf/spec.gmk.in index 93c9d629a0853..ff073c78c9279 100644 --- a/make/autoconf/spec.gmk.in +++ b/make/autoconf/spec.gmk.in @@ -448,6 +448,9 @@ JCOV_FILTERS=@JCOV_FILTERS@ # AddressSanitizer ASAN_ENABLED:=@ASAN_ENABLED@ +# LeakSanitizer +LSAN_ENABLED:=@LSAN_ENABLED@ + # UndefinedBehaviorSanitizer UBSAN_ENABLED:=@UBSAN_ENABLED@ UBSAN_CFLAGS:=@UBSAN_CFLAGS@ diff --git a/make/common/modules/LauncherCommon.gmk b/make/common/modules/LauncherCommon.gmk index f8ea1269e7949..5c13478cc3ac9 100644 --- a/make/common/modules/LauncherCommon.gmk +++ b/make/common/modules/LauncherCommon.gmk @@ -150,6 +150,10 @@ define SetupBuildLauncherBody $1_EXTRA_FILES += $(TOPDIR)/make/data/asan/asan_default_options.c endif + ifeq ($(LSAN_ENABLED), true) + $1_EXTRA_FILES += $(TOPDIR)/make/data/lsan/lsan_default_options.c + endif + $$(eval $$(call SetupJdkExecutable, BUILD_LAUNCHER_$1, \ NAME := $1, \ EXTRA_FILES := $$($1_EXTRA_FILES), \ diff --git a/make/data/asan/asan_default_options.c b/make/data/asan/asan_default_options.c index 140b5c5c311ca..444bcbf7a20bc 100644 --- a/make/data/asan/asan_default_options.c +++ b/make/data/asan/asan_default_options.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Google and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -50,8 +50,16 @@ // used. You can override these options by setting the environment variable ASAN_OPTIONS. ATTRIBUTE_DEFAULT_VISIBILITY ATTRIBUTE_USED const char* __asan_default_options() { return -#ifndef LEAK_SANITIZER +#ifdef LEAK_SANITIZER + "leak_check_at_exit=0," +#else + // ASan bundles LSan, however we only support LSan when it is explicitly requested during + // configuration. Thus we disable it to match if it was not requested. "detect_leaks=0," #endif - "handle_segv=0"; + "print_suppressions=0," + "handle_segv=0," + // See https://github.com/google/sanitizers/issues/1322. Hopefully this is resolved + // at some point and we can remove this option. + "intercept_tls_get_addr=0"; } diff --git a/make/data/asan/asan_default_options.cpp b/make/data/asan/asan_default_options.cpp index f84ebab27cb69..8afb3de46b780 100644 --- a/make/data/asan/asan_default_options.cpp +++ b/make/data/asan/asan_default_options.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Google and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/make/data/lsan/lsan_default_options.c b/make/data/lsan/lsan_default_options.c new file mode 100644 index 0000000000000..9b86eaa755adc --- /dev/null +++ b/make/data/lsan/lsan_default_options.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2023, Google and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef LEAK_SANITIZER +#error "Build misconfigured, preprocessor macro LEAK_SANITIZER should be defined" +#endif + +#ifndef __has_attribute +#define __has_attribute(x) 0 +#endif + +#if (defined(__GNUC__) && !defined(__clang__)) || __has_attribute(visibility) +#define ATTRIBUTE_DEFAULT_VISIBILITY __attribute__((visibility("default"))) +#else +#define ATTRIBUTE_DEFAULT_VISIBILITY +#endif + +#if (defined(__GNUC__) && !defined(__clang__)) || __has_attribute(used) +#define ATTRIBUTE_USED __attribute__((used)) +#else +#define ATTRIBUTE_USED +#endif + +// Override weak symbol exposed by LSan to override default options. This is called by LSan +// extremely early during library loading, before main is called. We need to override the default +// options because LSan will perform leak checking at program exit. Unfortunately Hotspot does not +// shutdown cleanly at the moment and some leaks occur, we want to ignore these. Instead we +// explicitly perform leak checking early during JVM shutdown. +ATTRIBUTE_DEFAULT_VISIBILITY ATTRIBUTE_USED const char* __lsan_default_options() { + return + "print_suppressions=0," + "leak_check_at_exit=0," + // See https://github.com/google/sanitizers/issues/1322. Hopefully this is resolved + // at some point and we can remove this option. + "intercept_tls_get_addr=0"; +} + +// Override weak symbol exposed by LSan to override default suppressions. This is called by LSan +// extremely early during library loading, before main is called. +ATTRIBUTE_DEFAULT_VISIBILITY ATTRIBUTE_USED const char* __lsan_default_suppressions() { + return + // Remove after JDK-8297688 is resolved. + "leak:^JLI_MemAlloc$\n" + "leak:^JLI_StringDup$\n"; +} diff --git a/make/data/lsan/lsan_default_options.cpp b/make/data/lsan/lsan_default_options.cpp new file mode 100644 index 0000000000000..72a8beeb7c2ef --- /dev/null +++ b/make/data/lsan/lsan_default_options.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2023, Google and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +extern "C" { + +#include "./lsan_default_options.c" + +} // extern "C" diff --git a/make/hotspot/lib/CompileGtest.gmk b/make/hotspot/lib/CompileGtest.gmk index d27cf53c05476..bba3ce15bb152 100644 --- a/make/hotspot/lib/CompileGtest.gmk +++ b/make/hotspot/lib/CompileGtest.gmk @@ -32,6 +32,10 @@ ifeq ($(ASAN_ENABLED), true) GTEST_LAUNCHER_SRC += $(TOPDIR)/make/data/asan/asan_default_options.cpp endif +ifeq ($(LSAN_ENABLED), true) + GTEST_LAUNCHER_SRC += $(TOPDIR)/make/data/lsan/lsan_default_options.cpp +endif + # On Windows, there are no internal debug symbols so must set copying to true # to get any at all. ifeq ($(call isTargetOs, windows), true) diff --git a/make/test/JtregNativeHotspot.gmk b/make/test/JtregNativeHotspot.gmk index 5c7126b6d84db..1d58c72554d0c 100644 --- a/make/test/JtregNativeHotspot.gmk +++ b/make/test/JtregNativeHotspot.gmk @@ -1521,7 +1521,13 @@ endif ifeq ($(ASAN_ENABLED), true) # Any executable which launches the JVM and uses a custom launcher needs to explicitly link in the # default ASan options. - BUILD_HOTSPOT_JTREG_EXTRA_FILES := $(TOPDIR)/make/data/asan/asan_default_options.c + BUILD_HOTSPOT_JTREG_EXTRA_FILES += $(TOPDIR)/make/data/asan/asan_default_options.c +endif + +ifeq ($(LSAN_ENABLED), true) + # Any executable which launches the JVM and uses a custom launcher needs to explicitly link in the + # default LSan options. + BUILD_HOTSPOT_JTREG_EXTRA_FILES += $(TOPDIR)/make/data/lsan/lsan_default_options.c endif # This evaluation is expensive and should only be done if this target was diff --git a/make/test/JtregNativeJdk.gmk b/make/test/JtregNativeJdk.gmk index ba3135bc9ff54..34d6bff0a522a 100644 --- a/make/test/JtregNativeJdk.gmk +++ b/make/test/JtregNativeJdk.gmk @@ -135,7 +135,13 @@ endif ifeq ($(ASAN_ENABLED), true) # Any executable which launches the JVM and uses a custom launcher needs to explicitly link in the # default ASan options. - BUILD_JDK_JTREG_EXTRA_FILES := $(TOPDIR)/make/data/asan/asan_default_options.c + BUILD_JDK_JTREG_EXTRA_FILES += $(TOPDIR)/make/data/asan/asan_default_options.c +endif + +ifeq ($(LSAN_ENABLED), true) + # Any executable which launches the JVM and uses a custom launcher needs to explicitly link in the + # default LSan options. + BUILD_JDK_JTREG_EXTRA_FILES += $(TOPDIR)/make/data/lsan/lsan_default_options.c endif # This evaluation is expensive and should only be done if this target was diff --git a/src/hotspot/share/c1/c1_Compiler.cpp b/src/hotspot/share/c1/c1_Compiler.cpp index 819ee6adeb9d4..dd5ab9c3cc0d5 100644 --- a/src/hotspot/share/c1/c1_Compiler.cpp +++ b/src/hotspot/share/c1/c1_Compiler.cpp @@ -40,6 +40,7 @@ #include "runtime/interfaceSupport.inline.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/vm_version.hpp" +#include "sanitizers/leak.hpp" #include "utilities/bitMap.inline.hpp" #include "utilities/macros.hpp" @@ -50,6 +51,8 @@ Compiler::Compiler() : AbstractCompiler(compiler_c1) { void Compiler::init_c1_runtime() { BufferBlob* buffer_blob = CompilerThread::current()->get_buffer_blob(); Arena* arena = new (mtCompiler) Arena(mtCompiler); + // Ignore leaked arena, it is used by ValueType and Interval during initialization. + LSAN_IGNORE_OBJECT(arena); Runtime1::initialize(buffer_blob); FrameMap::initialize(); // initialize data structures diff --git a/src/hotspot/share/cds/metaspaceShared.cpp b/src/hotspot/share/cds/metaspaceShared.cpp index 7afa8fb947c29..cc7506917906e 100644 --- a/src/hotspot/share/cds/metaspaceShared.cpp +++ b/src/hotspot/share/cds/metaspaceShared.cpp @@ -75,6 +75,7 @@ #include "runtime/sharedRuntime.hpp" #include "runtime/vmThread.hpp" #include "runtime/vmOperations.hpp" +#include "sanitizers/leak.hpp" #include "services/memTracker.hpp" #include "utilities/align.hpp" #include "utilities/bitMap.inline.hpp" @@ -990,6 +991,8 @@ void MetaspaceShared::initialize_runtime_shared_and_meta_spaces() { bool dynamic_mapped = (dynamic_mapinfo != nullptr && dynamic_mapinfo->is_mapped()); char* cds_base = static_mapinfo->mapped_base(); char* cds_end = dynamic_mapped ? dynamic_mapinfo->mapped_end() : static_mapinfo->mapped_end(); + // Register CDS memory region with LSan. + LSAN_REGISTER_ROOT_REGION(cds_base, cds_end - cds_base); set_shared_metaspace_range(cds_base, static_mapinfo->mapped_end(), cds_end); _relocation_delta = static_mapinfo->relocation_delta(); _requested_base_address = static_mapinfo->requested_base_address(); diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp index 87487c9d4ddfe..df1b27b31659b 100644 --- a/src/hotspot/share/code/codeCache.cpp +++ b/src/hotspot/share/code/codeCache.cpp @@ -62,6 +62,7 @@ #include "runtime/os.inline.hpp" #include "runtime/safepointVerifiers.hpp" #include "runtime/vmThread.hpp" +#include "sanitizers/leak.hpp" #include "services/memoryService.hpp" #include "utilities/align.hpp" #include "utilities/vmError.hpp" @@ -329,6 +330,9 @@ void CodeCache::initialize_heaps() { ReservedSpace non_method_space = rest.first_part(non_nmethod_size); ReservedSpace non_profiled_space = rest.last_part(non_nmethod_size); + // Register CodeHeaps with LSan as we sometimes embed pointers to malloc memory. + LSAN_REGISTER_ROOT_REGION(rs.base(), rs.size()); + // Non-nmethods (stubs, adapters, ...) add_heap(non_method_space, "CodeHeap 'non-nmethods'", CodeBlobType::NonNMethod); // Tier 2 and tier 3 (profiled) methods @@ -1194,6 +1198,8 @@ void CodeCache::initialize() { FLAG_SET_ERGO(ProfiledCodeHeapSize, 0); FLAG_SET_ERGO(NonProfiledCodeHeapSize, 0); ReservedCodeSpace rs = reserve_heap_memory(ReservedCodeCacheSize); + // Register CodeHeaps with LSan as we sometimes embed pointers to malloc memory. + LSAN_REGISTER_ROOT_REGION(rs.base(), rs.size()); add_heap(rs, "CodeCache", CodeBlobType::All); } diff --git a/src/hotspot/share/code/compiledIC.cpp b/src/hotspot/share/code/compiledIC.cpp index ea4b3d78b9bc4..c19ee182d5186 100644 --- a/src/hotspot/share/code/compiledIC.cpp +++ b/src/hotspot/share/code/compiledIC.cpp @@ -45,6 +45,7 @@ #include "runtime/safepoint.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/stubRoutines.hpp" +#include "sanitizers/leak.hpp" #include "utilities/events.hpp" @@ -272,6 +273,9 @@ bool CompiledIC::set_to_megamorphic(CallInfo* call_info, Bytecodes::Code bytecod needs_ic_stub_refill = true; return false; } + // LSan appears unable to follow malloc-based memory consistently when embedded as an immediate + // in generated machine code. So we have to ignore it. + LSAN_IGNORE_OBJECT(holder); } else { assert(call_info->call_kind() == CallInfo::vtable_call, "either itable or vtable"); // Can be different than selected_method->vtable_index(), due to package-private etc. @@ -442,6 +446,9 @@ bool CompiledIC::set_to_monomorphic(CompiledICInfo& info) { delete holder; return false; } + // LSan appears unable to follow malloc-based memory consistently when embedded as an + // immediate in generated machine code. So we have to ignore it. + LSAN_IGNORE_OBJECT(holder); if (TraceICs) { ResourceMark rm(thread); tty->print_cr ("IC@" INTPTR_FORMAT ": monomorphic to interpreter via icholder ", p2i(instruction_address())); diff --git a/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp b/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp index 286d1b6a01195..ad3c3941c8292 100644 --- a/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp +++ b/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp @@ -43,6 +43,7 @@ #include "runtime/mutexLocker.hpp" #include "runtime/os.hpp" #include "sanitizers/address.hpp" +#include "sanitizers/leak.hpp" #include "services/memTracker.hpp" #include "utilities/align.hpp" #include "utilities/debug.hpp" @@ -239,6 +240,10 @@ VirtualSpaceNode::VirtualSpaceNode(ReservedSpace rs, bool owns_rs, CommitLimiter // Poison the memory region. It will be unpoisoned later on a per-chunk base for chunks that are // handed to arenas. ASAN_POISON_MEMORY_REGION(rs.base(), rs.size()); + + // Register memory region related to Metaspace. The Metaspace contains lots of pointers to malloc + // memory. + LSAN_REGISTER_ROOT_REGION(rs.base(), rs.size()); } // Create a node of a given size (it will create its own space). @@ -270,11 +275,15 @@ VirtualSpaceNode* VirtualSpaceNode::create_node(ReservedSpace rs, CommitLimiter* VirtualSpaceNode::~VirtualSpaceNode() { DEBUG_ONLY(verify_locked();) + // Unregister memory region related to Metaspace. + LSAN_UNREGISTER_ROOT_REGION(_rs.base(), _rs.size()); + // Undo the poisoning before potentially unmapping memory. This ensures that future mappings at // the same address do not unexpectedly fail with use-after-poison. ASAN_UNPOISON_MEMORY_REGION(_rs.base(), _rs.size()); UL(debug, ": dies."); + if (_owns_rs) { _rs.release(); } diff --git a/src/hotspot/share/memory/universe.cpp b/src/hotspot/share/memory/universe.cpp index a0357a790b365..da34cf4349555 100644 --- a/src/hotspot/share/memory/universe.cpp +++ b/src/hotspot/share/memory/universe.cpp @@ -75,6 +75,7 @@ #include "runtime/jniHandles.hpp" #include "runtime/threads.hpp" #include "runtime/timerTrace.hpp" +#include "sanitizers/leak.hpp" #include "services/memoryService.hpp" #include "utilities/align.hpp" #include "utilities/autoRestore.hpp" @@ -751,7 +752,11 @@ bool Universe::contains_non_oop_word(void* p) { } static void initialize_global_behaviours() { - CompiledICProtectionBehaviour::set_current(new DefaultICProtectionBehaviour()); + DefaultICProtectionBehaviour* protection_behavior = new DefaultICProtectionBehaviour(); + // Ignore leak of DefaultICProtectionBehaviour. It is overriden by some GC implementations and the + // pointer is leaked once. + LSAN_IGNORE_OBJECT(protection_behavior); + CompiledICProtectionBehaviour::set_current(protection_behavior); } jint universe_init() { diff --git a/src/hotspot/share/prims/methodHandles.cpp b/src/hotspot/share/prims/methodHandles.cpp index b00af7d64cd41..8e4247e679142 100644 --- a/src/hotspot/share/prims/methodHandles.cpp +++ b/src/hotspot/share/prims/methodHandles.cpp @@ -59,6 +59,7 @@ #include "runtime/safepointVerifiers.hpp" #include "runtime/signature.hpp" #include "runtime/stubRoutines.hpp" +#include "sanitizers/leak.hpp" #include "utilities/exceptions.hpp" @@ -983,6 +984,8 @@ void MethodHandles::trace_method_handle_interpreter_entry(MacroAssembler* _masm, } jio_snprintf(qname, len, "MethodHandle::interpreter_entry::%s%s", name, suffix); trace_method_handle(_masm, qname); + // LSan appears unable to keep track of qname, ignore it. + LSAN_IGNORE_OBJECT(qname); // Note: Don't free the allocated char array because it's used // during runtime. } diff --git a/src/hotspot/share/runtime/init.cpp b/src/hotspot/share/runtime/init.cpp index 59ea2c808abc2..3507649fb537d 100644 --- a/src/hotspot/share/runtime/init.cpp +++ b/src/hotspot/share/runtime/init.cpp @@ -28,6 +28,7 @@ #include "code/icBuffer.hpp" #include "compiler/compiler_globals.hpp" #include "gc/shared/collectedHeap.hpp" +#include "gc/shared/gcHeapSummary.hpp" #include "interpreter/bytecodes.hpp" #include "logging/logAsyncWriter.hpp" #include "memory/universe.hpp" @@ -43,6 +44,7 @@ #include "runtime/init.hpp" #include "runtime/safepoint.hpp" #include "runtime/sharedRuntime.hpp" +#include "sanitizers/leak.hpp" #include "services/memTracker.hpp" #include "utilities/macros.hpp" #if INCLUDE_JVMCI @@ -123,6 +125,14 @@ jint init_globals() { if (status != JNI_OK) return status; +#ifdef LEAK_SANITIZER + { + // Register the Java heap with LSan. + VirtualSpaceSummary summary = Universe::heap()->create_heap_space_summary(); + LSAN_REGISTER_ROOT_REGION(summary.start(), summary.reserved_size()); + } +#endif // LEAK_SANITIZER + AsyncLogWriter::initialize(); gc_barrier_stubs_init(); // depends on universe_init, must be before interpreter_init continuations_init(); // must precede continuation stub generation @@ -183,6 +193,13 @@ void exit_globals() { StringTable::dump(tty); } ostream_exit(); +#ifdef LEAK_SANITIZER + { + // Unregister the Java heap with LSan. + VirtualSpaceSummary summary = Universe::heap()->create_heap_space_summary(); + LSAN_UNREGISTER_ROOT_REGION(summary.start(), summary.reserved_size()); + } +#endif // LEAK_SANITIZER } } diff --git a/src/hotspot/share/runtime/java.cpp b/src/hotspot/share/runtime/java.cpp index ba5e0c5dcc63b..44e587a6857ac 100644 --- a/src/hotspot/share/runtime/java.cpp +++ b/src/hotspot/share/runtime/java.cpp @@ -72,6 +72,7 @@ #include "runtime/vmOperations.hpp" #include "runtime/vmThread.hpp" #include "runtime/vm_version.hpp" +#include "sanitizers/leak.hpp" #include "services/memTracker.hpp" #include "utilities/dtrace.hpp" #include "utilities/globalDefinitions.hpp" @@ -416,6 +417,30 @@ void before_exit(JavaThread* thread, bool halt) { } } + // At this point only one thread is executing this logic. Any other threads + // attempting to invoke before_exit() will wait above and return early once + // this thread finishes before_exit(). + + // Do not add any additional shutdown logic between the above mutex logic and + // leak sanitizer logic below. Any additional shutdown code which performs some + // cleanup should be added after the leak sanitizer logic below. + +#ifdef LEAK_SANITIZER + // If we are built with LSan, we need to perform leak checking. If we are + // terminating normally, not halting and no VM error, we perform a normal + // leak check which terminates if leaks are found. If we are not terminating + // normally, halting or VM error, we perform a recoverable leak check which + // prints leaks but will not terminate. + if (!halt && !VMError::is_error_reported()) { + LSAN_DO_LEAK_CHECK(); + } else { + // Ignore the return value. + static_cast(LSAN_DO_RECOVERABLE_LEAK_CHECK()); + } +#endif + + // Actual shutdown logic begins here. + #if INCLUDE_JVMCI if (EnableJVMCI) { JVMCI::shutdown(thread); diff --git a/src/hotspot/share/runtime/nonJavaThread.cpp b/src/hotspot/share/runtime/nonJavaThread.cpp index fa68c2be1d746..93c12739e44b6 100644 --- a/src/hotspot/share/runtime/nonJavaThread.cpp +++ b/src/hotspot/share/runtime/nonJavaThread.cpp @@ -33,6 +33,7 @@ #include "runtime/nonJavaThread.hpp" #include "runtime/osThread.hpp" #include "runtime/task.hpp" +#include "sanitizers/leak.hpp" #include "utilities/defaultStream.hpp" #include "utilities/singleWriterSynchronizer.hpp" #include "utilities/vmError.hpp" @@ -285,6 +286,7 @@ void WatcherThread::run() { // Signal that it is terminated { MutexLocker mu(Terminator_lock, Mutex::_no_safepoint_check_flag); + LSAN_IGNORE_OBJECT(_watcher_thread); _watcher_thread = nullptr; Terminator_lock->notify_all(); } diff --git a/src/hotspot/share/sanitizers/leak.hpp b/src/hotspot/share/sanitizers/leak.hpp new file mode 100644 index 0000000000000..f3d7a23bf48b2 --- /dev/null +++ b/src/hotspot/share/sanitizers/leak.hpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2023, Google and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_SANITIZERS_LEAK_HPP +#define SHARE_SANITIZERS_LEAK_HPP + +#ifdef LEAK_SANITIZER +#include +#endif + +// LSAN_REGISTER_ROOT_REGION()/LSAN_UNREGISTER_ROOT_REGION() +// +// Register/unregister regions of memory with LSan. LSan scans these regions looking for +// pointers to malloc memory. This is only necessary when pointers to malloc memory are +// located in memory that is not returned by malloc, such as mapped memory. LSan will +// skip inaccessible parts of the region, such as those that are not readable. +#ifdef LEAK_SANITIZER +#define LSAN_REGISTER_ROOT_REGION(addr, size) __lsan_register_root_region((addr), (size)) +#define LSAN_UNREGISTER_ROOT_REGION(addr, size) __lsan_unregister_root_region((addr), (size)) +#else +#define LSAN_REGISTER_ROOT_REGION(addr, size) \ + do { \ + if (false) { \ + ((void) (addr)); \ + ((void) (size)); \ + } \ + } while (false) +#define LSAN_UNREGISTER_ROOT_REGION(addr, size) \ + do { \ + if (false) { \ + ((void) (addr)); \ + ((void) (size)); \ + } \ + } while (false) +#endif + +// LSAN_IGNORE_OBJECT() +// +// Causes LSan to ignore any leaks related to the object. Should only be used +// in cases where leaks are intentional or where LSan will be unable to discover +// pointers to object, for example due to pointers being stored unaligned. +#ifdef LEAK_SANITIZER +#define LSAN_IGNORE_OBJECT(object) __lsan_ignore_object(object) +#else +#define LSAN_IGNORE_OBJECT(object) \ + do { \ + if (false) { \ + ((void) (object)); \ + } \ + } while (false) +#endif + +// LSAN_DO_LEAK_CHECK() +// +// Perform a leak check, terminating the process if leaks are found. LSan will +// skip performing leak checks at process exit and further calls will be ignored. +#ifdef LEAK_SANITIZER +#define LSAN_DO_LEAK_CHECK() __lsan_do_leak_check() +#else +#define LSAN_DO_LEAK_CHECK() ((void) 0) +#endif + +// LSAN_DO_RECOVERABLE_LEAK_CHECK() +// +// Perform a leak check without terminating if leaks are found. +#ifdef LEAK_SANITIZER +#define LSAN_DO_RECOVERABLE_LEAK_CHECK() __lsan_do_recoverable_leak_check() +#else +#define LSAN_DO_RECOVERABLE_LEAK_CHECK() ((int) 0) +#endif + +#endif // SHARE_SANITIZERS_ADDRESS_HPP