From 2807399ea3527cde09c88d6106567263e962577f Mon Sep 17 00:00:00 2001 From: Justin King Date: Mon, 17 Oct 2022 08:28:25 -0700 Subject: [PATCH 1/7] Implement initial LSAN support --- make/autoconf/configure.ac | 3 + make/autoconf/jdk-options.m4 | 33 ++++++ make/autoconf/spec.gmk.in | 3 + src/hotspot/share/c1/c1_Compiler.cpp | 3 + src/hotspot/share/code/codeCache.cpp | 3 + src/hotspot/share/code/compiledIC.cpp | 7 ++ src/hotspot/share/lsan/lsan.hpp | 104 ++++++++++++++++++ src/hotspot/share/memory/heap.cpp | 18 ++- src/hotspot/share/memory/heap.hpp | 2 + .../memory/metaspace/virtualSpaceNode.cpp | 9 ++ src/hotspot/share/memory/universe.cpp | 5 +- src/hotspot/share/prims/methodHandles.cpp | 3 + src/hotspot/share/runtime/arguments.cpp | 23 ++++ src/hotspot/share/runtime/init.cpp | 13 +++ src/hotspot/share/runtime/java.cpp | 9 ++ src/hotspot/share/runtime/nonJavaThread.cpp | 2 + .../share/utilities/globalDefinitions.hpp | 4 + .../share/utilities/globalDefinitions_gcc.hpp | 2 + src/hotspot/share/utilities/macros.hpp | 8 ++ src/java.base/share/native/launcher/main.c | 9 ++ test/lib/jdk/test/lib/Platform.java | 6 + 21 files changed, 267 insertions(+), 2 deletions(-) create mode 100644 src/hotspot/share/lsan/lsan.hpp 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..ab1706aa11809 100644 --- a/make/autoconf/spec.gmk.in +++ b/make/autoconf/spec.gmk.in @@ -453,6 +453,9 @@ UBSAN_ENABLED:=@UBSAN_ENABLED@ UBSAN_CFLAGS:=@UBSAN_CFLAGS@ UBSAN_LDFLAGS:=@UBSAN_LDFLAGS@ +# LeakSanitizer +LSAN_ENABLED:=@LSAN_ENABLED@ + # Necessary additional compiler flags to compile X11 X_CFLAGS:=@X_CFLAGS@ X_LIBS:=@X_LIBS@ diff --git a/src/hotspot/share/c1/c1_Compiler.cpp b/src/hotspot/share/c1/c1_Compiler.cpp index 819ee6adeb9d4..070404b0925ce 100644 --- a/src/hotspot/share/c1/c1_Compiler.cpp +++ b/src/hotspot/share/c1/c1_Compiler.cpp @@ -42,6 +42,7 @@ #include "runtime/vm_version.hpp" #include "utilities/bitMap.inline.hpp" #include "utilities/macros.hpp" +#include "lsan/lsan.hpp" Compiler::Compiler() : AbstractCompiler(compiler_c1) { @@ -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_leak(arena); Runtime1::initialize(buffer_blob); FrameMap::initialize(); // initialize data structures diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp index e4537c11f9988..2ae3ec6d782b1 100644 --- a/src/hotspot/share/code/codeCache.cpp +++ b/src/hotspot/share/code/codeCache.cpp @@ -75,6 +75,7 @@ #include "opto/compile.hpp" #include "opto/node.hpp" #endif +#include "lsan/lsan.hpp" // Helper class for printing in CodeCache class CodeBlob_sizes { @@ -329,6 +330,8 @@ void CodeCache::initialize_heaps() { ReservedSpace non_method_space = rest.first_part(non_nmethod_size); ReservedSpace non_profiled_space = rest.last_part(non_nmethod_size); + 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 diff --git a/src/hotspot/share/code/compiledIC.cpp b/src/hotspot/share/code/compiledIC.cpp index ea4b3d78b9bc4..54e27c082893d 100644 --- a/src/hotspot/share/code/compiledIC.cpp +++ b/src/hotspot/share/code/compiledIC.cpp @@ -46,6 +46,7 @@ #include "runtime/sharedRuntime.hpp" #include "runtime/stubRoutines.hpp" #include "utilities/events.hpp" +#include "lsan/lsan.hpp" // Every time a compiled IC is changed or its type is being accessed, @@ -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_leak(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_leak(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/lsan/lsan.hpp b/src/hotspot/share/lsan/lsan.hpp new file mode 100644 index 0000000000000..73893bc894777 --- /dev/null +++ b/src/hotspot/share/lsan/lsan.hpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2022, Oracle 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_LSAN_LSAN_HPP +#define SHARE_LSAN_LSAN_HPP + +#include "memory/allStatic.hpp" +#include "utilities/globalDefinitions.hpp" + +#include + +#ifdef LEAK_SANITIZER +#include +#define NO_SANITIE_LEAK __attribute__((no_sanitize("leak"))) +#else +#define NO_SANITIE_LEAK +#endif + +// Class-based namespace enclosing methods for interacting with LSan. This +// interface is always available regardless of whether LSan is available or not +// in the build. If LSan is not available, all methods are NOOP and will be +// compiled out. This approach makes maintaining code related to LSan easier as +// the code is always compiled, removing the chance of accidental breakages. +class Lsan final : AllStatic { + public: + // Returns true IIF LSan is enabled and available. + static ATTRIBUTE_ARTIFICIAL ALWAYSINLINE bool enabled() { +#ifdef LEAK_SANITIZER + return true; +#else + return false; +#endif + } + + // Perform a leak check. If any leaks are detected the program immediatley + // exits with a non-zero code. + static ATTRIBUTE_ARTIFICIAL ALWAYSINLINE void do_leak_check() { +#ifdef LEAK_SANITIZER + __lsan_do_leak_check(); +#endif + } + + // Returns true IFF leaks were detected. + static ATTRIBUTE_ARTIFICIAL ALWAYSINLINE bool do_recoverable_leak_check() { +#ifdef LEAK_SANITIZER + return __lsan_do_recoverable_leak_check() != 0; +#else + return false; +#endif + } + + // Registers a region of memory that may contain pointers to malloc-based + // memory. This only needs to be done for manually mapped memory via mmap. + static ATTRIBUTE_ARTIFICIAL ALWAYSINLINE void register_root_region(const void* ptr, size_t n) { +#ifdef LEAK_SANITIZER + __lsan_register_root_region(ptr, n); +#else + static_cast(ptr); + static_cast(n); +#endif + } + + // Unregisters a previously registered region of memory. + static ATTRIBUTE_ARTIFICIAL ALWAYSINLINE void unregister_root_region(const void* ptr, size_t n) { +#ifdef LEAK_SANITIZER + __lsan_unregister_root_region(ptr, n); +#else + static_cast(ptr); + static_cast(n); +#endif + } + + // Ignore any leak related to the memory pointed to by `ptr`. + template + static ATTRIBUTE_ARTIFICIAL ALWAYSINLINE T* ignore_leak(T* ptr) { +#ifdef LEAK_SANITIZER + __lsan_ignore_object(ptr); +#endif + return ptr; + } +}; + +#endif // SHARE_LSAN_LSAN_HPP diff --git a/src/hotspot/share/memory/heap.cpp b/src/hotspot/share/memory/heap.cpp index b71615d563bec..e218ea177945a 100644 --- a/src/hotspot/share/memory/heap.cpp +++ b/src/hotspot/share/memory/heap.cpp @@ -30,6 +30,7 @@ #include "services/memTracker.hpp" #include "utilities/align.hpp" #include "utilities/powerOfTwo.hpp" +#include "lsan/lsan.hpp" // Implementation of Heap @@ -53,6 +54,11 @@ CodeHeap::CodeHeap(const char* name, const CodeBlobType code_blob_type) _fragmentation_count = 0; } +CodeHeap::~CodeHeap() { + // Unregister memory region related to Code Cache. + Lsan::unregister_root_region(_memory.low_boundary(), _memory.reserved_size()); +} + // Dummy initialization of template array. char CodeHeap::segmap_template[] = {0}; @@ -218,6 +224,10 @@ bool CodeHeap::reserve(ReservedSpace rs, size_t committed_size, size_t segment_s return false; } + // Register memory region related to Code Cache. It sometimes contains immediate values which are + // pointers to malloc memory. + Lsan::register_root_region(_memory.low_boundary(), _memory.reserved_size()); + on_code_mapping(_memory.low(), _memory.committed_size()); _number_of_committed_segments = size_to_segments(_memory.committed_size()); _number_of_reserved_segments = size_to_segments(_memory.reserved_size()); @@ -256,7 +266,13 @@ bool CodeHeap::expand_by(size_t size) { dm = _memory.uncommitted_size(); } char* base = _memory.low() + _memory.committed_size(); - if (!_memory.expand_by(dm)) return false; + // Unregister and register memory region related to Code Cache. + Lsan::unregister_root_region(_memory.low_boundary(), _memory.reserved_size()); + if (!_memory.expand_by(dm)) { + Lsan::register_root_region(_memory.low_boundary(), _memory.reserved_size()); + return false; + } + Lsan::register_root_region(_memory.low_boundary(), _memory.reserved_size()); on_code_mapping(base, dm); size_t i = _number_of_committed_segments; _number_of_committed_segments = size_to_segments(_memory.committed_size()); diff --git a/src/hotspot/share/memory/heap.hpp b/src/hotspot/share/memory/heap.hpp index 4b25d3bdba001..f81bdd75093fd 100644 --- a/src/hotspot/share/memory/heap.hpp +++ b/src/hotspot/share/memory/heap.hpp @@ -148,6 +148,8 @@ class CodeHeap : public CHeapObj { public: CodeHeap(const char* name, const CodeBlobType code_blob_type); + ~CodeHeap(); + // Heap extents bool reserve(ReservedSpace rs, size_t committed_size, size_t segment_size); bool expand_by(size_t size); // expands committed memory by size diff --git a/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp b/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp index db994003a175a..59f03960ed19b 100644 --- a/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp +++ b/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp @@ -48,6 +48,7 @@ #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/ostream.hpp" +#include "lsan/lsan.hpp" namespace metaspace { @@ -233,6 +234,10 @@ VirtualSpaceNode::VirtualSpaceNode(ReservedSpace rs, bool owns_rs, CommitLimiter // Update reserved counter in vslist _total_reserved_words_counter->increment_by(_word_size); + // Register memory region related to Metaspace. The Metaspace contains lots of pointers to malloc + // memory. + Lsan::register_root_region(rs.base(), rs.size()); + assert_is_aligned(_base, chunklevel::MAX_CHUNK_BYTE_SIZE); assert_is_aligned(_word_size, chunklevel::MAX_CHUNK_WORD_SIZE); @@ -275,6 +280,10 @@ VirtualSpaceNode::~VirtualSpaceNode() { ASAN_UNPOISON_MEMORY_REGION(_rs.base(), _rs.size()); UL(debug, ": dies."); + + // Unregister memory region related to Metaspace. + Lsan::unregister_root_region(_rs.base(), _rs.size()); + if (_owns_rs) { _rs.release(); } diff --git a/src/hotspot/share/memory/universe.cpp b/src/hotspot/share/memory/universe.cpp index 99348438a05fe..6c791435be8ab 100644 --- a/src/hotspot/share/memory/universe.cpp +++ b/src/hotspot/share/memory/universe.cpp @@ -83,6 +83,7 @@ #include "utilities/macros.hpp" #include "utilities/ostream.hpp" #include "utilities/preserveException.hpp" +#include "lsan/lsan.hpp" // Known objects Klass* Universe::_typeArrayKlassObjs[T_LONG+1] = { NULL /*, NULL...*/ }; @@ -751,7 +752,9 @@ bool Universe::contains_non_oop_word(void* p) { } static void initialize_global_behaviours() { - CompiledICProtectionBehaviour::set_current(new DefaultICProtectionBehaviour()); + // Ignore leak of DefaultICProtectionBehaviour. It is overriden by some GC implementations and the + // pointer is leaked once. + CompiledICProtectionBehaviour::set_current(Lsan::ignore_leak(new DefaultICProtectionBehaviour())); } jint universe_init() { diff --git a/src/hotspot/share/prims/methodHandles.cpp b/src/hotspot/share/prims/methodHandles.cpp index 46e68c5cc7ee4..1f25be35b0de5 100644 --- a/src/hotspot/share/prims/methodHandles.cpp +++ b/src/hotspot/share/prims/methodHandles.cpp @@ -60,6 +60,7 @@ #include "runtime/signature.hpp" #include "runtime/stubRoutines.hpp" #include "utilities/exceptions.hpp" +#include "lsan/lsan.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_leak(qname); // Note: Don't free the allocated char array because it's used // during runtime. } diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 0323bb1397d46..ccb42deacd79a 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -64,6 +64,7 @@ #include "utilities/parseInteger.hpp" #include "utilities/powerOfTwo.hpp" #include "utilities/stringUtils.hpp" +#include "lsan/lsan.hpp" #if INCLUDE_JFR #include "jfr/jfr.hpp" #endif @@ -427,6 +428,12 @@ void Arguments::init_system_properties() { PropertyList_add(&_system_properties, _jdk_boot_class_path_append); PropertyList_add(&_system_properties, _vm_info); + // Add sanitizer properties. + if (Lsan::enabled()) { + PropertyList_add(&_system_properties, + new SystemProperty("jdk.sanitizer.leak.enabled", "true", false)); + } + // Set OS specific system properties values os::init_system_properties_values(); } @@ -3972,6 +3979,22 @@ jint Arguments::parse(const JavaVMInitArgs* initial_cmd_args) { no_shared_spaces("CDS Disabled"); #endif // INCLUDE_CDS + LSAN_ONLY( +#ifdef _LP64 + // LSAN relies on pointers to be natively aligned. Using compressed class pointers breaks this + // expectation and results in nondeterministic leak reports. + if (FLAG_SET_CMDLINE(UseCompressedOops, false) != JVMFlag::SUCCESS) { + return JNI_EINVAL; + } + if (FLAG_SET_CMDLINE(UseCompressedClassPointers, false) != JVMFlag::SUCCESS) { + return JNI_EINVAL; + } +#endif // _LP64 + // During testing CDS appeared to cause LSan to report many false positives, so disable it. + UseSharedSpaces = false; + RequireSharedSpaces = false; + ); + // Verify NMT arguments const NMT_TrackingLevel lvl = NMTUtil::parse_tracking_level(NativeMemoryTracking); if (lvl == NMT_unknown) { diff --git a/src/hotspot/share/runtime/init.cpp b/src/hotspot/share/runtime/init.cpp index 59ea2c808abc2..c0b352fe82fdf 100644 --- a/src/hotspot/share/runtime/init.cpp +++ b/src/hotspot/share/runtime/init.cpp @@ -28,6 +28,8 @@ #include "code/icBuffer.hpp" #include "compiler/compiler_globals.hpp" #include "gc/shared/collectedHeap.hpp" +#include "gc/shared/gcHeapSummary.hpp" +#include "lsan/lsan.hpp" #include "interpreter/bytecodes.hpp" #include "logging/logAsyncWriter.hpp" #include "memory/universe.hpp" @@ -123,6 +125,12 @@ jint init_globals() { if (status != JNI_OK) return status; + { + // Register the Java heap with LSan. + VirtualSpaceSummary summary = Universe::heap()->create_heap_space_summary(); + Lsan::register_root_region(summary.start(), summary.reserved_size()); + } + AsyncLogWriter::initialize(); gc_barrier_stubs_init(); // depends on universe_init, must be before interpreter_init continuations_init(); // must precede continuation stub generation @@ -183,6 +191,11 @@ void exit_globals() { StringTable::dump(tty); } ostream_exit(); + { + // Unregister the Java heap with LSan. + VirtualSpaceSummary summary = Universe::heap()->create_heap_space_summary(); + Lsan::unregister_root_region(summary.start(), summary.reserved_size()); + } } } diff --git a/src/hotspot/share/runtime/java.cpp b/src/hotspot/share/runtime/java.cpp index ba5e0c5dcc63b..d1fdc22e1164c 100644 --- a/src/hotspot/share/runtime/java.cpp +++ b/src/hotspot/share/runtime/java.cpp @@ -93,6 +93,7 @@ #if INCLUDE_JVMCI #include "jvmci/jvmci.hpp" #endif +#include "lsan/lsan.hpp" GrowableArray* collected_profiled_methods; @@ -416,6 +417,14 @@ void before_exit(JavaThread* thread, bool halt) { } } + // If we are built with LSan, not halting, and there is no JVM error perform a full GC and then + // perform leak checking. We do this as early as possible during the shutdown sequence as we are + // not interested in leaks introduced during shutdown. + if (Lsan::enabled() && !halt && !VMError::is_error_reported()) { + Universe::heap()->collect(GCCause::_java_lang_system_gc); + Lsan::do_leak_check(); + } + #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..d1db6c139af19 100644 --- a/src/hotspot/share/runtime/nonJavaThread.cpp +++ b/src/hotspot/share/runtime/nonJavaThread.cpp @@ -39,6 +39,7 @@ #if INCLUDE_JFR #include "jfr/jfr.hpp" #endif +#include "lsan/lsan.hpp" // List of all NonJavaThreads and safe iteration over that list. @@ -285,6 +286,7 @@ void WatcherThread::run() { // Signal that it is terminated { MutexLocker mu(Terminator_lock, Mutex::_no_safepoint_check_flag); + Lsan::ignore_leak(_watcher_thread); _watcher_thread = nullptr; Terminator_lock->notify_all(); } diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp index 899aa45aea158..c131c668c5c49 100644 --- a/src/hotspot/share/utilities/globalDefinitions.hpp +++ b/src/hotspot/share/utilities/globalDefinitions.hpp @@ -56,6 +56,10 @@ class oopDesc; #define ATTRIBUTE_FLATTEN #endif +#ifndef ATTRIBUTE_ARTIFICIAL +#define ATTRIBUTE_ARTIFICIAL +#endif + // These are #defines to selectively turn on/off the Print(Opto)Assembly // capabilities. Choices should be led by a tradeoff between // code size and improved supportability. diff --git a/src/hotspot/share/utilities/globalDefinitions_gcc.hpp b/src/hotspot/share/utilities/globalDefinitions_gcc.hpp index 3da670ef47659..03a058defb88a 100644 --- a/src/hotspot/share/utilities/globalDefinitions_gcc.hpp +++ b/src/hotspot/share/utilities/globalDefinitions_gcc.hpp @@ -164,4 +164,6 @@ inline int g_isfinite(jdouble f) { return isfinite(f); } // #define ATTRIBUTE_ALIGNED(x) __attribute__((aligned(x))) +#define ATTRIBUTE_ARTIFICIAL __attribute__((artificial)) + #endif // SHARE_UTILITIES_GLOBALDEFINITIONS_GCC_HPP diff --git a/src/hotspot/share/utilities/macros.hpp b/src/hotspot/share/utilities/macros.hpp index bfbaaa58acc2c..a4e5d9e9eb91a 100644 --- a/src/hotspot/share/utilities/macros.hpp +++ b/src/hotspot/share/utilities/macros.hpp @@ -265,6 +265,14 @@ #define NOT_JFR_RETURN_(code) { return code; } #endif +#ifdef LEAK_SANITIZER +#define LSAN_ONLY(code) code +#define NOT_LSAN(code) +#else +#define LSAN_ONLY(code) +#define NOT_LSAN(code) code +#endif + #ifndef INCLUDE_JVMCI #define INCLUDE_JVMCI 1 #endif diff --git a/src/java.base/share/native/launcher/main.c b/src/java.base/share/native/launcher/main.c index d3898d7adc634..6250fe9985dc3 100644 --- a/src/java.base/share/native/launcher/main.c +++ b/src/java.base/share/native/launcher/main.c @@ -34,6 +34,15 @@ #include "jli_util.h" #include "jni.h" +#ifdef LEAK_SANITIZER +// 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 disable implicit leak checks +// during exit and instead invoke leak checking early during JVM shutdown. +JNIEXPORT const char* __lsan_default_options() { + return "leak_check_at_exit=0"; +} +#endif + /* * Entry point. */ diff --git a/test/lib/jdk/test/lib/Platform.java b/test/lib/jdk/test/lib/Platform.java index 647c62834aa5c..89ca171e8c38e 100644 --- a/test/lib/jdk/test/lib/Platform.java +++ b/test/lib/jdk/test/lib/Platform.java @@ -50,6 +50,8 @@ public class Platform { private static final String compiler = privilegedGetProperty("sun.management.compiler"); private static final String testJdk = privilegedGetProperty("test.jdk"); + private static final boolean leakSanitizer = Boolean.parseBoolean(privilegedGetProperty("jdk.sanitizer.leak.enabled")); + @SuppressWarnings("removal") private static String privilegedGetProperty(String key) { return AccessController.doPrivileged(( @@ -419,4 +421,8 @@ public static boolean isDefaultCDSArchiveSupported() { public static boolean areCustomLoadersSupportedForCDS() { return (is64bit() && (isLinux() || isOSX() || isWindows())); } + + public static boolean hasLeakSanitizer() { + return leakSanitizer; + } } From b43473af64b0e73f067fcaf53370e9f32ea10208 Mon Sep 17 00:00:00 2001 From: Justin King Date: Thu, 26 Jan 2023 08:01:46 -0800 Subject: [PATCH 2/7] Implement initial LSAN support v2 Signed-off-by: Justin King --- make/autoconf/spec.gmk.in | 6 +- make/common/modules/LauncherCommon.gmk | 4 + make/data/asan/asan_default_options.c | 10 +- make/data/asan/asan_default_options.cpp | 2 +- make/data/lsan/lsan_default_options.c | 66 +++++++++++ make/data/lsan/lsan_default_options.cpp | 30 +++++ make/hotspot/lib/CompileGtest.gmk | 4 + make/test/JtregNativeHotspot.gmk | 8 +- make/test/JtregNativeJdk.gmk | 8 +- src/hotspot/share/c1/c1_Compiler.cpp | 4 +- src/hotspot/share/code/codeCache.cpp | 7 +- src/hotspot/share/code/compiledIC.cpp | 6 +- src/hotspot/share/lsan/lsan.hpp | 104 ------------------ src/hotspot/share/memory/heap.cpp | 18 +-- src/hotspot/share/memory/heap.hpp | 2 - .../memory/metaspace/virtualSpaceNode.cpp | 16 +-- src/hotspot/share/memory/universe.cpp | 6 +- src/hotspot/share/prims/methodHandles.cpp | 4 +- src/hotspot/share/runtime/arguments.cpp | 35 +++--- src/hotspot/share/runtime/init.cpp | 6 +- src/hotspot/share/runtime/java.cpp | 19 ++-- src/hotspot/share/runtime/nonJavaThread.cpp | 4 +- src/hotspot/share/sanitizers/leak.hpp | 93 ++++++++++++++++ .../share/utilities/globalDefinitions.hpp | 4 - .../share/utilities/globalDefinitions_gcc.hpp | 2 - src/hotspot/share/utilities/macros.hpp | 8 -- src/java.base/share/native/launcher/main.c | 9 -- test/lib/jdk/test/lib/Platform.java | 6 - 28 files changed, 279 insertions(+), 212 deletions(-) create mode 100644 make/data/lsan/lsan_default_options.c create mode 100644 make/data/lsan/lsan_default_options.cpp delete mode 100644 src/hotspot/share/lsan/lsan.hpp create mode 100644 src/hotspot/share/sanitizers/leak.hpp diff --git a/make/autoconf/spec.gmk.in b/make/autoconf/spec.gmk.in index ab1706aa11809..ff073c78c9279 100644 --- a/make/autoconf/spec.gmk.in +++ b/make/autoconf/spec.gmk.in @@ -448,14 +448,14 @@ JCOV_FILTERS=@JCOV_FILTERS@ # AddressSanitizer ASAN_ENABLED:=@ASAN_ENABLED@ +# LeakSanitizer +LSAN_ENABLED:=@LSAN_ENABLED@ + # UndefinedBehaviorSanitizer UBSAN_ENABLED:=@UBSAN_ENABLED@ UBSAN_CFLAGS:=@UBSAN_CFLAGS@ UBSAN_LDFLAGS:=@UBSAN_LDFLAGS@ -# LeakSanitizer -LSAN_ENABLED:=@LSAN_ENABLED@ - # Necessary additional compiler flags to compile X11 X_CFLAGS:=@X_CFLAGS@ X_LIBS:=@X_LIBS@ 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..7ff4d5d0780eb 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 @@ -52,6 +52,12 @@ ATTRIBUTE_DEFAULT_VISIBILITY ATTRIBUTE_USED const char* __asan_default_options() return #ifndef LEAK_SANITIZER "detect_leaks=0," +#else + "leak_check_at_exit=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..a88967817067c --- /dev/null +++ b/make/data/lsan/lsan_default_options.c @@ -0,0 +1,66 @@ +/* + * 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 cleaning 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"; +} 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 070404b0925ce..dd5ab9c3cc0d5 100644 --- a/src/hotspot/share/c1/c1_Compiler.cpp +++ b/src/hotspot/share/c1/c1_Compiler.cpp @@ -40,9 +40,9 @@ #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" -#include "lsan/lsan.hpp" Compiler::Compiler() : AbstractCompiler(compiler_c1) { @@ -52,7 +52,7 @@ 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_leak(arena); + LSAN_IGNORE_OBJECT(arena); Runtime1::initialize(buffer_blob); FrameMap::initialize(); // initialize data structures diff --git a/src/hotspot/share/code/codeCache.cpp b/src/hotspot/share/code/codeCache.cpp index 2ae3ec6d782b1..97f7260308fb9 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" @@ -75,7 +76,6 @@ #include "opto/compile.hpp" #include "opto/node.hpp" #endif -#include "lsan/lsan.hpp" // Helper class for printing in CodeCache class CodeBlob_sizes { @@ -330,7 +330,8 @@ void CodeCache::initialize_heaps() { ReservedSpace non_method_space = rest.first_part(non_nmethod_size); ReservedSpace non_profiled_space = rest.last_part(non_nmethod_size); - Lsan::register_root_region(rs.base(), rs.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); @@ -1194,6 +1195,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 54e27c082893d..c19ee182d5186 100644 --- a/src/hotspot/share/code/compiledIC.cpp +++ b/src/hotspot/share/code/compiledIC.cpp @@ -45,8 +45,8 @@ #include "runtime/safepoint.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/stubRoutines.hpp" +#include "sanitizers/leak.hpp" #include "utilities/events.hpp" -#include "lsan/lsan.hpp" // Every time a compiled IC is changed or its type is being accessed, @@ -275,7 +275,7 @@ bool CompiledIC::set_to_megamorphic(CallInfo* call_info, Bytecodes::Code bytecod } // 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_leak(holder); + 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. @@ -448,7 +448,7 @@ bool CompiledIC::set_to_monomorphic(CompiledICInfo& info) { } // 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_leak(holder); + 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/lsan/lsan.hpp b/src/hotspot/share/lsan/lsan.hpp deleted file mode 100644 index 73893bc894777..0000000000000 --- a/src/hotspot/share/lsan/lsan.hpp +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2022, Oracle 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_LSAN_LSAN_HPP -#define SHARE_LSAN_LSAN_HPP - -#include "memory/allStatic.hpp" -#include "utilities/globalDefinitions.hpp" - -#include - -#ifdef LEAK_SANITIZER -#include -#define NO_SANITIE_LEAK __attribute__((no_sanitize("leak"))) -#else -#define NO_SANITIE_LEAK -#endif - -// Class-based namespace enclosing methods for interacting with LSan. This -// interface is always available regardless of whether LSan is available or not -// in the build. If LSan is not available, all methods are NOOP and will be -// compiled out. This approach makes maintaining code related to LSan easier as -// the code is always compiled, removing the chance of accidental breakages. -class Lsan final : AllStatic { - public: - // Returns true IIF LSan is enabled and available. - static ATTRIBUTE_ARTIFICIAL ALWAYSINLINE bool enabled() { -#ifdef LEAK_SANITIZER - return true; -#else - return false; -#endif - } - - // Perform a leak check. If any leaks are detected the program immediatley - // exits with a non-zero code. - static ATTRIBUTE_ARTIFICIAL ALWAYSINLINE void do_leak_check() { -#ifdef LEAK_SANITIZER - __lsan_do_leak_check(); -#endif - } - - // Returns true IFF leaks were detected. - static ATTRIBUTE_ARTIFICIAL ALWAYSINLINE bool do_recoverable_leak_check() { -#ifdef LEAK_SANITIZER - return __lsan_do_recoverable_leak_check() != 0; -#else - return false; -#endif - } - - // Registers a region of memory that may contain pointers to malloc-based - // memory. This only needs to be done for manually mapped memory via mmap. - static ATTRIBUTE_ARTIFICIAL ALWAYSINLINE void register_root_region(const void* ptr, size_t n) { -#ifdef LEAK_SANITIZER - __lsan_register_root_region(ptr, n); -#else - static_cast(ptr); - static_cast(n); -#endif - } - - // Unregisters a previously registered region of memory. - static ATTRIBUTE_ARTIFICIAL ALWAYSINLINE void unregister_root_region(const void* ptr, size_t n) { -#ifdef LEAK_SANITIZER - __lsan_unregister_root_region(ptr, n); -#else - static_cast(ptr); - static_cast(n); -#endif - } - - // Ignore any leak related to the memory pointed to by `ptr`. - template - static ATTRIBUTE_ARTIFICIAL ALWAYSINLINE T* ignore_leak(T* ptr) { -#ifdef LEAK_SANITIZER - __lsan_ignore_object(ptr); -#endif - return ptr; - } -}; - -#endif // SHARE_LSAN_LSAN_HPP diff --git a/src/hotspot/share/memory/heap.cpp b/src/hotspot/share/memory/heap.cpp index e218ea177945a..b71615d563bec 100644 --- a/src/hotspot/share/memory/heap.cpp +++ b/src/hotspot/share/memory/heap.cpp @@ -30,7 +30,6 @@ #include "services/memTracker.hpp" #include "utilities/align.hpp" #include "utilities/powerOfTwo.hpp" -#include "lsan/lsan.hpp" // Implementation of Heap @@ -54,11 +53,6 @@ CodeHeap::CodeHeap(const char* name, const CodeBlobType code_blob_type) _fragmentation_count = 0; } -CodeHeap::~CodeHeap() { - // Unregister memory region related to Code Cache. - Lsan::unregister_root_region(_memory.low_boundary(), _memory.reserved_size()); -} - // Dummy initialization of template array. char CodeHeap::segmap_template[] = {0}; @@ -224,10 +218,6 @@ bool CodeHeap::reserve(ReservedSpace rs, size_t committed_size, size_t segment_s return false; } - // Register memory region related to Code Cache. It sometimes contains immediate values which are - // pointers to malloc memory. - Lsan::register_root_region(_memory.low_boundary(), _memory.reserved_size()); - on_code_mapping(_memory.low(), _memory.committed_size()); _number_of_committed_segments = size_to_segments(_memory.committed_size()); _number_of_reserved_segments = size_to_segments(_memory.reserved_size()); @@ -266,13 +256,7 @@ bool CodeHeap::expand_by(size_t size) { dm = _memory.uncommitted_size(); } char* base = _memory.low() + _memory.committed_size(); - // Unregister and register memory region related to Code Cache. - Lsan::unregister_root_region(_memory.low_boundary(), _memory.reserved_size()); - if (!_memory.expand_by(dm)) { - Lsan::register_root_region(_memory.low_boundary(), _memory.reserved_size()); - return false; - } - Lsan::register_root_region(_memory.low_boundary(), _memory.reserved_size()); + if (!_memory.expand_by(dm)) return false; on_code_mapping(base, dm); size_t i = _number_of_committed_segments; _number_of_committed_segments = size_to_segments(_memory.committed_size()); diff --git a/src/hotspot/share/memory/heap.hpp b/src/hotspot/share/memory/heap.hpp index f81bdd75093fd..4b25d3bdba001 100644 --- a/src/hotspot/share/memory/heap.hpp +++ b/src/hotspot/share/memory/heap.hpp @@ -148,8 +148,6 @@ class CodeHeap : public CHeapObj { public: CodeHeap(const char* name, const CodeBlobType code_blob_type); - ~CodeHeap(); - // Heap extents bool reserve(ReservedSpace rs, size_t committed_size, size_t segment_size); bool expand_by(size_t size); // expands committed memory by size diff --git a/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp b/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp index 59f03960ed19b..5affaae2572de 100644 --- a/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp +++ b/src/hotspot/share/memory/metaspace/virtualSpaceNode.cpp @@ -43,12 +43,12 @@ #include "runtime/mutexLocker.hpp" #include "runtime/os.hpp" #include "sanitizers/address.h" +#include "sanitizers/leak.hpp" #include "services/memTracker.hpp" #include "utilities/align.hpp" #include "utilities/debug.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/ostream.hpp" -#include "lsan/lsan.hpp" namespace metaspace { @@ -234,16 +234,16 @@ VirtualSpaceNode::VirtualSpaceNode(ReservedSpace rs, bool owns_rs, CommitLimiter // Update reserved counter in vslist _total_reserved_words_counter->increment_by(_word_size); - // Register memory region related to Metaspace. The Metaspace contains lots of pointers to malloc - // memory. - Lsan::register_root_region(rs.base(), rs.size()); - assert_is_aligned(_base, chunklevel::MAX_CHUNK_BYTE_SIZE); assert_is_aligned(_word_size, chunklevel::MAX_CHUNK_WORD_SIZE); // 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). @@ -275,15 +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."); - // Unregister memory region related to Metaspace. - Lsan::unregister_root_region(_rs.base(), _rs.size()); - if (_owns_rs) { _rs.release(); } diff --git a/src/hotspot/share/memory/universe.cpp b/src/hotspot/share/memory/universe.cpp index 6c791435be8ab..0d54563cf0a79 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" @@ -83,7 +84,6 @@ #include "utilities/macros.hpp" #include "utilities/ostream.hpp" #include "utilities/preserveException.hpp" -#include "lsan/lsan.hpp" // Known objects Klass* Universe::_typeArrayKlassObjs[T_LONG+1] = { NULL /*, NULL...*/ }; @@ -752,9 +752,11 @@ bool Universe::contains_non_oop_word(void* p) { } static void initialize_global_behaviours() { + DefaultICProtectionBehaviour* protection_behavior = new DefaultICProtectionBehaviour(); // Ignore leak of DefaultICProtectionBehaviour. It is overriden by some GC implementations and the // pointer is leaked once. - CompiledICProtectionBehaviour::set_current(Lsan::ignore_leak(new DefaultICProtectionBehaviour())); + 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 1f25be35b0de5..129b3aeee5a87 100644 --- a/src/hotspot/share/prims/methodHandles.cpp +++ b/src/hotspot/share/prims/methodHandles.cpp @@ -59,8 +59,8 @@ #include "runtime/safepointVerifiers.hpp" #include "runtime/signature.hpp" #include "runtime/stubRoutines.hpp" +#include "sanitizers/leak.hpp" #include "utilities/exceptions.hpp" -#include "lsan/lsan.hpp" /* @@ -985,7 +985,7 @@ 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_leak(qname); + 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/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index ccb42deacd79a..79e27c81792b5 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -64,7 +64,6 @@ #include "utilities/parseInteger.hpp" #include "utilities/powerOfTwo.hpp" #include "utilities/stringUtils.hpp" -#include "lsan/lsan.hpp" #if INCLUDE_JFR #include "jfr/jfr.hpp" #endif @@ -428,12 +427,6 @@ void Arguments::init_system_properties() { PropertyList_add(&_system_properties, _jdk_boot_class_path_append); PropertyList_add(&_system_properties, _vm_info); - // Add sanitizer properties. - if (Lsan::enabled()) { - PropertyList_add(&_system_properties, - new SystemProperty("jdk.sanitizer.leak.enabled", "true", false)); - } - // Set OS specific system properties values os::init_system_properties_values(); } @@ -3979,21 +3972,21 @@ jint Arguments::parse(const JavaVMInitArgs* initial_cmd_args) { no_shared_spaces("CDS Disabled"); #endif // INCLUDE_CDS - LSAN_ONLY( +#ifdef LEAK_SANITIZER #ifdef _LP64 - // LSAN relies on pointers to be natively aligned. Using compressed class pointers breaks this - // expectation and results in nondeterministic leak reports. - if (FLAG_SET_CMDLINE(UseCompressedOops, false) != JVMFlag::SUCCESS) { - return JNI_EINVAL; - } - if (FLAG_SET_CMDLINE(UseCompressedClassPointers, false) != JVMFlag::SUCCESS) { - return JNI_EINVAL; - } -#endif // _LP64 - // During testing CDS appeared to cause LSan to report many false positives, so disable it. - UseSharedSpaces = false; - RequireSharedSpaces = false; - ); + // LSAN relies on pointers to be natively aligned. Using compressed class pointers breaks this + // expectation and results in nondeterministic leak reports. + if (FLAG_SET_CMDLINE(UseCompressedOops, false) != JVMFlag::SUCCESS) { + return JNI_EINVAL; + } + if (FLAG_SET_CMDLINE(UseCompressedClassPointers, false) != JVMFlag::SUCCESS) { + return JNI_EINVAL; + } +#endif // _LP64 + // During testing CDS appeared to cause LSan to report many false positives, so disable it. + UseSharedSpaces = false; + RequireSharedSpaces = false; +#endif // LEAK_SANITIZER // Verify NMT arguments const NMT_TrackingLevel lvl = NMTUtil::parse_tracking_level(NativeMemoryTracking); diff --git a/src/hotspot/share/runtime/init.cpp b/src/hotspot/share/runtime/init.cpp index c0b352fe82fdf..d915f93284d0e 100644 --- a/src/hotspot/share/runtime/init.cpp +++ b/src/hotspot/share/runtime/init.cpp @@ -29,7 +29,6 @@ #include "compiler/compiler_globals.hpp" #include "gc/shared/collectedHeap.hpp" #include "gc/shared/gcHeapSummary.hpp" -#include "lsan/lsan.hpp" #include "interpreter/bytecodes.hpp" #include "logging/logAsyncWriter.hpp" #include "memory/universe.hpp" @@ -45,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 @@ -128,7 +128,7 @@ jint init_globals() { { // Register the Java heap with LSan. VirtualSpaceSummary summary = Universe::heap()->create_heap_space_summary(); - Lsan::register_root_region(summary.start(), summary.reserved_size()); + LSAN_REGISTER_ROOT_REGION(summary.start(), summary.reserved_size()); } AsyncLogWriter::initialize(); @@ -194,7 +194,7 @@ void exit_globals() { { // Unregister the Java heap with LSan. VirtualSpaceSummary summary = Universe::heap()->create_heap_space_summary(); - Lsan::unregister_root_region(summary.start(), summary.reserved_size()); + LSAN_UNREGISTER_ROOT_REGION(summary.start(), summary.reserved_size()); } } } diff --git a/src/hotspot/share/runtime/java.cpp b/src/hotspot/share/runtime/java.cpp index d1fdc22e1164c..aff50ab5a8a99 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" @@ -93,7 +94,6 @@ #if INCLUDE_JVMCI #include "jvmci/jvmci.hpp" #endif -#include "lsan/lsan.hpp" GrowableArray* collected_profiled_methods; @@ -417,12 +417,17 @@ void before_exit(JavaThread* thread, bool halt) { } } - // If we are built with LSan, not halting, and there is no JVM error perform a full GC and then - // perform leak checking. We do this as early as possible during the shutdown sequence as we are - // not interested in leaks introduced during shutdown. - if (Lsan::enabled() && !halt && !VMError::is_error_reported()) { - Universe::heap()->collect(GCCause::_java_lang_system_gc); - Lsan::do_leak_check(); + // 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 { + int result = LSAN_DO_RECOVERABLE_LEAK_CHECK(); + // Ignore the return value. + static_cast(result); } #if INCLUDE_JVMCI diff --git a/src/hotspot/share/runtime/nonJavaThread.cpp b/src/hotspot/share/runtime/nonJavaThread.cpp index d1db6c139af19..93c12739e44b6 100644 --- a/src/hotspot/share/runtime/nonJavaThread.cpp +++ b/src/hotspot/share/runtime/nonJavaThread.cpp @@ -33,13 +33,13 @@ #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" #if INCLUDE_JFR #include "jfr/jfr.hpp" #endif -#include "lsan/lsan.hpp" // List of all NonJavaThreads and safe iteration over that list. @@ -286,7 +286,7 @@ void WatcherThread::run() { // Signal that it is terminated { MutexLocker mu(Terminator_lock, Mutex::_no_safepoint_check_flag); - Lsan::ignore_leak(_watcher_thread); + 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 diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp index c131c668c5c49..899aa45aea158 100644 --- a/src/hotspot/share/utilities/globalDefinitions.hpp +++ b/src/hotspot/share/utilities/globalDefinitions.hpp @@ -56,10 +56,6 @@ class oopDesc; #define ATTRIBUTE_FLATTEN #endif -#ifndef ATTRIBUTE_ARTIFICIAL -#define ATTRIBUTE_ARTIFICIAL -#endif - // These are #defines to selectively turn on/off the Print(Opto)Assembly // capabilities. Choices should be led by a tradeoff between // code size and improved supportability. diff --git a/src/hotspot/share/utilities/globalDefinitions_gcc.hpp b/src/hotspot/share/utilities/globalDefinitions_gcc.hpp index 03a058defb88a..3da670ef47659 100644 --- a/src/hotspot/share/utilities/globalDefinitions_gcc.hpp +++ b/src/hotspot/share/utilities/globalDefinitions_gcc.hpp @@ -164,6 +164,4 @@ inline int g_isfinite(jdouble f) { return isfinite(f); } // #define ATTRIBUTE_ALIGNED(x) __attribute__((aligned(x))) -#define ATTRIBUTE_ARTIFICIAL __attribute__((artificial)) - #endif // SHARE_UTILITIES_GLOBALDEFINITIONS_GCC_HPP diff --git a/src/hotspot/share/utilities/macros.hpp b/src/hotspot/share/utilities/macros.hpp index a4e5d9e9eb91a..bfbaaa58acc2c 100644 --- a/src/hotspot/share/utilities/macros.hpp +++ b/src/hotspot/share/utilities/macros.hpp @@ -265,14 +265,6 @@ #define NOT_JFR_RETURN_(code) { return code; } #endif -#ifdef LEAK_SANITIZER -#define LSAN_ONLY(code) code -#define NOT_LSAN(code) -#else -#define LSAN_ONLY(code) -#define NOT_LSAN(code) code -#endif - #ifndef INCLUDE_JVMCI #define INCLUDE_JVMCI 1 #endif diff --git a/src/java.base/share/native/launcher/main.c b/src/java.base/share/native/launcher/main.c index 6250fe9985dc3..d3898d7adc634 100644 --- a/src/java.base/share/native/launcher/main.c +++ b/src/java.base/share/native/launcher/main.c @@ -34,15 +34,6 @@ #include "jli_util.h" #include "jni.h" -#ifdef LEAK_SANITIZER -// 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 disable implicit leak checks -// during exit and instead invoke leak checking early during JVM shutdown. -JNIEXPORT const char* __lsan_default_options() { - return "leak_check_at_exit=0"; -} -#endif - /* * Entry point. */ diff --git a/test/lib/jdk/test/lib/Platform.java b/test/lib/jdk/test/lib/Platform.java index 89ca171e8c38e..647c62834aa5c 100644 --- a/test/lib/jdk/test/lib/Platform.java +++ b/test/lib/jdk/test/lib/Platform.java @@ -50,8 +50,6 @@ public class Platform { private static final String compiler = privilegedGetProperty("sun.management.compiler"); private static final String testJdk = privilegedGetProperty("test.jdk"); - private static final boolean leakSanitizer = Boolean.parseBoolean(privilegedGetProperty("jdk.sanitizer.leak.enabled")); - @SuppressWarnings("removal") private static String privilegedGetProperty(String key) { return AccessController.doPrivileged(( @@ -421,8 +419,4 @@ public static boolean isDefaultCDSArchiveSupported() { public static boolean areCustomLoadersSupportedForCDS() { return (is64bit() && (isLinux() || isOSX() || isWindows())); } - - public static boolean hasLeakSanitizer() { - return leakSanitizer; - } } From 4559ef4c3053e147f17933034742e893e36a857f Mon Sep 17 00:00:00 2001 From: Justin King Date: Wed, 1 Feb 2023 07:20:03 -0800 Subject: [PATCH 3/7] Support CDS Signed-off-by: Justin King --- src/hotspot/share/cds/metaspaceShared.cpp | 3 +++ src/hotspot/share/runtime/arguments.cpp | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) 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/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 79e27c81792b5..63bcaed527ede 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -3983,9 +3983,6 @@ jint Arguments::parse(const JavaVMInitArgs* initial_cmd_args) { return JNI_EINVAL; } #endif // _LP64 - // During testing CDS appeared to cause LSan to report many false positives, so disable it. - UseSharedSpaces = false; - RequireSharedSpaces = false; #endif // LEAK_SANITIZER // Verify NMT arguments From e83f96cb4c840be7147eb977336f7a1c0c64b905 Mon Sep 17 00:00:00 2001 From: Justin King Date: Thu, 2 Feb 2023 07:45:40 -0800 Subject: [PATCH 4/7] Update based on review and remove restriction on compressed oops and class pointers Signed-off-by: Justin King --- src/hotspot/share/runtime/arguments.cpp | 13 ------------- src/hotspot/share/runtime/init.cpp | 4 ++++ src/hotspot/share/runtime/java.cpp | 15 +++++++++++++-- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/hotspot/share/runtime/arguments.cpp b/src/hotspot/share/runtime/arguments.cpp index 63bcaed527ede..0323bb1397d46 100644 --- a/src/hotspot/share/runtime/arguments.cpp +++ b/src/hotspot/share/runtime/arguments.cpp @@ -3972,19 +3972,6 @@ jint Arguments::parse(const JavaVMInitArgs* initial_cmd_args) { no_shared_spaces("CDS Disabled"); #endif // INCLUDE_CDS -#ifdef LEAK_SANITIZER -#ifdef _LP64 - // LSAN relies on pointers to be natively aligned. Using compressed class pointers breaks this - // expectation and results in nondeterministic leak reports. - if (FLAG_SET_CMDLINE(UseCompressedOops, false) != JVMFlag::SUCCESS) { - return JNI_EINVAL; - } - if (FLAG_SET_CMDLINE(UseCompressedClassPointers, false) != JVMFlag::SUCCESS) { - return JNI_EINVAL; - } -#endif // _LP64 -#endif // LEAK_SANITIZER - // Verify NMT arguments const NMT_TrackingLevel lvl = NMTUtil::parse_tracking_level(NativeMemoryTracking); if (lvl == NMT_unknown) { diff --git a/src/hotspot/share/runtime/init.cpp b/src/hotspot/share/runtime/init.cpp index d915f93284d0e..3507649fb537d 100644 --- a/src/hotspot/share/runtime/init.cpp +++ b/src/hotspot/share/runtime/init.cpp @@ -125,11 +125,13 @@ 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 @@ -191,11 +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 aff50ab5a8a99..44e587a6857ac 100644 --- a/src/hotspot/share/runtime/java.cpp +++ b/src/hotspot/share/runtime/java.cpp @@ -417,6 +417,15 @@ 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 @@ -425,10 +434,12 @@ void before_exit(JavaThread* thread, bool halt) { if (!halt && !VMError::is_error_reported()) { LSAN_DO_LEAK_CHECK(); } else { - int result = LSAN_DO_RECOVERABLE_LEAK_CHECK(); // Ignore the return value. - static_cast(result); + static_cast(LSAN_DO_RECOVERABLE_LEAK_CHECK()); } +#endif + + // Actual shutdown logic begins here. #if INCLUDE_JVMCI if (EnableJVMCI) { From 93af227a5231dc848bc50f3c213c85b2e7acf835 Mon Sep 17 00:00:00 2001 From: Justin King Date: Thu, 2 Feb 2023 07:47:37 -0800 Subject: [PATCH 5/7] Fix grammatical error Signed-off-by: Justin King --- make/data/lsan/lsan_default_options.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/make/data/lsan/lsan_default_options.c b/make/data/lsan/lsan_default_options.c index a54806b9f3c32..9b86eaa755adc 100644 --- a/make/data/lsan/lsan_default_options.c +++ b/make/data/lsan/lsan_default_options.c @@ -46,7 +46,7 @@ // 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 cleaning at the moment and some leaks occur, we want to ignore these. Instead we +// 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 From 55b8381a391e3902d721aeadec6719f23a63e304 Mon Sep 17 00:00:00 2001 From: Justin King Date: Tue, 7 Feb 2023 07:31:30 -0800 Subject: [PATCH 6/7] Update based on review Signed-off-by: Justin King --- make/data/asan/asan_default_options.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/make/data/asan/asan_default_options.c b/make/data/asan/asan_default_options.c index 7ff4d5d0780eb..444bcbf7a20bc 100644 --- a/make/data/asan/asan_default_options.c +++ b/make/data/asan/asan_default_options.c @@ -50,10 +50,12 @@ // 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 - "detect_leaks=0," -#else +#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 "print_suppressions=0," "handle_segv=0," From 5c79bef55643fda302c62a3bf70721ca018bd62d Mon Sep 17 00:00:00 2001 From: Justin King Date: Tue, 7 Feb 2023 07:34:03 -0800 Subject: [PATCH 7/7] Revert changes to JDK Signed-off-by: Justin King --- test/jdk/jni/nullCaller/exeNullCallerTest.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/jdk/jni/nullCaller/exeNullCallerTest.cpp b/test/jdk/jni/nullCaller/exeNullCallerTest.cpp index 3985b8a4f1384..fef6818d66c22 100644 --- a/test/jdk/jni/nullCaller/exeNullCallerTest.cpp +++ b/test/jdk/jni/nullCaller/exeNullCallerTest.cpp @@ -52,11 +52,9 @@ void getBundle(JNIEnv* env) { // msg = b.getString("message"); jstring msg = (jstring) m_ResourceBundle_getString.callReturnNotNull(b, env->NewStringUTF("message")); - const char* chars = env->GetStringUTFChars(msg, NULL); - if (std::string("Hello!") != chars) { + if (std::string("Hello!") != env->GetStringUTFChars(msg, NULL)) { emitErrorMessageAndExit("Bundle didn't contain expected content"); } - env->ReleaseStringUTFChars(msg, chars); // ResourceBundle.clearCache() m_ResourceBundle_clearCache.callVoidMethod();