8296784: Provide clean mallinfo/mallinfo2 wrapper for Linux glibc platforms
Reviewed-by: rkennke, mbaesken
This commit is contained in:
parent
7b3d581497
commit
0a3b0fc8ad
@ -169,8 +169,37 @@ const char * os::Linux::_libpthread_version = NULL;
|
||||
size_t os::Linux::_default_large_page_size = 0;
|
||||
|
||||
#ifdef __GLIBC__
|
||||
os::Linux::mallinfo_func_t os::Linux::_mallinfo = NULL;
|
||||
os::Linux::mallinfo2_func_t os::Linux::_mallinfo2 = NULL;
|
||||
// We want to be buildable and runnable on older and newer glibcs, so resolve both
|
||||
// mallinfo and mallinfo2 dynamically.
|
||||
struct old_mallinfo {
|
||||
int arena;
|
||||
int ordblks;
|
||||
int smblks;
|
||||
int hblks;
|
||||
int hblkhd;
|
||||
int usmblks;
|
||||
int fsmblks;
|
||||
int uordblks;
|
||||
int fordblks;
|
||||
int keepcost;
|
||||
};
|
||||
typedef struct old_mallinfo (*mallinfo_func_t)(void);
|
||||
static mallinfo_func_t g_mallinfo = NULL;
|
||||
|
||||
struct new_mallinfo {
|
||||
size_t arena;
|
||||
size_t ordblks;
|
||||
size_t smblks;
|
||||
size_t hblks;
|
||||
size_t hblkhd;
|
||||
size_t usmblks;
|
||||
size_t fsmblks;
|
||||
size_t uordblks;
|
||||
size_t fordblks;
|
||||
size_t keepcost;
|
||||
};
|
||||
typedef struct new_mallinfo (*mallinfo2_func_t)(void);
|
||||
static mallinfo2_func_t g_mallinfo2 = NULL;
|
||||
#endif // __GLIBC__
|
||||
|
||||
static int clock_tics_per_sec = 100;
|
||||
@ -2102,26 +2131,17 @@ void os::Linux::print_process_memory_info(outputStream* st) {
|
||||
size_t total_allocated = 0;
|
||||
size_t free_retained = 0;
|
||||
bool might_have_wrapped = false;
|
||||
if (_mallinfo2 != NULL) {
|
||||
struct glibc_mallinfo2 mi = _mallinfo2();
|
||||
total_allocated = mi.uordblks + mi.hblkhd;
|
||||
free_retained = mi.fordblks;
|
||||
} else if (_mallinfo != NULL) {
|
||||
// mallinfo is an old API. Member names mean next to nothing and, beyond that, are 32-bit signed.
|
||||
// So for larger footprints the values may have wrapped around. We try to detect this here: if the
|
||||
// process whole resident set size is smaller than 4G, malloc footprint has to be less than that
|
||||
// and the numbers are reliable.
|
||||
struct glibc_mallinfo mi = _mallinfo();
|
||||
total_allocated = (size_t)(unsigned)mi.uordblks + (size_t)(unsigned)mi.hblkhd;
|
||||
free_retained = (size_t)(unsigned)mi.fordblks;
|
||||
// Since mallinfo members are int, glibc values may have wrapped. Warn about this.
|
||||
might_have_wrapped = (info.vmrss * K) > UINT_MAX && (info.vmrss * K) > (total_allocated + UINT_MAX);
|
||||
}
|
||||
if (_mallinfo2 != NULL || _mallinfo != NULL) {
|
||||
st->print_cr("C-Heap outstanding allocations: " SIZE_FORMAT "K, retained: " SIZE_FORMAT "K%s",
|
||||
total_allocated / K, free_retained / K,
|
||||
might_have_wrapped ? " (may have wrapped)" : "");
|
||||
}
|
||||
glibc_mallinfo mi;
|
||||
os::Linux::get_mallinfo(&mi, &might_have_wrapped);
|
||||
total_allocated = mi.uordblks + mi.hblkhd;
|
||||
free_retained = mi.fordblks;
|
||||
#ifdef _LP64
|
||||
// If legacy mallinfo(), we can still print the values if we are sure they cannot have wrapped.
|
||||
might_have_wrapped = might_have_wrapped && (info.vmsize * K) > UINT_MAX;
|
||||
#endif
|
||||
st->print_cr("C-Heap outstanding allocations: " SIZE_FORMAT "K, retained: " SIZE_FORMAT "K%s",
|
||||
total_allocated / K, free_retained / K,
|
||||
might_have_wrapped ? " (may have wrapped)" : "");
|
||||
// Tunables
|
||||
print_glibc_malloc_tunables(st);
|
||||
st->cr();
|
||||
@ -4273,8 +4293,8 @@ void os::init(void) {
|
||||
Linux::initialize_system_info();
|
||||
|
||||
#ifdef __GLIBC__
|
||||
Linux::_mallinfo = CAST_TO_FN_PTR(Linux::mallinfo_func_t, dlsym(RTLD_DEFAULT, "mallinfo"));
|
||||
Linux::_mallinfo2 = CAST_TO_FN_PTR(Linux::mallinfo2_func_t, dlsym(RTLD_DEFAULT, "mallinfo2"));
|
||||
g_mallinfo = CAST_TO_FN_PTR(mallinfo_func_t, dlsym(RTLD_DEFAULT, "mallinfo"));
|
||||
g_mallinfo2 = CAST_TO_FN_PTR(mallinfo2_func_t, dlsym(RTLD_DEFAULT, "mallinfo2"));
|
||||
#endif // __GLIBC__
|
||||
|
||||
os::Linux::CPUPerfTicks pticks;
|
||||
@ -5332,6 +5352,42 @@ void os::print_memory_mappings(char* addr, size_t bytes, outputStream* st) {
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __GLIBC__
|
||||
void os::Linux::get_mallinfo(glibc_mallinfo* out, bool* might_have_wrapped) {
|
||||
if (g_mallinfo2) {
|
||||
new_mallinfo mi = g_mallinfo2();
|
||||
out->arena = mi.arena;
|
||||
out->ordblks = mi.ordblks;
|
||||
out->smblks = mi.smblks;
|
||||
out->hblks = mi.hblks;
|
||||
out->hblkhd = mi.hblkhd;
|
||||
out->usmblks = mi.usmblks;
|
||||
out->fsmblks = mi.fsmblks;
|
||||
out->uordblks = mi.uordblks;
|
||||
out->fordblks = mi.fordblks;
|
||||
out->keepcost = mi.keepcost;
|
||||
*might_have_wrapped = false;
|
||||
} else if (g_mallinfo) {
|
||||
old_mallinfo mi = g_mallinfo();
|
||||
// glibc reports unsigned 32-bit sizes in int form. First make unsigned, then extend.
|
||||
out->arena = (size_t)(unsigned)mi.arena;
|
||||
out->ordblks = (size_t)(unsigned)mi.ordblks;
|
||||
out->smblks = (size_t)(unsigned)mi.smblks;
|
||||
out->hblks = (size_t)(unsigned)mi.hblks;
|
||||
out->hblkhd = (size_t)(unsigned)mi.hblkhd;
|
||||
out->usmblks = (size_t)(unsigned)mi.usmblks;
|
||||
out->fsmblks = (size_t)(unsigned)mi.fsmblks;
|
||||
out->uordblks = (size_t)(unsigned)mi.uordblks;
|
||||
out->fordblks = (size_t)(unsigned)mi.fordblks;
|
||||
out->keepcost = (size_t)(unsigned)mi.keepcost;
|
||||
*might_have_wrapped = NOT_LP64(false) LP64_ONLY(true);
|
||||
} else {
|
||||
// We should have either mallinfo or mallinfo2
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
}
|
||||
#endif // __GLIBC__
|
||||
|
||||
bool os::trim_native_heap(os::size_change_t* rss_change) {
|
||||
#ifdef __GLIBC__
|
||||
os::Linux::meminfo_t info1;
|
||||
|
@ -172,7 +172,7 @@ class os::Linux {
|
||||
// Return the namespace pid if so, otherwise -1.
|
||||
static int get_namespace_pid(int vmid);
|
||||
|
||||
// Output structure for query_process_memory_info()
|
||||
// Output structure for query_process_memory_info() (all values in KB)
|
||||
struct meminfo_t {
|
||||
ssize_t vmsize; // current virtual size
|
||||
ssize_t vmpeak; // peak virtual size
|
||||
@ -265,40 +265,6 @@ class os::Linux {
|
||||
};
|
||||
static NumaAllocationPolicy _current_numa_policy;
|
||||
|
||||
#ifdef __GLIBC__
|
||||
struct glibc_mallinfo {
|
||||
int arena;
|
||||
int ordblks;
|
||||
int smblks;
|
||||
int hblks;
|
||||
int hblkhd;
|
||||
int usmblks;
|
||||
int fsmblks;
|
||||
int uordblks;
|
||||
int fordblks;
|
||||
int keepcost;
|
||||
};
|
||||
|
||||
struct glibc_mallinfo2 {
|
||||
size_t arena;
|
||||
size_t ordblks;
|
||||
size_t smblks;
|
||||
size_t hblks;
|
||||
size_t hblkhd;
|
||||
size_t usmblks;
|
||||
size_t fsmblks;
|
||||
size_t uordblks;
|
||||
size_t fordblks;
|
||||
size_t keepcost;
|
||||
};
|
||||
|
||||
typedef struct glibc_mallinfo (*mallinfo_func_t)(void);
|
||||
typedef struct glibc_mallinfo2 (*mallinfo2_func_t)(void);
|
||||
|
||||
static mallinfo_func_t _mallinfo;
|
||||
static mallinfo2_func_t _mallinfo2;
|
||||
#endif
|
||||
|
||||
public:
|
||||
static int sched_getcpu() { return _sched_getcpu != NULL ? _sched_getcpu() : -1; }
|
||||
static int numa_node_to_cpus(int node, unsigned long *buffer, int bufferlen);
|
||||
@ -426,6 +392,39 @@ class os::Linux {
|
||||
}
|
||||
|
||||
static void* resolve_function_descriptor(void* p);
|
||||
|
||||
#ifdef __GLIBC__
|
||||
// os::Linux::get_mallinfo() hides the complexity of dealing with mallinfo() or
|
||||
// mallinfo2() from the user. Use this function instead of raw mallinfo/mallinfo2()
|
||||
// to keep the JVM runtime-compatible with different glibc versions.
|
||||
//
|
||||
// mallinfo2() was added with glibc (>2.32). Legacy mallinfo() was deprecated with
|
||||
// 2.33 and may vanish in future glibcs. So we may have both or either one of
|
||||
// them.
|
||||
//
|
||||
// mallinfo2() is functionally equivalent to legacy mallinfo but returns sizes as
|
||||
// 64-bit on 64-bit platforms. Legacy mallinfo uses 32-bit fields. However, legacy
|
||||
// mallinfo is still perfectly fine to use if we know the sizes cannot have wrapped.
|
||||
// For example, if the process virtual size does not exceed 4G, we cannot have
|
||||
// malloc'ed more than 4G, so the results from legacy mallinfo() can still be used.
|
||||
//
|
||||
// os::Linux::get_mallinfo() will always prefer mallinfo2() if found, but will fall back
|
||||
// to legacy mallinfo() if only that is available. In that case, it will return true
|
||||
// in *might_have_wrapped.
|
||||
struct glibc_mallinfo {
|
||||
size_t arena;
|
||||
size_t ordblks;
|
||||
size_t smblks;
|
||||
size_t hblks;
|
||||
size_t hblkhd;
|
||||
size_t usmblks;
|
||||
size_t fsmblks;
|
||||
size_t uordblks;
|
||||
size_t fordblks;
|
||||
size_t keepcost;
|
||||
};
|
||||
static void get_mallinfo(glibc_mallinfo* out, bool* might_have_wrapped);
|
||||
#endif // GLIBC
|
||||
};
|
||||
|
||||
#endif // OS_LINUX_OS_LINUX_HPP
|
||||
|
@ -35,6 +35,9 @@
|
||||
#include "utilities/align.hpp"
|
||||
#include "utilities/decoder.hpp"
|
||||
|
||||
#include "testutils.hpp"
|
||||
#include "unittest.hpp"
|
||||
|
||||
namespace {
|
||||
static void small_page_write(void* addr, size_t size) {
|
||||
size_t page_size = os::vm_page_size();
|
||||
@ -476,4 +479,28 @@ TEST_VM(os_linux, decoder_get_source_info_invalid) {
|
||||
}
|
||||
}
|
||||
#endif // NOT PRODUCT
|
||||
|
||||
#ifdef __GLIBC__
|
||||
TEST_VM(os_linux, glibc_mallinfo_wrapper) {
|
||||
// Very basic test. Call it. That proves that resolution and invocation works.
|
||||
os::Linux::glibc_mallinfo mi;
|
||||
bool did_wrap = false;
|
||||
|
||||
os::Linux::get_mallinfo(&mi, &did_wrap);
|
||||
|
||||
void* p = os::malloc(2 * K, mtTest);
|
||||
ASSERT_NOT_NULL(p);
|
||||
|
||||
// We should see total allocation values > 0
|
||||
ASSERT_GE((mi.uordblks + mi.hblkhd), 2 * K);
|
||||
|
||||
// These values also should exceed some reasonable size.
|
||||
ASSERT_LT(mi.fordblks, 2 * G);
|
||||
ASSERT_LT(mi.uordblks, 2 * G);
|
||||
ASSERT_LT(mi.hblkhd, 2 * G);
|
||||
|
||||
os::free(p);
|
||||
}
|
||||
#endif // __GLIBC__
|
||||
|
||||
#endif // LINUX
|
||||
|
Loading…
x
Reference in New Issue
Block a user