Skip to content

Commit 39b1d75

Browse files
committed
8277822: Remove debug-only heap overrun checks in os::malloc and friends
Reviewed-by: coleenp, zgu
1 parent 5af7f25 commit 39b1d75

File tree

6 files changed

+85
-151
lines changed

6 files changed

+85
-151
lines changed

src/hotspot/share/runtime/globals.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,7 @@ const intx ObjectAlignmentInBytes = 8;
543543
"compression. Otherwise the level must be between 1 and 9.") \
544544
range(0, 9) \
545545
\
546-
product(ccstr, NativeMemoryTracking, "off", \
546+
product(ccstr, NativeMemoryTracking, DEBUG_ONLY("summary") NOT_DEBUG("off"), \
547547
"Native memory tracking options") \
548548
\
549549
product(bool, PrintNMTStatistics, false, DIAGNOSTIC, \

src/hotspot/share/runtime/os.cpp

Lines changed: 66 additions & 131 deletions
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;)
@@ -603,30 +595,11 @@ char* os::strdup_check_oom(const char* str, MEMFLAGS flags) {
603595
return p;
604596
}
605597

606-
607-
#define paranoid 0 /* only set to 1 if you suspect checking code has bug */
608-
609-
#ifdef ASSERT
610-
611-
static void verify_memory(void* ptr) {
612-
GuardedMemory guarded(ptr);
613-
if (!guarded.verify_guards()) {
614-
LogTarget(Warning, malloc, free) lt;
615-
ResourceMark rm;
616-
LogStream ls(lt);
617-
ls.print_cr("## nof_mallocs = " UINT64_FORMAT ", nof_frees = " UINT64_FORMAT, os::num_mallocs, os::num_frees);
618-
ls.print_cr("## memory stomp:");
619-
guarded.print_on(&ls);
620-
fatal("memory stomping error");
621-
}
622-
}
623-
624-
#endif
625-
626598
//
627599
// This function supports testing of the malloc out of memory
628600
// condition without really running the system out of memory.
629601
//
602+
630603
static bool has_reached_max_malloc_test_peak(size_t alloc_size) {
631604
if (MallocMaxTestWords > 0) {
632605
size_t words = (alloc_size / BytesPerWord);
@@ -639,13 +612,24 @@ static bool has_reached_max_malloc_test_peak(size_t alloc_size) {
639612
return false;
640613
}
641614

615+
#ifdef ASSERT
616+
static void check_crash_protection() {
617+
assert(!os::ThreadCrashProtection::is_crash_protected(Thread::current_or_null()),
618+
"not allowed when crash protection is set");
619+
}
620+
static void break_if_ptr_caught(void* ptr) {
621+
if (p2i(ptr) == (intptr_t)MallocCatchPtr) {
622+
log_warning(malloc, free)("ptr caught: " PTR_FORMAT, p2i(ptr));
623+
breakpoint();
624+
}
625+
}
626+
#endif // ASSERT
627+
642628
void* os::malloc(size_t size, MEMFLAGS flags) {
643629
return os::malloc(size, flags, CALLER_PC);
644630
}
645631

646632
void* os::malloc(size_t size, MEMFLAGS memflags, const NativeCallStack& stack) {
647-
NOT_PRODUCT(inc_stat_counter(&num_mallocs, 1));
648-
NOT_PRODUCT(inc_stat_counter(&alloc_bytes, size));
649633

650634
#if INCLUDE_NMT
651635
{
@@ -656,58 +640,35 @@ void* os::malloc(size_t size, MEMFLAGS memflags, const NativeCallStack& stack) {
656640
}
657641
#endif
658642

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

664-
if (size == 0) {
665-
// return a valid pointer if size is zero
666-
// if NULL is returned the calling functions assume out of memory.
667-
size = 1;
668-
}
669-
670-
// NMT support
671-
NMT_TrackingLevel level = MemTracker::tracking_level();
672-
const size_t nmt_overhead =
673-
MemTracker::malloc_header_size(level) + MemTracker::malloc_footer_size(level);
674-
675-
#ifndef ASSERT
676-
const size_t alloc_size = size + nmt_overhead;
677-
#else
678-
const size_t alloc_size = GuardedMemory::get_total_size(size + nmt_overhead);
679-
if (size + nmt_overhead > alloc_size) { // Check for rollover.
680-
return NULL;
681-
}
682-
#endif
645+
// On malloc(0), implementators of malloc(3) have the choice to return either
646+
// NULL or a unique non-NULL pointer. To unify libc behavior across our platforms
647+
// we chose the latter.
648+
size = MAX2((size_t)1, size);
683649

684650
// For the test flag -XX:MallocMaxTestWords
685651
if (has_reached_max_malloc_test_peak(size)) {
686652
return NULL;
687653
}
688654

689-
u_char* ptr;
690-
ptr = (u_char*)::malloc(alloc_size);
655+
const NMT_TrackingLevel level = MemTracker::tracking_level();
656+
const size_t nmt_overhead =
657+
MemTracker::malloc_header_size(level) + MemTracker::malloc_footer_size(level);
658+
659+
const size_t outer_size = size + nmt_overhead;
691660

692-
#ifdef ASSERT
693-
if (ptr == NULL) {
661+
void* const outer_ptr = (u_char*)::malloc(outer_size);
662+
if (outer_ptr == NULL) {
694663
return NULL;
695664
}
696-
// Wrap memory with guard
697-
GuardedMemory guarded(ptr, size + nmt_overhead);
698-
ptr = guarded.get_user_ptr();
699665

700-
if ((intptr_t)ptr == (intptr_t)MallocCatchPtr) {
701-
log_warning(malloc, free)("os::malloc caught, " SIZE_FORMAT " bytes --> " PTR_FORMAT, size, p2i(ptr));
702-
breakpoint();
703-
}
704-
if (paranoid) {
705-
verify_memory(ptr);
706-
}
707-
#endif
666+
void* inner_ptr = MemTracker::record_malloc((address)outer_ptr, size, memflags, stack, level);
708667

709-
// we do not track guard memory
710-
return MemTracker::record_malloc((address)ptr, size, memflags, stack, level);
668+
DEBUG_ONLY(::memset(inner_ptr, uninitBlockPad, size);)
669+
DEBUG_ONLY(break_if_ptr_caught(inner_ptr);)
670+
671+
return inner_ptr;
711672
}
712673

713674
void* os::realloc(void *memblock, size_t size, MEMFLAGS flags) {
@@ -725,59 +686,41 @@ void* os::realloc(void *memblock, size_t size, MEMFLAGS memflags, const NativeCa
725686
}
726687
#endif
727688

689+
if (memblock == NULL) {
690+
return os::malloc(size, memflags, stack);
691+
}
692+
693+
DEBUG_ONLY(check_crash_protection());
694+
695+
// On realloc(p, 0), implementators of realloc(3) have the choice to return either
696+
// NULL or a unique non-NULL pointer. To unify libc behavior across our platforms
697+
// we chose the latter.
698+
size = MAX2((size_t)1, size);
699+
728700
// For the test flag -XX:MallocMaxTestWords
729701
if (has_reached_max_malloc_test_peak(size)) {
730702
return NULL;
731703
}
732704

733-
if (size == 0) {
734-
// return a valid pointer if size is zero
735-
// if NULL is returned the calling functions assume out of memory.
736-
size = 1;
737-
}
738-
739-
#ifndef ASSERT
740-
NOT_PRODUCT(inc_stat_counter(&num_mallocs, 1));
741-
NOT_PRODUCT(inc_stat_counter(&alloc_bytes, size));
742-
// NMT support
743-
NMT_TrackingLevel level = MemTracker::tracking_level();
744-
void* membase = MemTracker::record_free(memblock, level);
705+
const NMT_TrackingLevel level = MemTracker::tracking_level();
745706
const size_t nmt_overhead =
746707
MemTracker::malloc_header_size(level) + MemTracker::malloc_footer_size(level);
747-
void* ptr = ::realloc(membase, size + nmt_overhead);
748-
return MemTracker::record_malloc(ptr, size, memflags, stack, level);
749-
#else
750-
if (memblock == NULL) {
751-
return os::malloc(size, memflags, stack);
752-
}
753-
if ((intptr_t)memblock == (intptr_t)MallocCatchPtr) {
754-
log_warning(malloc, free)("os::realloc caught " PTR_FORMAT, p2i(memblock));
755-
breakpoint();
756-
}
757-
// NMT support
758-
void* membase = MemTracker::malloc_base(memblock);
759-
verify_memory(membase);
760-
// always move the block
761-
void* ptr = os::malloc(size, memflags, stack);
762-
// Copy to new memory if malloc didn't fail
763-
if (ptr != NULL ) {
764-
GuardedMemory guarded(MemTracker::malloc_base(memblock));
765-
// Guard's user data contains NMT header
766-
NMT_TrackingLevel level = MemTracker::tracking_level();
767-
const size_t nmt_overhead =
768-
MemTracker::malloc_header_size(level) + MemTracker::malloc_footer_size(level);
769-
size_t memblock_size = guarded.get_user_size() - nmt_overhead;
770-
memcpy(ptr, memblock, MIN2(size, memblock_size));
771-
if (paranoid) {
772-
verify_memory(MemTracker::malloc_base(ptr));
773-
}
774-
os::free(memblock);
775-
}
776-
return ptr;
777-
#endif
708+
709+
const size_t new_outer_size = size + nmt_overhead;
710+
711+
// If NMT is enabled, this checks for heap overwrites, then de-accounts the old block.
712+
void* const old_outer_ptr = MemTracker::record_free(memblock, level);
713+
714+
void* const new_outer_ptr = ::realloc(old_outer_ptr, new_outer_size);
715+
716+
// If NMT is enabled, this checks for heap overwrites, then de-accounts the old block.
717+
void* const new_inner_ptr = MemTracker::record_malloc(new_outer_ptr, size, memflags, stack, level);
718+
719+
DEBUG_ONLY(break_if_ptr_caught(new_inner_ptr);)
720+
721+
return new_inner_ptr;
778722
}
779723

780-
// handles NULL pointers
781724
void os::free(void *memblock) {
782725

783726
#if INCLUDE_NMT
@@ -786,25 +729,17 @@ void os::free(void *memblock) {
786729
}
787730
#endif
788731

789-
NOT_PRODUCT(inc_stat_counter(&num_frees, 1));
790-
#ifdef ASSERT
791-
if (memblock == NULL) return;
792-
if ((intptr_t)memblock == (intptr_t)MallocCatchPtr) {
793-
log_warning(malloc, free)("os::free caught " PTR_FORMAT, p2i(memblock));
794-
breakpoint();
732+
if (memblock == NULL) {
733+
return;
795734
}
796-
void* membase = MemTracker::record_free(memblock, MemTracker::tracking_level());
797-
verify_memory(membase);
798735

799-
GuardedMemory guarded(membase);
800-
size_t size = guarded.get_user_size();
801-
inc_stat_counter(&free_bytes, size);
802-
membase = guarded.release_for_freeing();
803-
::free(membase);
804-
#else
805-
void* membase = MemTracker::record_free(memblock, MemTracker::tracking_level());
806-
::free(membase);
807-
#endif
736+
DEBUG_ONLY(break_if_ptr_caught(memblock);)
737+
738+
const NMT_TrackingLevel level = MemTracker::tracking_level();
739+
740+
// If NMT is enabled, this checks for heap overwrites, then de-accounts the old block.
741+
void* const old_outer_ptr = MemTracker::record_free(memblock, level);
742+
::free(old_outer_ptr);
808743
}
809744

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

src/hotspot/share/runtime/os.hpp

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -784,13 +784,6 @@ class os: AllStatic {
784784
// Like strdup, but exit VM when strdup() returns NULL
785785
static char* strdup_check_oom(const char*, MEMFLAGS flags = mtInternal);
786786

787-
#ifndef PRODUCT
788-
static julong num_mallocs; // # of calls to malloc/realloc
789-
static julong alloc_bytes; // # of bytes allocated
790-
static julong num_frees; // # of calls to free
791-
static julong free_bytes; // # of bytes freed
792-
#endif
793-
794787
// SocketInterface (ex HPI SocketInterface )
795788
static int socket(int domain, int type, int protocol);
796789
static int socket_close(int fd);

test/hotspot/jtreg/gtest/NMTGtests.java

Lines changed: 10 additions & 7 deletions
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

Lines changed: 7 additions & 4 deletions
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

Lines changed: 1 addition & 1 deletion
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");

0 commit comments

Comments
 (0)