Skip to content

Commit 1dc6a38

Browse files
committedJan 10, 2023
8277822: Remove debug-only heap overrun checks in os::malloc and friends
Reviewed-by: phh Backport-of: 39b1d75f25ff2cc348f8b69d4e280847c6843ae2
1 parent c370dec commit 1dc6a38

File tree

6 files changed

+87
-153
lines changed

6 files changed

+87
-153
lines changed
 

‎src/hotspot/share/runtime/globals.hpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -545,7 +545,7 @@ const intx ObjectAlignmentInBytes = 8;
545545
"compression. Otherwise the level must be between 1 and 9.") \
546546
range(0, 9) \
547547
\
548-
product(ccstr, NativeMemoryTracking, "off", \
548+
product(ccstr, NativeMemoryTracking, DEBUG_ONLY("summary") NOT_DEBUG("off"), \
549549
"Native memory tracking options") \
550550
\
551551
product(bool, PrintNMTStatistics, false, DIAGNOSTIC, \

‎src/hotspot/share/runtime/os.cpp

+68-133
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
#include "logging/log.hpp"
3939
#include "logging/logStream.hpp"
4040
#include "memory/allocation.inline.hpp"
41-
#include "memory/guardedMemory.hpp"
4241
#include "memory/resourceArea.hpp"
4342
#include "memory/universe.hpp"
4443
#include "oops/compressedOops.inline.hpp"
@@ -83,13 +82,6 @@ int os::_processor_count = 0;
8382
int os::_initial_active_processor_count = 0;
8483
os::PageSizes os::_page_sizes;
8584

86-
#ifndef PRODUCT
87-
julong os::num_mallocs = 0; // # of calls to malloc/realloc
88-
julong os::alloc_bytes = 0; // # of bytes allocated
89-
julong os::num_frees = 0; // # of calls to free
90-
julong os::free_bytes = 0; // # of bytes freed
91-
#endif
92-
9385
static size_t cur_malloc_words = 0; // current size for MallocMaxTestWords
9486

9587
DEBUG_ONLY(bool os::_mutex_init_done = false;)
@@ -636,30 +628,11 @@ char* os::strdup_check_oom(const char* str, MEMFLAGS flags) {
636628
return p;
637629
}
638630

639-
640-
#define paranoid 0 /* only set to 1 if you suspect checking code has bug */
641-
642-
#ifdef ASSERT
643-
644-
static void verify_memory(void* ptr) {
645-
GuardedMemory guarded(ptr);
646-
if (!guarded.verify_guards()) {
647-
LogTarget(Warning, malloc, free) lt;
648-
ResourceMark rm;
649-
LogStream ls(lt);
650-
ls.print_cr("## nof_mallocs = " UINT64_FORMAT ", nof_frees = " UINT64_FORMAT, os::num_mallocs, os::num_frees);
651-
ls.print_cr("## memory stomp:");
652-
guarded.print_on(&ls);
653-
fatal("memory stomping error");
654-
}
655-
}
656-
657-
#endif
658-
659631
//
660632
// This function supports testing of the malloc out of memory
661633
// condition without really running the system out of memory.
662634
//
635+
663636
static bool has_reached_max_malloc_test_peak(size_t alloc_size) {
664637
if (MallocMaxTestWords > 0) {
665638
size_t words = (alloc_size / BytesPerWord);
@@ -672,13 +645,24 @@ static bool has_reached_max_malloc_test_peak(size_t alloc_size) {
672645
return false;
673646
}
674647

648+
#ifdef ASSERT
649+
static void check_crash_protection() {
650+
assert(!os::ThreadCrashProtection::is_crash_protected(Thread::current_or_null()),
651+
"not allowed when crash protection is set");
652+
}
653+
static void break_if_ptr_caught(void* ptr) {
654+
if (p2i(ptr) == (intptr_t)MallocCatchPtr) {
655+
log_warning(malloc, free)("ptr caught: " PTR_FORMAT, p2i(ptr));
656+
breakpoint();
657+
}
658+
}
659+
#endif // ASSERT
660+
675661
void* os::malloc(size_t size, MEMFLAGS flags) {
676662
return os::malloc(size, flags, CALLER_PC);
677663
}
678664

679665
void* os::malloc(size_t size, MEMFLAGS memflags, const NativeCallStack& stack) {
680-
NOT_PRODUCT(inc_stat_counter(&num_mallocs, 1));
681-
NOT_PRODUCT(inc_stat_counter(&alloc_bytes, size));
682666

683667
#if INCLUDE_NMT
684668
{
@@ -689,63 +673,40 @@ void* os::malloc(size_t size, MEMFLAGS memflags, const NativeCallStack& stack) {
689673
}
690674
#endif
691675

692-
// Since os::malloc can be called when the libjvm.{dll,so} is
693-
// first loaded and we don't have a thread yet we must accept NULL also here.
694-
assert(!os::ThreadCrashProtection::is_crash_protected(Thread::current_or_null()),
695-
"malloc() not allowed when crash protection is set");
676+
DEBUG_ONLY(check_crash_protection());
696677

697-
if (size == 0) {
698-
// return a valid pointer if size is zero
699-
// if NULL is returned the calling functions assume out of memory.
700-
size = 1;
678+
// On malloc(0), implementators of malloc(3) have the choice to return either
679+
// NULL or a unique non-NULL pointer. To unify libc behavior across our platforms
680+
// we chose the latter.
681+
size = MAX2((size_t)1, size);
682+
683+
// For the test flag -XX:MallocMaxTestWords
684+
if (has_reached_max_malloc_test_peak(size)) {
685+
return NULL;
701686
}
702687

703-
// NMT support
704-
NMT_TrackingLevel level = MemTracker::tracking_level();
688+
const NMT_TrackingLevel level = MemTracker::tracking_level();
705689
const size_t nmt_overhead =
706690
MemTracker::malloc_header_size(level) + MemTracker::malloc_footer_size(level);
707691

708-
// Check for overflow.
709-
if (size + nmt_overhead < size) {
710-
return NULL;
711-
}
692+
const size_t outer_size = size + nmt_overhead;
712693

713-
#ifndef ASSERT
714-
const size_t alloc_size = size + nmt_overhead;
715-
#else
716-
const size_t alloc_size = GuardedMemory::get_total_size(size + nmt_overhead);
717-
if (size + nmt_overhead > alloc_size) { // Check for rollover.
694+
// Check for overflow.
695+
if (outer_size < size) {
718696
return NULL;
719697
}
720-
#endif
721698

722-
// For the test flag -XX:MallocMaxTestWords
723-
if (has_reached_max_malloc_test_peak(size)) {
699+
void* const outer_ptr = (u_char*)::malloc(outer_size);
700+
if (outer_ptr == NULL) {
724701
return NULL;
725702
}
726703

727-
u_char* ptr;
728-
ptr = (u_char*)::malloc(alloc_size);
729-
730-
#ifdef ASSERT
731-
if (ptr == NULL) {
732-
return NULL;
733-
}
734-
// Wrap memory with guard
735-
GuardedMemory guarded(ptr, size + nmt_overhead);
736-
ptr = guarded.get_user_ptr();
704+
void* inner_ptr = MemTracker::record_malloc((address)outer_ptr, size, memflags, stack, level);
737705

738-
if ((intptr_t)ptr == (intptr_t)MallocCatchPtr) {
739-
log_warning(malloc, free)("os::malloc caught, " SIZE_FORMAT " bytes --> " PTR_FORMAT, size, p2i(ptr));
740-
breakpoint();
741-
}
742-
if (paranoid) {
743-
verify_memory(ptr);
744-
}
745-
#endif
706+
DEBUG_ONLY(::memset(inner_ptr, uninitBlockPad, size);)
707+
DEBUG_ONLY(break_if_ptr_caught(inner_ptr);)
746708

747-
// we do not track guard memory
748-
return MemTracker::record_malloc((address)ptr, size, memflags, stack, level);
709+
return inner_ptr;
749710
}
750711

751712
void* os::realloc(void *memblock, size_t size, MEMFLAGS flags) {
@@ -763,59 +724,41 @@ void* os::realloc(void *memblock, size_t size, MEMFLAGS memflags, const NativeCa
763724
}
764725
#endif
765726

727+
if (memblock == NULL) {
728+
return os::malloc(size, memflags, stack);
729+
}
730+
731+
DEBUG_ONLY(check_crash_protection());
732+
733+
// On realloc(p, 0), implementators of realloc(3) have the choice to return either
734+
// NULL or a unique non-NULL pointer. To unify libc behavior across our platforms
735+
// we chose the latter.
736+
size = MAX2((size_t)1, size);
737+
766738
// For the test flag -XX:MallocMaxTestWords
767739
if (has_reached_max_malloc_test_peak(size)) {
768740
return NULL;
769741
}
770742

771-
if (size == 0) {
772-
// return a valid pointer if size is zero
773-
// if NULL is returned the calling functions assume out of memory.
774-
size = 1;
775-
}
776-
777-
#ifndef ASSERT
778-
NOT_PRODUCT(inc_stat_counter(&num_mallocs, 1));
779-
NOT_PRODUCT(inc_stat_counter(&alloc_bytes, size));
780-
// NMT support
781-
NMT_TrackingLevel level = MemTracker::tracking_level();
782-
void* membase = MemTracker::record_free(memblock, level);
743+
const NMT_TrackingLevel level = MemTracker::tracking_level();
783744
const size_t nmt_overhead =
784745
MemTracker::malloc_header_size(level) + MemTracker::malloc_footer_size(level);
785-
void* ptr = ::realloc(membase, size + nmt_overhead);
786-
return MemTracker::record_malloc(ptr, size, memflags, stack, level);
787-
#else
788-
if (memblock == NULL) {
789-
return os::malloc(size, memflags, stack);
790-
}
791-
if ((intptr_t)memblock == (intptr_t)MallocCatchPtr) {
792-
log_warning(malloc, free)("os::realloc caught " PTR_FORMAT, p2i(memblock));
793-
breakpoint();
794-
}
795-
// NMT support
796-
void* membase = MemTracker::malloc_base(memblock);
797-
verify_memory(membase);
798-
// always move the block
799-
void* ptr = os::malloc(size, memflags, stack);
800-
// Copy to new memory if malloc didn't fail
801-
if (ptr != NULL ) {
802-
GuardedMemory guarded(MemTracker::malloc_base(memblock));
803-
// Guard's user data contains NMT header
804-
NMT_TrackingLevel level = MemTracker::tracking_level();
805-
const size_t nmt_overhead =
806-
MemTracker::malloc_header_size(level) + MemTracker::malloc_footer_size(level);
807-
size_t memblock_size = guarded.get_user_size() - nmt_overhead;
808-
memcpy(ptr, memblock, MIN2(size, memblock_size));
809-
if (paranoid) {
810-
verify_memory(MemTracker::malloc_base(ptr));
811-
}
812-
os::free(memblock);
813-
}
814-
return ptr;
815-
#endif
746+
747+
const size_t new_outer_size = size + nmt_overhead;
748+
749+
// If NMT is enabled, this checks for heap overwrites, then de-accounts the old block.
750+
void* const old_outer_ptr = MemTracker::record_free(memblock, level);
751+
752+
void* const new_outer_ptr = ::realloc(old_outer_ptr, new_outer_size);
753+
754+
// If NMT is enabled, this checks for heap overwrites, then de-accounts the old block.
755+
void* const new_inner_ptr = MemTracker::record_malloc(new_outer_ptr, size, memflags, stack, level);
756+
757+
DEBUG_ONLY(break_if_ptr_caught(new_inner_ptr);)
758+
759+
return new_inner_ptr;
816760
}
817761

818-
// handles NULL pointers
819762
void os::free(void *memblock) {
820763

821764
#if INCLUDE_NMT
@@ -824,25 +767,17 @@ void os::free(void *memblock) {
824767
}
825768
#endif
826769

827-
NOT_PRODUCT(inc_stat_counter(&num_frees, 1));
828-
#ifdef ASSERT
829-
if (memblock == NULL) return;
830-
if ((intptr_t)memblock == (intptr_t)MallocCatchPtr) {
831-
log_warning(malloc, free)("os::free caught " PTR_FORMAT, p2i(memblock));
832-
breakpoint();
770+
if (memblock == NULL) {
771+
return;
833772
}
834-
void* membase = MemTracker::record_free(memblock, MemTracker::tracking_level());
835-
verify_memory(membase);
836773

837-
GuardedMemory guarded(membase);
838-
size_t size = guarded.get_user_size();
839-
inc_stat_counter(&free_bytes, size);
840-
membase = guarded.release_for_freeing();
841-
::free(membase);
842-
#else
843-
void* membase = MemTracker::record_free(memblock, MemTracker::tracking_level());
844-
::free(membase);
845-
#endif
774+
DEBUG_ONLY(break_if_ptr_caught(memblock);)
775+
776+
const NMT_TrackingLevel level = MemTracker::tracking_level();
777+
778+
// If NMT is enabled, this checks for heap overwrites, then de-accounts the old block.
779+
void* const old_outer_ptr = MemTracker::record_free(memblock, level);
780+
::free(old_outer_ptr);
846781
}
847782

848783
void os::init_random(unsigned int initval) {

‎src/hotspot/share/runtime/os.hpp

-7
Original file line numberDiff line numberDiff line change
@@ -789,13 +789,6 @@ class os: AllStatic {
789789
// Like strdup, but exit VM when strdup() returns NULL
790790
static char* strdup_check_oom(const char*, MEMFLAGS flags = mtInternal);
791791

792-
#ifndef PRODUCT
793-
static julong num_mallocs; // # of calls to malloc/realloc
794-
static julong alloc_bytes; // # of bytes allocated
795-
static julong num_frees; // # of calls to free
796-
static julong free_bytes; // # of bytes freed
797-
#endif
798-
799792
// SocketInterface (ex HPI SocketInterface )
800793
static int socket(int domain, int type, int protocol);
801794
static int socket_close(int fd);

‎test/hotspot/jtreg/gtest/NMTGtests.java

+10-7
Original file line numberDiff line numberDiff line change
@@ -24,26 +24,29 @@
2424
*/
2525

2626
/*
27-
* This tests NMT by running gtests with NMT enabled.
28-
*
29-
* To save time, we just run them for debug builds (where we would catch assertions) and only a selection of tests
30-
* (namely, NMT tests themselves, and - for the detail statistics - os tests, since those reserve a lot and stress NMT)
27+
* This tests NMT by running gtests with NMT enabled (only those which are relevant for NMT)
28+
*/
29+
30+
/* @test id=nmt-off
31+
* @summary Run NMT-related gtests with NMT switched off
32+
* @library /test/lib
33+
* @modules java.base/jdk.internal.misc
34+
* java.xml
35+
* @run main/native GTestWrapper --gtest_filter=NMT*:os* -XX:NativeMemoryTracking=off
3136
*/
3237

3338
/* @test id=nmt-summary
3439
* @summary Run NMT-related gtests with summary statistics
3540
* @library /test/lib
3641
* @modules java.base/jdk.internal.misc
3742
* java.xml
38-
* @requires vm.debug
39-
* @run main/native GTestWrapper --gtest_filter=NMT* -XX:NativeMemoryTracking=summary
43+
* @run main/native GTestWrapper --gtest_filter=NMT*:os* -XX:NativeMemoryTracking=summary
4044
*/
4145

4246
/* @test id=nmt-detail
4347
* @summary Run NMT-related gtests with detailed statistics
4448
* @library /test/lib
4549
* @modules java.base/jdk.internal.misc
4650
* java.xml
47-
* @requires vm.debug
4851
* @run main/native GTestWrapper --gtest_filter=NMT*:os* -XX:NativeMemoryTracking=detail
4952
*/

‎test/hotspot/jtreg/runtime/NMT/JcmdWithNMTDisabled.java

+7-4
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
* @run driver JcmdWithNMTDisabled 1
3131
*/
3232

33+
import jdk.test.lib.Platform;
3334
import jdk.test.lib.process.ProcessTools;
3435
import jdk.test.lib.process.OutputAnalyzer;
3536
import jdk.test.lib.JDKToolFinder;
@@ -47,10 +48,12 @@ public static void main(String args[]) throws Exception {
4748
OutputAnalyzer output;
4849
String testjdkPath = System.getProperty("test.jdk");
4950

50-
// First run without enabling NMT
51-
pb = ProcessTools.createJavaProcessBuilder("-Dtest.jdk=" + testjdkPath, "JcmdWithNMTDisabled");
52-
output = new OutputAnalyzer(pb.start());
53-
output.shouldHaveExitValue(0);
51+
// First run without enabling NMT (not in debug, where NMT is by default on)
52+
if (!Platform.isDebugBuild()) {
53+
pb = ProcessTools.createJavaProcessBuilder("-Dtest.jdk=" + testjdkPath, "JcmdWithNMTDisabled");
54+
output = new OutputAnalyzer(pb.start());
55+
output.shouldHaveExitValue(0);
56+
}
5457

5558
// Then run with explicitly disabling NMT, should not be any difference
5659
pb = ProcessTools.createJavaProcessBuilder("-Dtest.jdk=" + testjdkPath, "-XX:NativeMemoryTracking=off", "JcmdWithNMTDisabled");

‎test/hotspot/jtreg/runtime/NMT/PrintNMTStatisticsWithNMTDisabled.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public class PrintNMTStatisticsWithNMTDisabled {
3838
public static void main(String args[]) throws Exception {
3939
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
4040
"-XX:+UnlockDiagnosticVMOptions",
41-
"-XX:+PrintNMTStatistics",
41+
"-XX:+PrintNMTStatistics", "-XX:NativeMemoryTracking=off",
4242
"-version");
4343
OutputAnalyzer output = new OutputAnalyzer(pb.start());
4444
output.shouldContain("warning: PrintNMTStatistics is disabled, because native memory tracking is not enabled");

1 commit comments

Comments
 (1)

openjdk-notifier[bot] commented on Jan 10, 2023

@openjdk-notifier[bot]
Please sign in to comment.