mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
332 lines
14 KiB
C++
332 lines
14 KiB
C++
/*
|
|
* Copyright (c) 2017, 2026, 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.
|
|
*
|
|
*/
|
|
|
|
#include "cgroupSubsystem_linux.hpp"
|
|
#include "logging/log.hpp"
|
|
#include "os_linux.hpp"
|
|
#include "osContainer_linux.hpp"
|
|
#include "runtime/globals.hpp"
|
|
#include "runtime/os.hpp"
|
|
|
|
#include <errno.h>
|
|
#include <math.h>
|
|
#include <string.h>
|
|
|
|
|
|
bool OSContainer::_is_initialized = false;
|
|
bool OSContainer::_is_containerized = false;
|
|
CgroupSubsystem* cgroup_subsystem;
|
|
|
|
/* init
|
|
*
|
|
* Initialize the container support and determine if
|
|
* we are running under cgroup control.
|
|
*/
|
|
void OSContainer::init() {
|
|
assert(!_is_initialized, "Initializing OSContainer more than once");
|
|
|
|
_is_initialized = true;
|
|
_is_containerized = false;
|
|
|
|
log_trace(os, container)("OSContainer::init: Initializing Container Support");
|
|
if (!UseContainerSupport) {
|
|
log_trace(os, container)("Container Support not enabled");
|
|
return;
|
|
}
|
|
|
|
cgroup_subsystem = CgroupSubsystemFactory::create();
|
|
if (cgroup_subsystem == nullptr) {
|
|
return; // Required subsystem files not found or other error
|
|
}
|
|
/*
|
|
* In order to avoid a false positive on is_containerized() on
|
|
* Linux systems outside a container *and* to ensure compatibility
|
|
* with in-container usage, we detemine is_containerized() by two
|
|
* steps:
|
|
* 1.) Determine if all the cgroup controllers are mounted read only.
|
|
* If yes, is_containerized() == true. Otherwise, do the fallback
|
|
* in 2.)
|
|
* 2.) Query for memory and cpu limits. If any limit is set, we set
|
|
* is_containerized() == true.
|
|
*
|
|
* Step 1.) covers the basic in container use-cases. Step 2.) ensures
|
|
* that limits enforced by other means (e.g. systemd slice) are properly
|
|
* detected.
|
|
*/
|
|
const char *reason;
|
|
bool any_mem_cpu_limit_present = false;
|
|
bool controllers_read_only = cgroup_subsystem->is_containerized();
|
|
if (controllers_read_only) {
|
|
// in-container case
|
|
reason = " because all controllers are mounted read-only (container case)";
|
|
} else {
|
|
// We can be in one of two cases:
|
|
// 1.) On a physical Linux system without any limit
|
|
// 2.) On a physical Linux system with a limit enforced by other means (like systemd slice)
|
|
physical_memory_size_type mem_limit_val = value_unlimited;
|
|
(void)memory_limit_in_bytes(mem_limit_val); // discard error and use default
|
|
double host_cpus = os::Linux::active_processor_count();
|
|
double cpus = host_cpus;
|
|
(void)active_processor_count(cpus); // discard error and use default
|
|
any_mem_cpu_limit_present = mem_limit_val != value_unlimited || host_cpus != cpus;
|
|
if (any_mem_cpu_limit_present) {
|
|
reason = " because either a cpu or a memory limit is present";
|
|
} else {
|
|
reason = " because no cpu or memory limit is present";
|
|
}
|
|
}
|
|
_is_containerized = controllers_read_only || any_mem_cpu_limit_present;
|
|
log_debug(os, container)("OSContainer::init: is_containerized() = %s%s",
|
|
_is_containerized ? "true" : "false",
|
|
reason);
|
|
}
|
|
|
|
const char * OSContainer::container_type() {
|
|
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
|
|
return cgroup_subsystem->container_type();
|
|
}
|
|
|
|
bool OSContainer::memory_limit_in_bytes(physical_memory_size_type& value) {
|
|
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
|
|
physical_memory_size_type phys_mem = os::Linux::physical_memory();
|
|
return cgroup_subsystem->memory_limit_in_bytes(phys_mem, value);
|
|
}
|
|
|
|
bool OSContainer::available_memory_in_bytes(physical_memory_size_type& value) {
|
|
physical_memory_size_type mem_limit = value_unlimited;
|
|
physical_memory_size_type mem_usage = 0;
|
|
if (memory_limit_in_bytes(mem_limit) && memory_usage_in_bytes(mem_usage)) {
|
|
assert(mem_usage != value_unlimited, "invariant");
|
|
if (mem_limit != value_unlimited) {
|
|
value = (mem_limit > mem_usage) ? mem_limit - mem_usage : 0;
|
|
return true;
|
|
}
|
|
}
|
|
log_trace(os, container)("calculating available memory in container failed");
|
|
return false;
|
|
}
|
|
|
|
bool OSContainer::available_swap_in_bytes(physical_memory_size_type& value) {
|
|
physical_memory_size_type mem_limit = 0;
|
|
physical_memory_size_type mem_swap_limit = 0;
|
|
if (memory_limit_in_bytes(mem_limit) &&
|
|
memory_and_swap_limit_in_bytes(mem_swap_limit) &&
|
|
mem_limit != value_unlimited &&
|
|
mem_swap_limit != value_unlimited) {
|
|
if (mem_limit >= mem_swap_limit) {
|
|
value = 0; // no swap, thus no free swap
|
|
return true;
|
|
}
|
|
physical_memory_size_type swap_limit = mem_swap_limit - mem_limit;
|
|
physical_memory_size_type mem_swap_usage = 0;
|
|
physical_memory_size_type mem_usage = 0;
|
|
if (memory_and_swap_usage_in_bytes(mem_swap_usage) &&
|
|
memory_usage_in_bytes(mem_usage)) {
|
|
physical_memory_size_type swap_usage = value_unlimited;
|
|
if (mem_usage > mem_swap_usage) {
|
|
swap_usage = 0; // delta usage must not be negative
|
|
} else {
|
|
swap_usage = mem_swap_usage - mem_usage;
|
|
}
|
|
// free swap is based on swap limit (upper bound) and swap usage
|
|
if (swap_usage >= swap_limit) {
|
|
value = 0; // free swap must not be negative
|
|
return true;
|
|
}
|
|
value = swap_limit - swap_usage;
|
|
return true;
|
|
}
|
|
}
|
|
// unlimited or not supported. Leave an appropriate trace message
|
|
if (log_is_enabled(Trace, os, container)) {
|
|
char mem_swap_buf[25]; // uint64_t => 20 + 1, 'unlimited' => 9 + 1; 10 < 21 < 25
|
|
char mem_limit_buf[25];
|
|
int num = 0;
|
|
if (mem_swap_limit == value_unlimited) {
|
|
num = os::snprintf(mem_swap_buf, sizeof(mem_swap_buf), "%s", "unlimited");
|
|
} else {
|
|
num = os::snprintf(mem_swap_buf, sizeof(mem_swap_buf), PHYS_MEM_TYPE_FORMAT, mem_swap_limit);
|
|
}
|
|
assert(num < 25, "buffer too small");
|
|
mem_swap_buf[num] = '\0';
|
|
if (mem_limit == value_unlimited) {
|
|
num = os::snprintf(mem_limit_buf, sizeof(mem_limit_buf), "%s", "unlimited");
|
|
} else {
|
|
num = os::snprintf(mem_limit_buf, sizeof(mem_limit_buf), PHYS_MEM_TYPE_FORMAT, mem_limit);
|
|
}
|
|
assert(num < 25, "buffer too small");
|
|
mem_limit_buf[num] = '\0';
|
|
log_trace(os,container)("OSContainer::available_swap_in_bytes: container_swap_limit=%s"
|
|
" container_mem_limit=%s", mem_swap_buf, mem_limit_buf);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool OSContainer::memory_and_swap_limit_in_bytes(physical_memory_size_type& value) {
|
|
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
|
|
physical_memory_size_type phys_mem = os::Linux::physical_memory();
|
|
physical_memory_size_type host_swap = 0;
|
|
if (!os::Linux::host_swap(host_swap)) {
|
|
return false;
|
|
}
|
|
return cgroup_subsystem->memory_and_swap_limit_in_bytes(phys_mem, host_swap, value);
|
|
}
|
|
|
|
bool OSContainer::memory_and_swap_usage_in_bytes(physical_memory_size_type& value) {
|
|
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
|
|
physical_memory_size_type phys_mem = os::Linux::physical_memory();
|
|
physical_memory_size_type host_swap = 0;
|
|
if (!os::Linux::host_swap(host_swap)) {
|
|
return false;
|
|
}
|
|
return cgroup_subsystem->memory_and_swap_usage_in_bytes(phys_mem, host_swap, value);
|
|
}
|
|
|
|
bool OSContainer::memory_soft_limit_in_bytes(physical_memory_size_type& value) {
|
|
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
|
|
physical_memory_size_type phys_mem = os::Linux::physical_memory();
|
|
return cgroup_subsystem->memory_soft_limit_in_bytes(phys_mem, value);
|
|
}
|
|
|
|
bool OSContainer::memory_throttle_limit_in_bytes(physical_memory_size_type& value) {
|
|
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
|
|
return cgroup_subsystem->memory_throttle_limit_in_bytes(value);
|
|
}
|
|
|
|
bool OSContainer::memory_usage_in_bytes(physical_memory_size_type& value) {
|
|
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
|
|
return cgroup_subsystem->memory_usage_in_bytes(value);
|
|
}
|
|
|
|
bool OSContainer::memory_max_usage_in_bytes(physical_memory_size_type& value) {
|
|
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
|
|
return cgroup_subsystem->memory_max_usage_in_bytes(value);
|
|
}
|
|
|
|
bool OSContainer::rss_usage_in_bytes(physical_memory_size_type& value) {
|
|
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
|
|
return cgroup_subsystem->rss_usage_in_bytes(value);
|
|
}
|
|
|
|
bool OSContainer::cache_usage_in_bytes(physical_memory_size_type& value) {
|
|
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
|
|
return cgroup_subsystem->cache_usage_in_bytes(value);
|
|
}
|
|
|
|
void OSContainer::print_version_specific_info(outputStream* st) {
|
|
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
|
|
physical_memory_size_type phys_mem = os::Linux::physical_memory();
|
|
cgroup_subsystem->print_version_specific_info(st, phys_mem);
|
|
}
|
|
|
|
char * OSContainer::cpu_cpuset_cpus() {
|
|
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
|
|
return cgroup_subsystem->cpu_cpuset_cpus();
|
|
}
|
|
|
|
char * OSContainer::cpu_cpuset_memory_nodes() {
|
|
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
|
|
return cgroup_subsystem->cpu_cpuset_memory_nodes();
|
|
}
|
|
|
|
bool OSContainer::active_processor_count(double& value) {
|
|
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
|
|
return cgroup_subsystem->active_processor_count(value);
|
|
}
|
|
|
|
bool OSContainer::cpu_quota(int& value) {
|
|
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
|
|
return cgroup_subsystem->cpu_quota(value);
|
|
}
|
|
|
|
bool OSContainer::cpu_period(int& value) {
|
|
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
|
|
return cgroup_subsystem->cpu_period(value);
|
|
}
|
|
|
|
bool OSContainer::cpu_shares(int& value) {
|
|
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
|
|
return cgroup_subsystem->cpu_shares(value);
|
|
}
|
|
|
|
bool OSContainer::cpu_usage_in_micros(uint64_t& value) {
|
|
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
|
|
return cgroup_subsystem->cpu_usage_in_micros(value);
|
|
}
|
|
|
|
bool OSContainer::pids_max(uint64_t& value) {
|
|
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
|
|
return cgroup_subsystem->pids_max(value);
|
|
}
|
|
|
|
bool OSContainer::pids_current(uint64_t& value) {
|
|
assert(cgroup_subsystem != nullptr, "cgroup subsystem not available");
|
|
return cgroup_subsystem->pids_current(value);
|
|
}
|
|
|
|
template<typename T> struct metric_fmt;
|
|
template<> struct metric_fmt<unsigned long long int> { static constexpr const char* fmt = "%llu"; };
|
|
template<> struct metric_fmt<unsigned long int> { static constexpr const char* fmt = "%lu"; };
|
|
template<> struct metric_fmt<int> { static constexpr const char* fmt = "%d"; };
|
|
template<> struct metric_fmt<double> { static constexpr const char* fmt = "%.2f"; };
|
|
template<> struct metric_fmt<const char*> { static constexpr const char* fmt = "%s"; };
|
|
|
|
template void OSContainer::print_container_metric<unsigned long long int>(outputStream*, const char*, unsigned long long int, const char*);
|
|
template void OSContainer::print_container_metric<unsigned long int>(outputStream*, const char*, unsigned long int, const char*);
|
|
template void OSContainer::print_container_metric<int>(outputStream*, const char*, int, const char*);
|
|
template void OSContainer::print_container_metric<double>(outputStream*, const char*, double, const char*);
|
|
template void OSContainer::print_container_metric<const char*>(outputStream*, const char*, const char*, const char*);
|
|
|
|
template <typename T>
|
|
void OSContainer::print_container_metric(outputStream* st, const char* metrics, T value, const char* unit) {
|
|
constexpr int max_length = 38; // Longest "metric: value" string ("maximum number of tasks: not supported")
|
|
constexpr int longest_value = max_length - 11; // Max length - shortest "metric: " string ("cpu_quota: ")
|
|
char value_str[longest_value + 1] = {};
|
|
os::snprintf_checked(value_str, longest_value, metric_fmt<T>::fmt, value);
|
|
|
|
const int pad_width = max_length - static_cast<int>(strlen(metrics)) - 2; // -2 for the ": "
|
|
const char* unit_prefix = unit[0] != '\0' ? " " : "";
|
|
|
|
char line[128] = {};
|
|
os::snprintf_checked(line, sizeof(line), "%s: %*s%s%s", metrics, pad_width, value_str, unit_prefix, unit);
|
|
st->print_cr("%s", line);
|
|
}
|
|
|
|
void OSContainer::print_container_helper(outputStream* st, MetricResult& res, const char* metrics) {
|
|
if (res.success()) {
|
|
if (res.value() != value_unlimited) {
|
|
if (res.value() >= 1024) {
|
|
print_container_metric(st, metrics, res.value() / K, "kB");
|
|
} else {
|
|
print_container_metric(st, metrics, res.value(), "B");
|
|
}
|
|
} else {
|
|
print_container_metric(st, metrics, "unlimited");
|
|
}
|
|
} else {
|
|
// Not supported
|
|
print_container_metric(st, metrics, "unavailable");
|
|
}
|
|
}
|