8325139: JFR SwapSpace event - add free swap space information on Linux when running in a container environment

Reviewed-by: lucy, sgehwolf
This commit is contained in:
Matthias Baesken 2024-03-05 15:34:27 +00:00
parent c00c939f99
commit 3d106cb091
9 changed files with 103 additions and 12 deletions

View File

@ -264,6 +264,7 @@ class CgroupSubsystem: public CHeapObj<mtInternal> {
virtual jlong pids_current() = 0;
virtual jlong memory_usage_in_bytes() = 0;
virtual jlong memory_and_swap_limit_in_bytes() = 0;
virtual jlong memory_and_swap_usage_in_bytes() = 0;
virtual jlong memory_soft_limit_in_bytes() = 0;
virtual jlong memory_max_usage_in_bytes() = 0;
virtual jlong rss_usage_in_bytes() = 0;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2024, 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
@ -168,6 +168,20 @@ jlong CgroupV1Subsystem::memory_and_swap_limit_in_bytes() {
return memory_swap;
}
jlong CgroupV1Subsystem::memory_and_swap_usage_in_bytes() {
jlong memory_sw_limit = memory_and_swap_limit_in_bytes();
jlong memory_limit = CgroupSubsystem::memory_limit_in_bytes();
if (memory_sw_limit > 0 && memory_limit > 0) {
jlong delta_swap = memory_sw_limit - memory_limit;
if (delta_swap > 0) {
GET_CONTAINER_INFO(julong, _memory->controller(), "/memory.memsw.usage_in_bytes",
"mem swap usage is: ", JULONG_FORMAT, JULONG_FORMAT, memory_swap_usage);
return (jlong)memory_swap_usage;
}
}
return memory_usage_in_bytes();
}
jlong CgroupV1Subsystem::read_mem_swappiness() {
GET_CONTAINER_INFO(julong, _memory->controller(), "/memory.swappiness",
"Swappiness is: ", JULONG_FORMAT, JULONG_FORMAT, swappiness);

View File

@ -76,6 +76,7 @@ class CgroupV1Subsystem: public CgroupSubsystem {
public:
jlong read_memory_limit_in_bytes();
jlong memory_and_swap_limit_in_bytes();
jlong memory_and_swap_usage_in_bytes();
jlong memory_soft_limit_in_bytes();
jlong memory_usage_in_bytes();
jlong memory_max_usage_in_bytes();

View File

@ -180,6 +180,16 @@ jlong CgroupV2Subsystem::memory_and_swap_limit_in_bytes() {
return swap_limit;
}
jlong CgroupV2Subsystem::memory_and_swap_usage_in_bytes() {
jlong memory_usage = memory_usage_in_bytes();
if (memory_usage >= 0) {
char* mem_swp_current_str = mem_swp_current_val();
jlong swap_current = limit_from_str(mem_swp_current_str);
return memory_usage + (swap_current >= 0 ? swap_current : 0);
}
return memory_usage; // not supported or unlimited case
}
char* CgroupV2Subsystem::mem_swp_limit_val() {
GET_CONTAINER_INFO_CPTR(cptr, _unified, "/memory.swap.max",
"Memory and Swap Limit is: %s", "%1023s", mem_swp_limit_str, 1024);

View File

@ -75,6 +75,7 @@ class CgroupV2Subsystem: public CgroupSubsystem {
int cpu_period();
int cpu_shares();
jlong memory_and_swap_limit_in_bytes();
jlong memory_and_swap_usage_in_bytes();
jlong memory_soft_limit_in_bytes();
jlong memory_usage_in_bytes();
jlong memory_max_usage_in_bytes();

View File

@ -77,6 +77,11 @@ jlong OSContainer::memory_and_swap_limit_in_bytes() {
return cgroup_subsystem->memory_and_swap_limit_in_bytes();
}
jlong OSContainer::memory_and_swap_usage_in_bytes() {
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
return cgroup_subsystem->memory_and_swap_usage_in_bytes();
}
jlong OSContainer::memory_soft_limit_in_bytes() {
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
return cgroup_subsystem->memory_soft_limit_in_bytes();

View File

@ -52,6 +52,7 @@ class OSContainer: AllStatic {
static jlong memory_limit_in_bytes();
static jlong memory_and_swap_limit_in_bytes();
static jlong memory_and_swap_usage_in_bytes();
static jlong memory_soft_limit_in_bytes();
static jlong memory_usage_in_bytes();
static jlong memory_max_usage_in_bytes();

View File

@ -304,18 +304,41 @@ jlong os::total_swap_space() {
return (jlong)(si.totalswap * si.mem_unit);
}
jlong os::free_swap_space() {
if (OSContainer::is_containerized()) {
// TODO add a good implementation
static jlong host_free_swap() {
struct sysinfo si;
int ret = sysinfo(&si);
if (ret != 0) {
return -1;
} else {
struct sysinfo si;
int ret = sysinfo(&si);
if (ret != 0) {
return -1;
}
return (jlong)(si.freeswap * si.mem_unit);
}
return (jlong)(si.freeswap * si.mem_unit);
}
jlong os::free_swap_space() {
jlong host_free_swap_val = host_free_swap();
if (OSContainer::is_containerized()) {
jlong mem_swap_limit = OSContainer::memory_and_swap_limit_in_bytes();
jlong mem_limit = OSContainer::memory_limit_in_bytes();
if (mem_swap_limit >= 0 && mem_limit >= 0) {
jlong delta_limit = mem_swap_limit - mem_limit;
if (delta_limit <= 0) {
return 0;
}
jlong mem_swap_usage = OSContainer::memory_and_swap_usage_in_bytes();
jlong mem_usage = OSContainer::memory_usage_in_bytes();
if (mem_swap_usage > 0 && mem_usage > 0) {
jlong delta_usage = mem_swap_usage - mem_usage;
if (delta_usage >= 0) {
jlong free_swap = delta_limit - delta_usage;
return free_swap >= 0 ? free_swap : 0;
}
}
}
// unlimited or not supported. Fall through to return host value
log_trace(os,container)("os::free_swap_space: container_swap_limit=" JLONG_FORMAT
" container_mem_limit=" JLONG_FORMAT " returning host value: " JLONG_FORMAT,
mem_swap_limit, mem_limit, host_free_swap_val);
}
return host_free_swap_val;
}
julong os::physical_memory() {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2019, 2024, 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
@ -68,6 +68,10 @@ public class TestJFREvents {
testMemory("500m", "" + 500*MB);
testMemory("1g", "" + 1024*MB);
// see https://docs.docker.com/config/containers/resource_constraints/
testSwapMemory("200m", "200m", "" + 0*MB, "" + 0*MB);
testSwapMemory("200m", "300m", "" + 100*MB, "" + 100*MB);
testProcessInfo();
testEnvironmentVariables();
@ -211,6 +215,37 @@ public class TestJFREvents {
}
private static void testSwapMemory(String memValueToSet, String swapValueToSet, String expectedTotalValue, String expectedFreeValue) throws Exception {
Common.logNewTestCase("Memory: --memory = " + memValueToSet + " --memory-swap = " + swapValueToSet);
OutputAnalyzer out = DockerTestUtils.dockerRunJava(
commonDockerOpts()
.addDockerOpts("--memory=" + memValueToSet)
.addDockerOpts("--memory-swap=" + swapValueToSet)
.addClassOptions("jdk.SwapSpace"));
out.shouldHaveExitValue(0)
.shouldContain("totalSize = " + expectedTotalValue)
.shouldContain("freeSize = ");
List<String> ls = out.asLinesWithoutVMWarnings();
for (String cur : ls) {
int idx = cur.indexOf("freeSize = ");
if (idx != -1) {
int startNbr = idx+11;
int endNbr = cur.indexOf(' ', startNbr);
if (endNbr == -1) endNbr = cur.length();
String freeSizeStr = cur.substring(startNbr, endNbr);
long freeval = Long.parseLong(freeSizeStr);
long totalval = Long.parseLong(expectedTotalValue);
if (0 <= freeval && freeval <= totalval) {
System.out.println("Found freeSize value " + freeval + " is fine");
} else {
System.out.println("Found freeSize value " + freeval + " is bad");
throw new Exception("Found free size value is bad");
}
}
}
}
private static void testProcessInfo() throws Exception {
Common.logNewTestCase("ProcessInfo");
DockerTestUtils.dockerRunJava(