diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/container/ContainerLibrary.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/container/ContainerLibrary.java index de197ccd459d..0d5021fed17e 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/container/ContainerLibrary.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/container/ContainerLibrary.java @@ -48,12 +48,12 @@ @CContext(ContainerLibraryDirectives.class) @CLibrary(value = "svm_container", requireStatic = true, dependsOn = "m") // The following annotations are for files in `src/hotspot`, which are copied from the JDK -@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/java.base/share/native/include/jni.h") -@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/java.base/unix/native/include/jni_md.h") +@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/java.base/share/native/include/jni.h") +@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/java.base/unix/native/include/jni_md.h") @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/os/linux/cgroupSubsystem_linux.cpp") @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/os/linux/cgroupSubsystem_linux.hpp") -@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/os/linux/cgroupUtil_linux.cpp") -@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/os/linux/cgroupUtil_linux.hpp") +@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/os/linux/cgroupUtil_linux.cpp") +@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/os/linux/cgroupUtil_linux.hpp") @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp") @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp") @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp") @@ -61,33 +61,34 @@ @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/os/linux/osContainer_linux.cpp") @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/os/linux/osContainer_linux.hpp") @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/os/linux/os_linux.cpp") -@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/os/linux/os_linux.hpp") -@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/os/linux/os_linux.inline.hpp") -@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/os/posix/include/jvm_md.h") +@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/os/linux/os_linux.hpp") +@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/os/linux/os_linux.inline.hpp") +@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/os/posix/include/jvm_md.h") @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+2/src/hotspot/os/posix/os_posix.cpp") -@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+25/src/hotspot/os/posix/os_posix.hpp") -@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/os/posix/os_posix.inline.hpp") -@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+26/src/hotspot/share/memory/allocation.hpp") -@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+21/src/hotspot/share/memory/allocation.inline.hpp") -@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/share/memory/allStatic.hpp") -@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/share/nmt/memTag.hpp") -@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+22/src/hotspot/share/runtime/os.cpp") -@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+25/src/hotspot/share/runtime/os.hpp") -@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/share/runtime/os.inline.hpp") -@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/share/utilities/checkedCast.hpp") -@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+22/src/hotspot/share/utilities/compilerWarnings_gcc.hpp") -@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+22/src/hotspot/share/utilities/compilerWarnings.hpp") -@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/share/utilities/globalDefinitions_gcc.hpp") -@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+23/src/hotspot/share/utilities/globalDefinitions.hpp") -@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+21/src/hotspot/share/utilities/macros.hpp") -@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+23/src/hotspot/share/utilities/ostream.cpp") -@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+23/src/hotspot/share/utilities/ostream.hpp") +@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/os/posix/os_posix.hpp") +@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/os/posix/os_posix.inline.hpp") +@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/share/memory/allocation.hpp") +@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/share/memory/allocation.inline.hpp") +@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/share/memory/allStatic.hpp") +@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/share/nmt/memTag.hpp") +@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/share/runtime/os.cpp") +@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/share/runtime/os.hpp") +@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/share/runtime/os.inline.hpp") +@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/share/utilities/checkedCast.hpp") +@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/share/utilities/compilerWarnings_gcc.hpp") +@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/share/utilities/compilerWarnings.hpp") +@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/share/utilities/globalDefinitions_gcc.hpp") +@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/share/utilities/globalDefinitions.hpp") +@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/share/utilities/macros.hpp") +@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/share/utilities/ostream.cpp") +@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/share/utilities/ostream.hpp") +@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/share/utilities/permitForbiddenFunctions.hpp") // The following annotations are for files in `src/svm`, which are completely customized for SVM -@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/share/logging/log.hpp") -@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+26/src/hotspot/share/memory/allocation.cpp") +@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/share/logging/log.hpp") +@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/share/memory/allocation.cpp") @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/share/runtime/globals.hpp") @BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+3/src/hotspot/share/utilities/debug.cpp") -@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-25+18/src/hotspot/share/utilities/debug.hpp") +@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-26+4/src/hotspot/share/utilities/debug.hpp") public class ContainerLibrary { static final int VERSION = 240100; diff --git a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/cgroupSubsystem_linux.cpp b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/cgroupSubsystem_linux.cpp index 8c671388f573..3e74f42f618c 100644 --- a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/cgroupSubsystem_linux.cpp +++ b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/cgroupSubsystem_linux.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, 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 @@ -66,7 +66,7 @@ CgroupSubsystem* CgroupSubsystemFactory::create() { CgroupV1MemoryController* memory = nullptr; CgroupV1Controller* cpuset = nullptr; CgroupV1CpuController* cpu = nullptr; - CgroupV1Controller* cpuacct = nullptr; + CgroupV1CpuacctController* cpuacct = nullptr; CgroupV1Controller* pids = nullptr; CgroupInfo cg_infos[CG_INFO_LENGTH]; u1 cg_type_flags = INVALID_CGROUPS_GENERIC; @@ -109,9 +109,10 @@ CgroupSubsystem* CgroupSubsystemFactory::create() { CgroupV2CpuController* cpu = new CgroupV2CpuController(CgroupV2Controller(cg_infos[CPU_IDX]._mount_path, cg_infos[CPU_IDX]._cgroup_path, cg_infos[CPU_IDX]._read_only)); + CgroupV2CpuacctController* cpuacct = new CgroupV2CpuacctController(cpu); log_debug(os, container)("Detected cgroups v2 unified hierarchy"); cleanup(cg_infos); - return new CgroupV2Subsystem(memory, cpu, mem_other); + return new CgroupV2Subsystem(memory, cpu, cpuacct, mem_other); } /* @@ -154,7 +155,7 @@ CgroupSubsystem* CgroupSubsystemFactory::create() { cpu = new CgroupV1CpuController(CgroupV1Controller(info._root_mount_path, info._mount_path, info._read_only)); cpu->set_subsystem_path(info._cgroup_path); } else if (strcmp(info._name, "cpuacct") == 0) { - cpuacct = new CgroupV1Controller(info._root_mount_path, info._mount_path, info._read_only); + cpuacct = new CgroupV1CpuacctController(CgroupV1Controller(info._root_mount_path, info._mount_path, info._read_only)); cpuacct->set_subsystem_path(info._cgroup_path); } else if (strcmp(info._name, "pids") == 0) { pids = new CgroupV1Controller(info._root_mount_path, info._mount_path, info._read_only); @@ -301,6 +302,7 @@ bool CgroupSubsystemFactory::determine_type(CgroupInfo* cg_infos, } else { log_debug(os, container)("Can't read %s, %s", controllers_file, os::strerror(errno)); *flags = INVALID_CGROUPS_V2; + fclose(controllers); return false; } for (int i = 0; i < CG_INFO_LENGTH; i++) { @@ -361,7 +363,7 @@ bool CgroupSubsystemFactory::determine_type(CgroupInfo* cg_infos, all_required_controllers_enabled = all_required_controllers_enabled && cg_infos[i]._enabled; } if (log_is_enabled(Debug, os, container) && !cg_infos[i]._enabled) { - log_debug(os, container)("controller %s is not enabled\n", cg_controller_name[i]); + log_debug(os, container)("controller %s is not enabled", cg_controller_name[i]); } } } @@ -859,6 +861,10 @@ jlong CgroupSubsystem::memory_soft_limit_in_bytes() { return memory_controller()->controller()->memory_soft_limit_in_bytes(phys_mem); } +jlong CgroupSubsystem::memory_throttle_limit_in_bytes() { + return memory_controller()->controller()->memory_throttle_limit_in_bytes(); +} + jlong CgroupSubsystem::memory_usage_in_bytes() { return memory_controller()->controller()->memory_usage_in_bytes(); } @@ -887,6 +893,10 @@ int CgroupSubsystem::cpu_shares() { return cpu_controller()->controller()->cpu_shares(); } +jlong CgroupSubsystem::cpu_usage_in_micros() { + return cpuacct_controller()->cpu_usage_in_micros(); +} + #ifndef NATIVE_IMAGE void CgroupSubsystem::print_version_specific_info(outputStream* st) { julong phys_mem = os::Linux::physical_memory(); diff --git a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/cgroupSubsystem_linux.hpp b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/cgroupSubsystem_linux.hpp index a2ea1476d09a..514d84c786bc 100644 --- a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/cgroupSubsystem_linux.hpp +++ b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/cgroupSubsystem_linux.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, 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 @@ -227,6 +227,18 @@ class CgroupCpuController: public CHeapObj { virtual const char* cgroup_path() = 0; }; +// Pure virtual class representing version agnostic CPU accounting controllers +class CgroupCpuacctController: public CHeapObj { + public: + virtual jlong cpu_usage_in_micros() = 0; + virtual bool needs_hierarchy_adjustment() = 0; + virtual bool is_read_only() = 0; + virtual const char* subsystem_path() = 0; + virtual void set_subsystem_path(const char* cgroup_path) = 0; + virtual const char* mount_point() = 0; + virtual const char* cgroup_path() = 0; +}; + // Pure virtual class representing version agnostic memory controllers class CgroupMemoryController: public CHeapObj { public: @@ -235,6 +247,7 @@ class CgroupMemoryController: public CHeapObj { virtual jlong memory_and_swap_limit_in_bytes(julong host_mem, julong host_swap) = 0; virtual jlong memory_and_swap_usage_in_bytes(julong host_mem, julong host_swap) = 0; virtual jlong memory_soft_limit_in_bytes(julong upper_bound) = 0; + virtual jlong memory_throttle_limit_in_bytes() = 0; virtual jlong memory_max_usage_in_bytes() = 0; virtual jlong rss_usage_in_bytes() = 0; virtual jlong cache_usage_in_bytes() = 0; @@ -263,15 +276,19 @@ class CgroupSubsystem: public CHeapObj { virtual const char * container_type() = 0; virtual CachingCgroupController* memory_controller() = 0; virtual CachingCgroupController* cpu_controller() = 0; + virtual CgroupCpuacctController* cpuacct_controller() = 0; int cpu_quota(); int cpu_period(); int cpu_shares(); + jlong cpu_usage_in_micros(); + jlong memory_usage_in_bytes(); jlong memory_and_swap_limit_in_bytes(); jlong memory_and_swap_usage_in_bytes(); jlong memory_soft_limit_in_bytes(); + jlong memory_throttle_limit_in_bytes(); jlong memory_max_usage_in_bytes(); jlong rss_usage_in_bytes(); jlong cache_usage_in_bytes(); diff --git a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp index a21f4ab8446d..ee5c09bd51c2 100644 --- a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp +++ b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/cgroupV1Subsystem_linux.cpp @@ -252,10 +252,16 @@ jlong CgroupV1MemoryController::memory_soft_limit_in_bytes(julong phys_mem) { } } +jlong CgroupV1MemoryController::memory_throttle_limit_in_bytes() { + // Log this string at trace level so as to make tests happy. + log_trace(os, container)("Memory Throttle Limit is not supported."); + return OSCONTAINER_ERROR; // not supported +} + // Constructor CgroupV1Subsystem::CgroupV1Subsystem(CgroupV1Controller* cpuset, CgroupV1CpuController* cpu, - CgroupV1Controller* cpuacct, + CgroupV1CpuacctController* cpuacct, CgroupV1Controller* pids, CgroupV1MemoryController* memory) : _cpuset(cpuset), @@ -422,6 +428,13 @@ int CgroupV1CpuController::cpu_shares() { return shares_int; } +jlong CgroupV1CpuacctController::cpu_usage_in_micros() { + julong cpu_usage; + CONTAINER_READ_NUMBER_CHECKED(reader(), "/cpuacct.usage", "CPU Usage", cpu_usage); + // Output is in nanoseconds, convert to microseconds. + return (jlong)cpu_usage / 1000; +} + /* pids_max * * Return the maximum number of tasks available to the process diff --git a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp index 28c9a8166e61..4019e46d52e5 100644 --- a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp +++ b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/cgroupV1Subsystem_linux.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2025, 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 @@ -86,6 +86,7 @@ class CgroupV1MemoryController final : public CgroupMemoryController { jlong memory_and_swap_limit_in_bytes(julong host_mem, julong host_swap) override; jlong memory_and_swap_usage_in_bytes(julong host_mem, julong host_swap) override; jlong memory_soft_limit_in_bytes(julong upper_bound) override; + jlong memory_throttle_limit_in_bytes() override; jlong memory_max_usage_in_bytes() override; jlong rss_usage_in_bytes() override; jlong cache_usage_in_bytes() override; @@ -146,12 +147,41 @@ class CgroupV1CpuController final : public CgroupCpuController { } }; +class CgroupV1CpuacctController final : public CgroupCpuacctController { + + private: + CgroupV1Controller _reader; + CgroupV1Controller* reader() { return &_reader; } + public: + jlong cpu_usage_in_micros() override; + void set_subsystem_path(const char *cgroup_path) override { + reader()->set_subsystem_path(cgroup_path); + } + bool is_read_only() override { + return reader()->is_read_only(); + } + const char* subsystem_path() override { + return reader()->subsystem_path(); + } + const char* mount_point() override { + return reader()->mount_point(); + } + bool needs_hierarchy_adjustment() override { + return reader()->needs_hierarchy_adjustment(); + } + const char* cgroup_path() override { return reader()->cgroup_path(); } + + public: + CgroupV1CpuacctController(const CgroupV1Controller& reader) : _reader(reader) { + } +}; + class CgroupV1Subsystem: public CgroupSubsystem { public: CgroupV1Subsystem(CgroupV1Controller* cpuset, CgroupV1CpuController* cpu, - CgroupV1Controller* cpuacct, + CgroupV1CpuacctController* cpuacct, CgroupV1Controller* pids, CgroupV1MemoryController* memory); @@ -171,13 +201,14 @@ class CgroupV1Subsystem: public CgroupSubsystem { } CachingCgroupController* memory_controller() { return _memory; } CachingCgroupController* cpu_controller() { return _cpu; } + CgroupCpuacctController* cpuacct_controller() { return _cpuacct; } private: /* controllers */ CachingCgroupController* _memory = nullptr; CgroupV1Controller* _cpuset = nullptr; CachingCgroupController* _cpu = nullptr; - CgroupV1Controller* _cpuacct = nullptr; + CgroupV1CpuacctController* _cpuacct = nullptr; CgroupV1Controller* _pids = nullptr; }; diff --git a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp index 519f3eef814d..7015bc6c9206 100644 --- a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp +++ b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/cgroupV2Subsystem_linux.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2025, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -119,12 +119,14 @@ int CgroupV2CpuController::cpu_quota() { // Constructor CgroupV2Subsystem::CgroupV2Subsystem(CgroupV2MemoryController * memory, CgroupV2CpuController* cpu, + CgroupV2CpuacctController* cpuacct, CgroupV2Controller unified) : _unified(unified) { CgroupUtil::adjust_controller(memory); CgroupUtil::adjust_controller(cpu); _memory = new CachingCgroupController(memory); _cpu = new CachingCgroupController(cpu); + _cpuacct = cpuacct; } bool CgroupV2Subsystem::is_containerized() { @@ -157,6 +159,17 @@ int CgroupV2CpuController::cpu_period() { return period; } +jlong CgroupV2CpuController::cpu_usage_in_micros() { + julong cpu_usage; + bool is_ok = reader()->read_numerical_key_value("/cpu.stat", "usage_usec", &cpu_usage); + if (!is_ok) { + log_trace(os, container)("CPU Usage failed: %d", OSCONTAINER_ERROR); + return OSCONTAINER_ERROR; + } + log_trace(os, container)("CPU Usage is: " JULONG_FORMAT, cpu_usage); + return (jlong)cpu_usage; +} + /* memory_usage_in_bytes * * Return the amount of used memory used by this cgroup and descendents @@ -178,10 +191,16 @@ jlong CgroupV2MemoryController::memory_soft_limit_in_bytes(julong phys_mem) { return mem_soft_limit; } +jlong CgroupV2MemoryController::memory_throttle_limit_in_bytes() { + jlong mem_throttle_limit; + CONTAINER_READ_NUMBER_CHECKED_MAX(reader(), "/memory.high", "Memory Throttle Limit", mem_throttle_limit); + return mem_throttle_limit; +} + jlong CgroupV2MemoryController::memory_max_usage_in_bytes() { - // Log this string at trace level so as to make tests happy. - log_trace(os, container)("Maximum Memory Usage is not supported."); - return OSCONTAINER_ERROR; // not supported + julong mem_max_usage; + CONTAINER_READ_NUMBER_CHECKED(reader(), "/memory.peak", "Maximum Memory Usage", mem_max_usage); + return mem_max_usage; } jlong CgroupV2MemoryController::rss_usage_in_bytes() { diff --git a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp index 3dd6000d4202..1d754321265f 100644 --- a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp +++ b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/cgroupV2Subsystem_linux.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2024, Red Hat Inc. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * @@ -66,6 +66,34 @@ class CgroupV2CpuController: public CgroupCpuController { int cpu_quota() override; int cpu_period() override; int cpu_shares() override; + jlong cpu_usage_in_micros(); + bool is_read_only() override { + return reader()->is_read_only(); + } + const char* subsystem_path() override { + return reader()->subsystem_path(); + } + bool needs_hierarchy_adjustment() override { + return reader()->needs_hierarchy_adjustment(); + } + void set_subsystem_path(const char* cgroup_path) override { + reader()->set_subsystem_path(cgroup_path); + } + const char* mount_point() override { return reader()->mount_point(); } + const char* cgroup_path() override { return reader()->cgroup_path(); } +}; + +class CgroupV2CpuacctController: public CgroupCpuacctController { + private: + CgroupV2CpuController* _reader; + CgroupV2CpuController* reader() { return _reader; } + public: + CgroupV2CpuacctController(CgroupV2CpuController* reader) : _reader(reader) { + } + // In cgroup v2, cpu usage is a part of the cpu controller. + jlong cpu_usage_in_micros() override { + return reader()->cpu_usage_in_micros(); + } bool is_read_only() override { return reader()->is_read_only(); } @@ -94,6 +122,7 @@ class CgroupV2MemoryController final: public CgroupMemoryController { jlong memory_and_swap_limit_in_bytes(julong host_mem, julong host_swp) override; jlong memory_and_swap_usage_in_bytes(julong host_mem, julong host_swp) override; jlong memory_soft_limit_in_bytes(julong upper_bound) override; + jlong memory_throttle_limit_in_bytes() override; jlong memory_usage_in_bytes() override; jlong memory_max_usage_in_bytes() override; jlong rss_usage_in_bytes() override; @@ -125,11 +154,14 @@ class CgroupV2Subsystem: public CgroupSubsystem { CachingCgroupController* _memory = nullptr; CachingCgroupController* _cpu = nullptr; + CgroupCpuacctController* _cpuacct = nullptr; + CgroupV2Controller* unified() { return &_unified; } public: CgroupV2Subsystem(CgroupV2MemoryController * memory, CgroupV2CpuController* cpu, + CgroupV2CpuacctController* cpuacct, CgroupV2Controller unified); char * cpu_cpuset_cpus() override; @@ -144,6 +176,7 @@ class CgroupV2Subsystem: public CgroupSubsystem { } CachingCgroupController* memory_controller() override { return _memory; } CachingCgroupController* cpu_controller() override { return _cpu; } + CgroupCpuacctController* cpuacct_controller() override { return _cpuacct; }; }; diff --git a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/osContainer_linux.cpp b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/osContainer_linux.cpp index 5f18730f1d0f..9c8c2ee1eeee 100644 --- a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/osContainer_linux.cpp +++ b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/osContainer_linux.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2025, 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 @@ -126,6 +126,11 @@ jlong OSContainer::memory_soft_limit_in_bytes() { return cgroup_subsystem->memory_soft_limit_in_bytes(); } +jlong OSContainer::memory_throttle_limit_in_bytes() { + assert(cgroup_subsystem != nullptr, "cgroup subsystem not available"); + return cgroup_subsystem->memory_throttle_limit_in_bytes(); +} + jlong OSContainer::memory_usage_in_bytes() { assert(cgroup_subsystem != nullptr, "cgroup subsystem not available"); return cgroup_subsystem->memory_usage_in_bytes(); @@ -183,6 +188,11 @@ int OSContainer::cpu_shares() { return cgroup_subsystem->cpu_shares(); } +jlong OSContainer::cpu_usage_in_micros() { + assert(cgroup_subsystem != nullptr, "cgroup subsystem not available"); + return cgroup_subsystem->cpu_usage_in_micros(); +} + jlong OSContainer::pids_max() { assert(cgroup_subsystem != nullptr, "cgroup subsystem not available"); return cgroup_subsystem->pids_max(); diff --git a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/osContainer_linux.hpp b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/osContainer_linux.hpp index 1e9484c40d8f..50437e8649f6 100644 --- a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/osContainer_linux.hpp +++ b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/osContainer_linux.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2025, 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 @@ -60,6 +60,7 @@ class OSContainer: AllStatic { 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_throttle_limit_in_bytes(); static jlong memory_usage_in_bytes(); static jlong memory_max_usage_in_bytes(); static jlong rss_usage_in_bytes(); @@ -75,6 +76,8 @@ class OSContainer: AllStatic { static int cpu_shares(); + static jlong cpu_usage_in_micros(); + static jlong pids_max(); static jlong pids_current(); }; diff --git a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/os_linux.cpp b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/os_linux.cpp index b5d5bc1bd235..d78947241ad8 100644 --- a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/os_linux.cpp +++ b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/linux/os_linux.cpp @@ -64,9 +64,7 @@ #include "runtime/osThread.hpp" #include "runtime/perfMemory.hpp" #include "runtime/sharedRuntime.hpp" -#include "runtime/statSampler.hpp" #include "runtime/stubRoutines.hpp" -#include "runtime/threadCritical.hpp" #include "runtime/threads.hpp" #include "runtime/threadSMR.hpp" #include "runtime/timer.hpp" @@ -151,11 +149,6 @@ #define MAX_PATH (2 * K) -#define MAX_SECS 100000000 - -// for timer info max values which include all bits -#define ALL_64_BITS CONST64(0xFFFFFFFFFFFFFFFF) - #ifdef MUSL_LIBC // dlvsym is not a part of POSIX // and musl libc doesn't implement it. @@ -244,8 +237,6 @@ typedef int (*malloc_info_func_t)(int options, FILE *stream); static malloc_info_func_t g_malloc_info = nullptr; #endif // __GLIBC__ -static int clock_tics_per_sec = 100; - // If the VM might have been created on the primordial thread, we need to resolve the // primordial thread stack bounds and check if the current thread might be the // primordial thread in places. If we know that the primordial thread is never used, @@ -1101,10 +1092,23 @@ bool os::create_thread(Thread* thread, ThreadType thr_type, ResourceMark rm; pthread_t tid; int ret = 0; - int limit = 3; - do { + int trials_remaining = 4; + useconds_t next_delay = 1000; + while (true) { ret = pthread_create(&tid, &attr, (void* (*)(void*)) thread_native_entry, thread); - } while (ret == EAGAIN && limit-- > 0); + + if (ret != EAGAIN) { + break; + } + + if (--trials_remaining <= 0) { + break; + } + + log_debug(os, thread)("Failed to start native thread (%s), retrying after %dus.", os::errno_name(ret), next_delay); + ::usleep(next_delay); + next_delay *= 2; + } char buf[64]; if (ret == 0) { @@ -1706,7 +1710,7 @@ void * os::dll_load(const char *filename, char *ebuf, int ebuflen) { } ThreadInVMfromNative tiv(jt); - debug_only(VMNativeEntryWrapper vew;) + DEBUG_ONLY(VMNativeEntryWrapper vew;) VM_LinuxDllLoad op(filename, ebuf, ebuflen); VMThread::execute(&op); @@ -2520,9 +2524,18 @@ bool os::Linux::print_container_info(outputStream* st) { st->print_cr("%s", i == OSCONTAINER_ERROR ? "not supported" : "no shares"); } + jlong j = OSContainer::cpu_usage_in_micros(); + st->print("cpu_usage_in_micros: "); + if (j >= 0) { + st->print_cr(JLONG_FORMAT, j); + } else { + st->print_cr("%s", j == OSCONTAINER_ERROR ? "not supported" : "no usage"); + } + OSContainer::print_container_helper(st, OSContainer::memory_limit_in_bytes(), "memory_limit_in_bytes"); OSContainer::print_container_helper(st, OSContainer::memory_and_swap_limit_in_bytes(), "memory_and_swap_limit_in_bytes"); OSContainer::print_container_helper(st, OSContainer::memory_soft_limit_in_bytes(), "memory_soft_limit_in_bytes"); + OSContainer::print_container_helper(st, OSContainer::memory_throttle_limit_in_bytes(), "memory_throttle_limit_in_bytes"); OSContainer::print_container_helper(st, OSContainer::memory_usage_in_bytes(), "memory_usage_in_bytes"); OSContainer::print_container_helper(st, OSContainer::memory_max_usage_in_bytes(), "memory_max_usage_in_bytes"); OSContainer::print_container_helper(st, OSContainer::rss_usage_in_bytes(), "rss_usage_in_bytes"); @@ -2530,7 +2543,7 @@ bool os::Linux::print_container_info(outputStream* st) { OSContainer::print_version_specific_info(st); - jlong j = OSContainer::pids_max(); + j = OSContainer::pids_max(); st->print("maximum number of tasks: "); if (j > 0) { st->print_cr(JLONG_FORMAT, j); @@ -2779,139 +2792,9 @@ void os::get_summary_cpu_info(char* cpuinfo, size_t length) { #endif } -static char saved_jvm_path[MAXPATHLEN] = {0}; - -// Find the full path to the current module, libjvm.so -void os::jvm_path(char *buf, jint buflen) { - // Error checking. - if (buflen < MAXPATHLEN) { - assert(false, "must use a large-enough buffer"); - buf[0] = '\0'; - return; - } - // Lazy resolve the path to current module. - if (saved_jvm_path[0] != 0) { - strcpy(buf, saved_jvm_path); - return; - } - - char dli_fname[MAXPATHLEN]; - dli_fname[0] = '\0'; - bool ret = dll_address_to_library_name( - CAST_FROM_FN_PTR(address, os::jvm_path), - dli_fname, sizeof(dli_fname), nullptr); - assert(ret, "cannot locate libjvm"); - char *rp = nullptr; - if (ret && dli_fname[0] != '\0') { - rp = os::realpath(dli_fname, buf, buflen); - } - if (rp == nullptr) { - return; - } - - if (Arguments::sun_java_launcher_is_altjvm()) { - // Support for the java launcher's '-XXaltjvm=' option. Typical - // value for buf is "/jre/lib//libjvm.so". - // If "/jre/lib/" appears at the right place in the string, then - // assume we are installed in a JDK and we're done. Otherwise, check - // for a JAVA_HOME environment variable and fix up the path so it - // looks like libjvm.so is installed there (append a fake suffix - // hotspot/libjvm.so). - const char *p = buf + strlen(buf) - 1; - for (int count = 0; p > buf && count < 5; ++count) { - for (--p; p > buf && *p != '/'; --p) - /* empty */ ; - } - - if (strncmp(p, "/jre/lib/", 9) != 0) { - // Look for JAVA_HOME in the environment. - char* java_home_var = ::getenv("JAVA_HOME"); - if (java_home_var != nullptr && java_home_var[0] != 0) { - char* jrelib_p; - int len; - - // Check the current module name "libjvm.so". - p = strrchr(buf, '/'); - if (p == nullptr) { - return; - } - assert(strstr(p, "/libjvm") == p, "invalid library name"); - - rp = os::realpath(java_home_var, buf, buflen); - if (rp == nullptr) { - return; - } - - // determine if this is a legacy image or modules image - // modules image doesn't have "jre" subdirectory - len = checked_cast(strlen(buf)); - assert(len < buflen, "Ran out of buffer room"); - jrelib_p = buf + len; - snprintf(jrelib_p, buflen-len, "/jre/lib"); - if (0 != access(buf, F_OK)) { - snprintf(jrelib_p, buflen-len, "/lib"); - } - - if (0 == access(buf, F_OK)) { - // Use current module name "libjvm.so" - len = (int)strlen(buf); - snprintf(buf + len, buflen-len, "/hotspot/libjvm.so"); - } else { - // Go back to path of .so - rp = os::realpath(dli_fname, buf, buflen); - if (rp == nullptr) { - return; - } - } - } - } - } - - strncpy(saved_jvm_path, buf, MAXPATHLEN); - saved_jvm_path[MAXPATHLEN - 1] = '\0'; -} - //////////////////////////////////////////////////////////////////////////////// // Virtual Memory -// Rationale behind this function: -// current (Mon Apr 25 20:12:18 MSD 2005) oprofile drops samples without executable -// mapping for address (see lookup_dcookie() in the kernel module), thus we cannot get -// samples for JITted code. Here we create private executable mapping over the code cache -// and then we can use standard (well, almost, as mapping can change) way to provide -// info for the reporting script by storing timestamp and location of symbol -void linux_wrap_code(char* base, size_t size) { - static volatile jint cnt = 0; - - static_assert(sizeof(off_t) == 8, "Expected Large File Support in this file"); - - if (!UseOprofile) { - return; - } - - char buf[PATH_MAX+1]; - int num = Atomic::add(&cnt, 1); - - snprintf(buf, sizeof(buf), "%s/hs-vm-%d-%d", - os::get_temp_directory(), os::current_process_id(), num); - unlink(buf); - - int fd = ::open(buf, O_CREAT | O_RDWR, S_IRWXU); - - if (fd != -1) { - off_t rv = ::lseek(fd, size-2, SEEK_SET); - if (rv != (off_t)-1) { - if (::write(fd, "", 1) == 1) { - mmap(base, size, - PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_PRIVATE|MAP_FIXED|MAP_NORESERVE, fd, 0); - } - } - ::close(fd); - unlink(buf); - } -} - static bool recoverable_mmap_error(int err) { // See if the error is one we can let the caller handle. This // list of errno values comes from JBS-6843484. I can't find a @@ -4441,8 +4324,6 @@ static void check_pax(void) { // this is called _before_ most of the global arguments have been parsed void os::init(void) { char dummy; // used to get a guess on initial stack address - - clock_tics_per_sec = checked_cast(sysconf(_SC_CLK_TCK)); int sys_pg_size = checked_cast(sysconf(_SC_PAGESIZE)); if (sys_pg_size < 0) { fatal("os_linux.cpp: os::init: sysconf failed (%s)", @@ -4635,7 +4516,7 @@ static void workaround_expand_exec_shield_cs_limit() { */ char* hint = (char*)(os::Linux::initial_thread_stack_bottom() - (StackOverflow::stack_guard_zone_size() + page_size)); - char* codebuf = os::attempt_reserve_memory_at(hint, page_size, false, mtThread); + char* codebuf = os::attempt_reserve_memory_at(hint, page_size, mtThread); if (codebuf == nullptr) { // JDK-8197429: There may be a stack gap of one megabyte between @@ -4643,7 +4524,7 @@ static void workaround_expand_exec_shield_cs_limit() { // Linux kernel workaround for CVE-2017-1000364. If we failed to // map our codebuf, try again at an address one megabyte lower. hint -= 1 * M; - codebuf = os::attempt_reserve_memory_at(hint, page_size, false, mtThread); + codebuf = os::attempt_reserve_memory_at(hint, page_size, mtThread); } if ((codebuf == nullptr) || (!os::commit_memory(codebuf, page_size, true))) { @@ -5197,21 +5078,21 @@ static jlong slow_thread_cpu_time(Thread *thread, bool user_sys_cpu_time) { &user_time, &sys_time); if (count != 13) return -1; if (user_sys_cpu_time) { - return ((jlong)sys_time + (jlong)user_time) * (1000000000 / clock_tics_per_sec); + return ((jlong)sys_time + (jlong)user_time) * (1000000000 / os::Posix::clock_tics_per_second()); } else { - return (jlong)user_time * (1000000000 / clock_tics_per_sec); + return (jlong)user_time * (1000000000 / os::Posix::clock_tics_per_second()); } } void os::current_thread_cpu_time_info(jvmtiTimerInfo *info_ptr) { - info_ptr->max_value = ALL_64_BITS; // will not wrap in less than 64 bits + info_ptr->max_value = all_bits_jlong; // will not wrap in less than 64 bits info_ptr->may_skip_backward = false; // elapsed time not wall time info_ptr->may_skip_forward = false; // elapsed time not wall time info_ptr->kind = JVMTI_TIMER_TOTAL_CPU; // user+system time is returned } void os::thread_cpu_time_info(jvmtiTimerInfo *info_ptr) { - info_ptr->max_value = ALL_64_BITS; // will not wrap in less than 64 bits + info_ptr->max_value = all_bits_jlong; // will not wrap in less than 64 bits info_ptr->may_skip_backward = false; // elapsed time not wall time info_ptr->may_skip_forward = false; // elapsed time not wall time info_ptr->kind = JVMTI_TIMER_TOTAL_CPU; // user+system time is returned diff --git a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/posix/os_posix.cpp b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/posix/os_posix.cpp index 08f1fed53010..dc1026104838 100644 --- a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/posix/os_posix.cpp +++ b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/posix/os_posix.cpp @@ -25,6 +25,7 @@ #ifndef NATIVE_IMAGE #include "classfile/classLoader.hpp" +#include "interpreter/interpreter.hpp" #include "jvm.h" #include "jvmtifiles/jvmti.h" #include "logging/log.hpp" @@ -55,6 +56,7 @@ #include "utilities/formatBuffer.hpp" #include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" +#include "utilities/permitForbiddenFunctions.hpp" #include "utilities/vmError.hpp" #if INCLUDE_JFR #include "jfr/support/jfrNativeLibraryLoadEvent.hpp" @@ -63,6 +65,7 @@ #ifdef AIX #include "loadlib_aix.hpp" #include "os_aix.hpp" +#include "porting_aix.hpp" #endif #ifdef LINUX #include "os_linux.hpp" @@ -500,9 +503,9 @@ static char* chop_extra_memory(size_t size, size_t alignment, char* extra_base, // Multiple threads can race in this code, and can remap over each other with MAP_FIXED, // so on posix, unmap the section at the start and at the end of the chunk that we mapped // rather than unmapping and remapping the whole chunk to get requested alignment. -char* os::reserve_memory_aligned(size_t size, size_t alignment, bool exec) { +char* os::reserve_memory_aligned(size_t size, size_t alignment, MemTag mem_tag, bool exec) { size_t extra_size = calculate_aligned_extra_size(size, alignment); - char* extra_base = os::reserve_memory(extra_size, exec); + char* extra_base = os::reserve_memory(extra_size, mem_tag, exec); if (extra_base == nullptr) { return nullptr; } @@ -939,11 +942,11 @@ ssize_t os::connect(int fd, struct sockaddr* him, socklen_t len) { } void os::exit(int num) { - ALLOW_C_FUNCTION(::exit, ::exit(num);) + permit_forbidden_function::exit(num); } void os::_exit(int num) { - ALLOW_C_FUNCTION(::_exit, ::_exit(num);) + permit_forbidden_function::_exit(num); } void os::naked_yield() { @@ -1000,7 +1003,7 @@ char* os::realpath(const char* filename, char* outbuf, size_t outbuflen) { // This assumes platform realpath() is implemented according to POSIX.1-2008. // POSIX.1-2008 allows to specify null for the output buffer, in which case // output buffer is dynamically allocated and must be ::free()'d by the caller. - ALLOW_C_FUNCTION(::realpath, char* p = ::realpath(filename, nullptr);) + char* p = permit_forbidden_function::realpath(filename, nullptr); if (p != nullptr) { if (strlen(p) < outbuflen) { strcpy(outbuf, p); @@ -1008,7 +1011,7 @@ char* os::realpath(const char* filename, char* outbuf, size_t outbuflen) { } else { errno = ENAMETOOLONG; } - ALLOW_C_FUNCTION(::free, ::free(p);) // *not* os::free + permit_forbidden_function::free(p); // *not* os::free } else { // Fallback for platforms struggling with modern Posix standards (AIX 5.3, 6.1). If realpath // returns EINVAL, this may indicate that realpath is not POSIX.1-2008 compatible and @@ -1017,7 +1020,7 @@ char* os::realpath(const char* filename, char* outbuf, size_t outbuflen) { // a memory overwrite. if (errno == EINVAL) { outbuf[outbuflen - 1] = '\0'; - ALLOW_C_FUNCTION(::realpath, p = ::realpath(filename, outbuf);) + p = permit_forbidden_function::realpath(filename, outbuf); if (p != nullptr) { guarantee(outbuf[outbuflen - 1] == '\0', "realpath buffer overwrite detected."); result = p; @@ -1077,6 +1080,95 @@ bool os::same_files(const char* file1, const char* file2) { return is_same; } +static char saved_jvm_path[MAXPATHLEN] = {0}; + +// Find the full path to the current module, libjvm.so +void os::jvm_path(char *buf, jint buflen) { + // Error checking. + if (buflen < MAXPATHLEN) { + assert(false, "must use a large-enough buffer"); + buf[0] = '\0'; + return; + } + // Lazy resolve the path to current module. + if (saved_jvm_path[0] != 0) { + strcpy(buf, saved_jvm_path); + return; + } + + const char* fname; +#ifdef AIX + Dl_info dlinfo; + int ret = dladdr(CAST_FROM_FN_PTR(void *, os::jvm_path), &dlinfo); + assert(ret != 0, "cannot locate libjvm"); + if (ret == 0) { + return; + } + fname = dlinfo.dli_fname; +#else + char dli_fname[MAXPATHLEN]; + dli_fname[0] = '\0'; + bool ret = dll_address_to_library_name( + CAST_FROM_FN_PTR(address, os::jvm_path), + dli_fname, sizeof(dli_fname), nullptr); + assert(ret, "cannot locate libjvm"); + if (!ret) { + return; + } + fname = dli_fname; +#endif // AIX + char* rp = nullptr; + if (fname[0] != '\0') { + rp = os::realpath(fname, buf, buflen); + } + if (rp == nullptr) { + return; + } + + // If executing unit tests we require JAVA_HOME to point to the real JDK. + if (Arguments::executing_unit_tests()) { + // Look for JAVA_HOME in the environment. + char* java_home_var = ::getenv("JAVA_HOME"); + if (java_home_var != nullptr && java_home_var[0] != 0) { + + // Check the current module name "libjvm.so". + const char* p = strrchr(buf, '/'); + if (p == nullptr) { + return; + } + assert(strstr(p, "/libjvm") == p, "invalid library name"); + + stringStream ss(buf, buflen); + rp = os::realpath(java_home_var, buf, buflen); + if (rp == nullptr) { + return; + } + + assert((int)strlen(buf) < buflen, "Ran out of buffer room"); + ss.print("%s/lib", buf); + + // If the path exists within JAVA_HOME, add the VM variant directory and JVM + // library name to complete the path to JVM being overridden. Otherwise fallback + // to the path to the current library. + if (0 == access(buf, F_OK)) { + // Use current module name "libjvm.so" + ss.print("/%s/libjvm%s", Abstract_VM_Version::vm_variant(), JNI_LIB_SUFFIX); + assert(strcmp(buf + strlen(buf) - strlen(JNI_LIB_SUFFIX), JNI_LIB_SUFFIX) == 0, + "buf has been truncated"); + } else { + // Go back to path of .so + rp = os::realpath(fname, buf, buflen); + if (rp == nullptr) { + return; + } + } + } + } + + strncpy(saved_jvm_path, buf, MAXPATHLEN); + saved_jvm_path[MAXPATHLEN - 1] = '\0'; +} + // Called when creating the thread. The minimum stack sizes have already been calculated size_t os::Posix::get_initial_stack_size(ThreadType thr_type, size_t req_stack_size) { size_t stack_size; @@ -1344,6 +1436,19 @@ void os::Posix::init_2(void) { _use_clock_monotonic_condattr ? "CLOCK_MONOTONIC" : "the default clock"); } +int os::Posix::clock_tics_per_second() { + return clock_tics_per_sec; +} + +#ifdef ASSERT +bool os::Posix::ucontext_is_interpreter(const ucontext_t* uc) { + assert(uc != nullptr, "invariant"); + address pc = os::Posix::ucontext_get_pc(uc); + assert(pc != nullptr, "invariant"); + return Interpreter::contains(pc); +} +#endif + // Utility to convert the given timeout to an absolute timespec // (based on the appropriate clock) to use with pthread_cond_timewait, // and sem_timedwait(). @@ -1491,12 +1596,9 @@ jlong os::javaTimeNanos() { return result; } -// for timer info max values which include all bits -#define ALL_64_BITS CONST64(0xFFFFFFFFFFFFFFFF) - void os::javaTimeNanos_info(jvmtiTimerInfo *info_ptr) { // CLOCK_MONOTONIC - amount of time since some arbitrary point in the past - info_ptr->max_value = ALL_64_BITS; + info_ptr->max_value = all_bits_jlong; info_ptr->may_skip_backward = false; // not subject to resetting or drifting info_ptr->may_skip_forward = false; // not subject to resetting or drifting info_ptr->kind = JVMTI_TIMER_ELAPSED; // elapsed not CPU time diff --git a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/posix/os_posix.hpp b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/posix/os_posix.hpp index b6dae92257c5..bd92702bd097 100644 --- a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/posix/os_posix.hpp +++ b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/os/posix/os_posix.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2025, 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 @@ -94,8 +94,13 @@ class os::Posix { static address ucontext_get_pc(const ucontext_t* ctx); static void ucontext_set_pc(ucontext_t* ctx, address pc); + DEBUG_ONLY(static bool ucontext_is_interpreter(const ucontext_t* ctx);) + static void to_RTC_abstime(timespec* abstime, int64_t millis); + // clock ticks per second of the system + static int clock_tics_per_second(); + static bool handle_stack_overflow(JavaThread* thread, address addr, address pc, const void* ucVoid, address* stub); diff --git a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/memory/allocation.hpp b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/memory/allocation.hpp index 092e55a722ea..970446354c0e 100644 --- a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/memory/allocation.hpp +++ b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/memory/allocation.hpp @@ -319,7 +319,12 @@ class MetaspaceObj { f(ConstantPoolCache) \ f(Annotations) \ f(MethodCounters) \ - f(RecordComponent) + f(RecordComponent) \ + f(KlassTrainingData) \ + f(MethodTrainingData) \ + f(CompileTrainingData) \ + f(AdapterHandlerEntry) \ + f(AdapterFingerPrint) #define METASPACE_OBJ_TYPE_DECLARE(name) name ## Type, #define METASPACE_OBJ_TYPE_NAME_CASE(name) case name ## Type: return #name; @@ -357,6 +362,8 @@ class MetaspaceObj { void* operator new(size_t size, ClassLoaderData* loader_data, size_t word_size, Type type) throw(); + // This is used for allocating training data. We are allocating training data in many cases where a GC cannot be triggered. + void* operator new(size_t size, MemTag flags); void operator delete(void* p) = delete; // Declare a *static* method with the same signature in any subclass of MetaspaceObj diff --git a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/memory/allocation.inline.hpp b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/memory/allocation.inline.hpp index 39047a40e010..9304d21b704b 100644 --- a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/memory/allocation.inline.hpp +++ b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/memory/allocation.inline.hpp @@ -69,7 +69,7 @@ template E* MmapArrayAllocator::allocate_or_null(size_t length, MemTag mem_tag) { size_t size = size_for(length); - char* addr = os::reserve_memory(size, !ExecMem, mem_tag); + char* addr = os::reserve_memory(size, mem_tag); if (addr == nullptr) { return nullptr; } @@ -86,7 +86,7 @@ template E* MmapArrayAllocator::allocate(size_t length, MemTag mem_tag) { size_t size = size_for(length); - char* addr = os::reserve_memory(size, !ExecMem, mem_tag); + char* addr = os::reserve_memory(size, mem_tag); if (addr == nullptr) { vm_exit_out_of_memory(size, OOM_MMAP_ERROR, "Allocator (reserve)"); } diff --git a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/runtime/os.cpp b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/runtime/os.cpp index d4f7b258f954..714fcc806931 100644 --- a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/runtime/os.cpp +++ b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/runtime/os.cpp @@ -84,7 +84,11 @@ #include "utilities/defaultStream.hpp" #include "utilities/events.hpp" #include "utilities/fastrand.hpp" +#include "utilities/globalDefinitions.hpp" #include "utilities/macros.hpp" +#endif // !NATIVE_IMAGE +#include "utilities/permitForbiddenFunctions.hpp" +#ifndef NATIVE_IMAGE #include "utilities/powerOfTwo.hpp" #ifdef LINUX @@ -137,7 +141,7 @@ int os::snprintf_checked(char* buf, size_t len, const char* fmt, ...) { } int os::vsnprintf(char* buf, size_t len, const char* fmt, va_list args) { - ALLOW_C_FUNCTION(::vsnprintf, int result = ::vsnprintf(buf, len, fmt, args);) + int result = permit_forbidden_function::vsnprintf(buf, len, fmt, args); // If an encoding error occurred (result < 0) then it's not clear // whether the buffer is NUL terminated, so ensure it is. if ((result < 0) && (len > 0)) { @@ -262,7 +266,7 @@ char* os::iso8601_time(jlong milliseconds_since_19700101, char* buffer, size_t b } OSReturn os::set_priority(Thread* thread, ThreadPriority p) { - debug_only(Thread::check_for_dangling_thread_pointer(thread);) + DEBUG_ONLY(Thread::check_for_dangling_thread_pointer(thread);) if ((p >= MinPriority && p <= MaxPriority) || (p == CriticalPriority && thread->is_ConcurrentGC_thread())) { @@ -618,7 +622,7 @@ char *os::strdup(const char *str, MemTag mem_tag) { size_t size = strlen(str); char *dup_str = (char *)malloc(size + 1, mem_tag); if (dup_str == nullptr) return nullptr; - strcpy(dup_str, str); + memcpy(dup_str, str, size + 1); return dup_str; } @@ -706,7 +710,7 @@ void* os::malloc(size_t size, MemTag mem_tag, const NativeCallStack& stack) { return nullptr; } - ALLOW_C_FUNCTION(::malloc, void* const outer_ptr = ::malloc(outer_size);) + void* const outer_ptr = permit_forbidden_function::malloc(outer_size); if (outer_ptr == nullptr) { return nullptr; } @@ -773,7 +777,7 @@ void* os::realloc(void *memblock, size_t size, MemTag mem_tag, const NativeCallS header->mark_block_as_dead(); // the real realloc - ALLOW_C_FUNCTION(::realloc, void* const new_outer_ptr = ::realloc(header, new_outer_size);) + void* const new_outer_ptr = permit_forbidden_function::realloc(header, new_outer_size); if (new_outer_ptr == nullptr) { // realloc(3) failed and the block still exists. @@ -801,7 +805,7 @@ void* os::realloc(void *memblock, size_t size, MemTag mem_tag, const NativeCallS } else { // NMT disabled. - ALLOW_C_FUNCTION(::realloc, rc = ::realloc(memblock, size);) + rc = permit_forbidden_function::realloc(memblock, size); if (rc == nullptr) { return nullptr; } @@ -829,7 +833,7 @@ void os::free(void *memblock) { // When NMT is enabled this checks for heap overwrites, then deaccounts the old block. void* const old_outer_ptr = MemTracker::record_free(memblock); - ALLOW_C_FUNCTION(::free, ::free(old_outer_ptr);) + permit_forbidden_function::free(old_outer_ptr); } void os::init_random(unsigned int initval) { @@ -1218,7 +1222,7 @@ void os::print_cpu_info(outputStream* st, char* buf, size_t buflen) { // We access the raw value here because the assert in the accessor will // fail if the crash occurs before initialization of this value. st->print(" (initial active %d)", _initial_active_processor_count); - st->print(" %s", VM_Version::features_string()); + st->print(" %s", VM_Version::cpu_info_string()); st->cr(); pd_print_cpu_info(st, buf, buflen); } @@ -1588,6 +1592,57 @@ bool os::set_boot_path(char fileSep, char pathSep) { return false; } +static char* _image_release_file_content = nullptr; + +void os::read_image_release_file() { + assert(_image_release_file_content == nullptr, "release file content must not be already set"); + const char* home = Arguments::get_java_home(); + stringStream ss; + ss.print("%s/release", home); + + FILE* file = fopen(ss.base(), "rb"); + if (file == nullptr) { + return; + } + fseek(file, 0, SEEK_END); + long sz = ftell(file); + if (sz == -1) { + fclose(file); + return; + } + fseek(file, 0, SEEK_SET); + + char* tmp = (char*) os::malloc(sz + 1, mtInternal); + if (tmp == nullptr) { + fclose(file); + return; + } + + size_t elements_read = fread(tmp, 1, sz, file); + if (elements_read < (size_t)sz) { + tmp[elements_read] = '\0'; + } else { + tmp[sz] = '\0'; + } + // issues with \r in line endings on Windows, so better replace those + for (size_t i = 0; i < elements_read; i++) { + if (tmp[i] == '\r') { + tmp[i] = ' '; + } + } + Atomic::release_store(&_image_release_file_content, tmp); + fclose(file); +} + +void os::print_image_release_file(outputStream* st) { + char* ifrc = Atomic::load_acquire(&_image_release_file_content); + if (ifrc != nullptr) { + st->print_cr("%s", ifrc); + } else { + st->print_cr(""); + } +} + #endif // !NATIVE_IMAGE bool os::file_exists(const char* filename) { struct stat statbuf; @@ -1974,7 +2029,7 @@ bool os::create_stack_guard_pages(char* addr, size_t bytes) { return os::pd_create_stack_guard_pages(addr, bytes); } -char* os::reserve_memory(size_t bytes, bool executable, MemTag mem_tag) { +char* os::reserve_memory(size_t bytes, MemTag mem_tag, bool executable) { char* result = pd_reserve_memory(bytes, executable); if (result != nullptr) { MemTracker::record_virtual_memory_reserve(result, bytes, CALLER_PC, mem_tag); @@ -1985,7 +2040,7 @@ char* os::reserve_memory(size_t bytes, bool executable, MemTag mem_tag) { return result; } -char* os::attempt_reserve_memory_at(char* addr, size_t bytes, bool executable, MemTag mem_tag) { +char* os::attempt_reserve_memory_at(char* addr, size_t bytes, MemTag mem_tag, bool executable) { char* result = SimulateFullAddressSpace ? nullptr : pd_attempt_reserve_memory_at(addr, bytes, executable); if (result != nullptr) { MemTracker::record_virtual_memory_reserve((address)result, bytes, CALLER_PC, mem_tag); @@ -2191,7 +2246,7 @@ char* os::attempt_reserve_memory_between(char* min, char* max, size_t bytes, siz assert(is_aligned(result, alignment), "alignment invalid (" ERRFMT ")", ERRFMTARGS); log_trace(os, map)(ERRFMT, ERRFMTARGS); log_debug(os, map)("successfully attached at " PTR_FORMAT, p2i(result)); - MemTracker::record_virtual_memory_reserve((address)result, bytes, CALLER_PC); + MemTracker::record_virtual_memory_reserve((address)result, bytes, CALLER_PC, mtNone); } else { log_debug(os, map)("failed to attach anywhere in [" PTR_FORMAT "-" PTR_FORMAT ")", p2i(min), p2i(max)); } @@ -2259,6 +2314,11 @@ void os::commit_memory_or_exit(char* addr, size_t size, size_t alignment_hint, MemTracker::record_virtual_memory_commit((address)addr, size, CALLER_PC); } +// The scope of NmtVirtualMemoryLocker covers both pd_uncommit_memory and record_virtual_memory_uncommit because +// these operations must happen atomically to avoid races causing NMT to fall out os sync with the OS reality. +// We do not have the same lock protection for pd_commit_memory and record_virtual_memory_commit. +// We assume that there is some external synchronization that prevents a region from being uncommitted +// before it is finished being committed. bool os::uncommit_memory(char* addr, size_t bytes, bool executable) { assert_nonempty_range(addr, bytes); bool res; @@ -2281,6 +2341,11 @@ bool os::uncommit_memory(char* addr, size_t bytes, bool executable) { return res; } +// The scope of NmtVirtualMemoryLocker covers both pd_release_memory and record_virtual_memory_release because +// these operations must happen atomically to avoid races causing NMT to fall out os sync with the OS reality. +// We do not have the same lock protection for pd_reserve_memory and record_virtual_memory_reserve. +// We assume that there is some external synchronization that prevents a region from being released +// before it is finished being reserved. bool os::release_memory(char* addr, size_t bytes) { assert_nonempty_range(addr, bytes); bool res; @@ -2358,8 +2423,8 @@ char* os::attempt_map_memory_to_file_at(char* addr, size_t bytes, int file_desc, } char* os::map_memory(int fd, const char* file_name, size_t file_offset, - char *addr, size_t bytes, bool read_only, - bool allow_exec, MemTag mem_tag) { + char *addr, size_t bytes, MemTag mem_tag, + bool read_only, bool allow_exec) { char* result = pd_map_memory(fd, file_name, file_offset, addr, bytes, read_only, allow_exec); if (result != nullptr) { MemTracker::record_virtual_memory_reserve_and_commit((address)result, bytes, CALLER_PC, mem_tag); @@ -2397,7 +2462,7 @@ char* os::reserve_memory_special(size_t size, size_t alignment, size_t page_size char* result = pd_reserve_memory_special(size, alignment, page_size, addr, executable); if (result != nullptr) { // The memory is committed - MemTracker::record_virtual_memory_reserve_and_commit((address)result, size, CALLER_PC); + MemTracker::record_virtual_memory_reserve_and_commit((address)result, size, CALLER_PC, mtNone); log_debug(os, map)("Reserved and committed " RANGEFMT, RANGEFMTARGS(result, size)); } else { log_info(os, map)("Reserve and commit failed (%zu bytes)", size); @@ -2593,7 +2658,7 @@ char* os::build_agent_function_name(const char *sym_name, const char *lib_name, if ((start = strrchr(lib_name, *os::file_separator())) != nullptr) { lib_name = ++start; } -#ifdef WINDOWS +#ifdef _WINDOWS else { // Need to check for drive prefix e.g. C:L.dll if ((start = strchr(lib_name, ':')) != nullptr) { lib_name = ++start; diff --git a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/runtime/os.hpp b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/runtime/os.hpp index c443cd12adaa..187f48caccac 100644 --- a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/runtime/os.hpp +++ b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/runtime/os.hpp @@ -478,14 +478,14 @@ class os: AllStatic { inline static size_t cds_core_region_alignment(); // Reserves virtual memory. - static char* reserve_memory(size_t bytes, bool executable = false, MemTag mem_tag = mtNone); + static char* reserve_memory(size_t bytes, MemTag mem_tag, bool executable = false); // Reserves virtual memory that starts at an address that is aligned to 'alignment'. - static char* reserve_memory_aligned(size_t size, size_t alignment, bool executable = false); + static char* reserve_memory_aligned(size_t size, size_t alignment, MemTag mem_tag, bool executable = false); // Attempts to reserve the virtual memory at [addr, addr + bytes). // Does not overwrite existing mappings. - static char* attempt_reserve_memory_at(char* addr, size_t bytes, bool executable = false, MemTag mem_tag = mtNone); + static char* attempt_reserve_memory_at(char* addr, size_t bytes, MemTag mem_tag, bool executable = false); // Given an address range [min, max), attempts to reserve memory within this area, with the given alignment. // If randomize is true, the location will be randomized. @@ -537,16 +537,16 @@ class os: AllStatic { static int create_file_for_heap(const char* dir); // Map memory to the file referred by fd. This function is slightly different from map_memory() // and is added to be used for implementation of -XX:AllocateHeapAt - static char* map_memory_to_file(size_t size, int fd, MemTag mem_tag = mtNone); - static char* map_memory_to_file_aligned(size_t size, size_t alignment, int fd, MemTag mem_tag = mtNone); + static char* map_memory_to_file(size_t size, int fd, MemTag mem_tag); + static char* map_memory_to_file_aligned(size_t size, size_t alignment, int fd, MemTag mem_tag); static char* map_memory_to_file(char* base, size_t size, int fd); - static char* attempt_map_memory_to_file_at(char* base, size_t size, int fd, MemTag mem_tag = mtNone); + static char* attempt_map_memory_to_file_at(char* base, size_t size, int fd, MemTag mem_tag); // Replace existing reserved memory with file mapping static char* replace_existing_mapping_with_file_mapping(char* base, size_t size, int fd); static char* map_memory(int fd, const char* file_name, size_t file_offset, - char *addr, size_t bytes, bool read_only = false, - bool allow_exec = false, MemTag mem_tag = mtNone); + char *addr, size_t bytes, MemTag mem_tag, bool read_only = false, + bool allow_exec = false); static bool unmap_memory(char *addr, size_t bytes); static void disclaim_memory(char *addr, size_t bytes); static void realign_memory(char *addr, size_t bytes, size_t alignment_hint); @@ -644,6 +644,7 @@ class os: AllStatic { static address fetch_frame_from_context(const void* ucVoid, intptr_t** sp, intptr_t** fp); static frame fetch_frame_from_context(const void* ucVoid); static frame fetch_compiled_frame_from_context(const void* ucVoid); + static intptr_t* fetch_bcp_from_context(const void* ucVoid); // For saving an os specific context generated by an assert or guarantee. static void save_assert_context(const void* ucVoid); @@ -694,7 +695,12 @@ class os: AllStatic { static jlong lseek(int fd, jlong offset, int whence); #endif // !NATIVE_IMAGE static bool file_exists(const char* file); + #ifndef NATIVE_IMAGE + // read/store and print the release file of the image + static void read_image_release_file(); + static void print_image_release_file(outputStream* st); + // This function, on Windows, canonicalizes a given path (see os_windows.cpp for details). // On Posix, this function is a noop: it does not change anything and just returns // the input pointer. diff --git a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/utilities/compilerWarnings.hpp b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/utilities/compilerWarnings.hpp index a71f641b3311..56f19cb5f4f6 100644 --- a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/utilities/compilerWarnings.hpp +++ b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/utilities/compilerWarnings.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2025, 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 @@ -63,9 +63,6 @@ #ifndef PRAGMA_FORMAT_NONLITERAL_IGNORED #define PRAGMA_FORMAT_NONLITERAL_IGNORED #endif -#ifndef PRAGMA_FORMAT_IGNORED -#define PRAGMA_FORMAT_IGNORED -#endif #ifndef PRAGMA_STRINGOP_TRUNCATION_IGNORED #define PRAGMA_STRINGOP_TRUNCATION_IGNORED @@ -89,23 +86,61 @@ // Support warnings for use of certain C functions, except where explicitly // permitted. + +// FORBID_C_FUNCTION(Signature, Alternative) +// - Signature: the function that should not normally be used. +// - Alternative: a string literal that may be used in a warning about a use, +// often suggesting an alternative. +// Declares the C-linkage function designated by Signature to be deprecated, +// using the `deprecated` attribute with Alternative as an argument. // -// FORBID_C_FUNCTION(signature, alternative) -// - signature: the function that should not normally be used. -// - alternative: a string that may be used in a warning about a use, typically -// suggesting an alternative. +// The variants with IMPORTED in the name are to deal with Windows +// requirements, using FORBIDDEN_FUNCTION_IMPORT_SPEC. See the Visual +// Studio definition of that macro for more details. The default has +// an empty expansion. The potentially added spec must precede the +// base signature but follow all attributes. // -// ALLOW_C_FUNCTION(name, ... using statement ...) -// - name: the name of a forbidden function whose use is permitted in statement. -// - statement: a use of the otherwise forbidden function. Using a variadic -// tail allows the statement to contain non-nested commas. +// FORBID_NORETURN_C_FUNCTION deals with a clang issue. See the clang +// definition of FORBIDDEN_FUNCTION_NORETURN_ATTRIBUTE for more +// details. The default expands to `[[noreturn]]`. +#define FORBID_C_FUNCTION(Signature, Alternative) \ + extern "C" { [[deprecated(Alternative)]] Signature; } -#ifndef FORBID_C_FUNCTION -#define FORBID_C_FUNCTION(signature, alternative) +#ifndef FORBIDDEN_FUNCTION_IMPORT_SPEC +#define FORBIDDEN_FUNCTION_IMPORT_SPEC #endif -#ifndef ALLOW_C_FUNCTION -#define ALLOW_C_FUNCTION(name, ...) __VA_ARGS__ +#ifndef FORBIDDEN_FUNCTION_NORETURN_ATTRIBUTE +#define FORBIDDEN_FUNCTION_NORETURN_ATTRIBUTE [[noreturn]] #endif +#ifndef FORBIDDEN_FUNCTION_IGNORE_CLANG_FORTIFY_WARNING +#define FORBIDDEN_FUNCTION_IGNORE_CLANG_FORTIFY_WARNING +#endif + +#define FORBID_IMPORTED_C_FUNCTION(Signature, Alternative) \ + FORBID_C_FUNCTION(FORBIDDEN_FUNCTION_IMPORT_SPEC Signature, Alternative) + +#define FORBID_NORETURN_C_FUNCTION(Signature, Alternative) \ + FORBID_C_FUNCTION(FORBIDDEN_FUNCTION_NORETURN_ATTRIBUTE Signature, Alternative) + +#define FORBID_IMPORTED_NORETURN_C_FUNCTION(Signature, Alternative) \ + FORBID_NORETURN_C_FUNCTION(FORBIDDEN_FUNCTION_IMPORT_SPEC Signature, Alternative) + +// A BEGIN/END_ALLOW_FORBIDDEN_FUNCTIONS pair establishes a scope in which the +// deprecation warnings used to forbid the use of certain functions are +// suppressed. These macros are not intended for warning suppression at +// individual call sites; see permitForbiddenFunctions.hpp for the approach +// taken for that where needed. Rather, these are used to suppress warnings +// from 3rd-party code included by HotSpot, such as the gtest framework and +// C++ Standard Library headers, which may refer to functions that are +// disallowed in other parts of HotSpot. They are also used in the +// implementation of the "permit" mechanism. +#define BEGIN_ALLOW_FORBIDDEN_FUNCTIONS \ + PRAGMA_DIAG_PUSH \ + PRAGMA_DEPRECATED_IGNORED + +#define END_ALLOW_FORBIDDEN_FUNCTIONS \ + PRAGMA_DIAG_POP + #endif // SHARE_UTILITIES_COMPILERWARNINGS_HPP diff --git a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/utilities/compilerWarnings_gcc.hpp b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/utilities/compilerWarnings_gcc.hpp index f11b4eb8af24..0687b784399a 100644 --- a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/utilities/compilerWarnings_gcc.hpp +++ b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/utilities/compilerWarnings_gcc.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2025, 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 @@ -52,8 +52,6 @@ PRAGMA_DISABLE_GCC_WARNING("-Wformat-nonliteral") \ PRAGMA_DISABLE_GCC_WARNING("-Wformat-security") -#define PRAGMA_FORMAT_IGNORED PRAGMA_DISABLE_GCC_WARNING("-Wformat") - // Disable -Wstringop-truncation which is introduced in GCC 8. // https://gcc.gnu.org/gcc-8/changes.html #if !defined(__clang_major__) && (__GNUC__ >= 8) @@ -71,32 +69,71 @@ #define PRAGMA_ZERO_AS_NULL_POINTER_CONSTANT_IGNORED \ PRAGMA_DISABLE_GCC_WARNING("-Wzero-as-null-pointer-constant") -#if (__GNUC__ >= 10) -// TODO: Re-enable warning attribute for Clang once -// https://github.com/llvm/llvm-project/issues/56519 is fixed and released. -// || (defined(__clang_major__) && (__clang_major__ >= 14)) +#define PRAGMA_DEPRECATED_IGNORED \ + PRAGMA_DISABLE_GCC_WARNING("-Wdeprecated-declarations") + +// This macro is used by the NORETURN variants of FORBID_C_FUNCTION. +// +// The [[noreturn]] attribute requires that the first declaration of a +// function has it if any have it. +// +// gcc, clang, and MSVC all provide compiler-specific alternatives to that +// attribute: __attribute__((noreturn)) for gcc and clang, +// __declspec(noreturn) for MSVC and clang. gcc and MSVC treat their +// respective compiler-specific alternatives as satisfying that requirement. +// clang does not. +// +// So clang warns if we use [[noreturn]] in the forbidding declaration and the +// library header has already been included and uses the compiler-specific +// attribute. Similarly, clang warns if we use the compiler-specific attribute +// while the library uses [[noreturn]] and the library header is included +// after the forbidding declaration. +// +// For now, we're only going to worry about the standard library, and not +// noreturn functions in some other library that we might want to forbid in +// the future. If there's more than one library to be accounted for, then +// things may get more complicated. +// +// There are several ways we could deal with this. +// +// Probably the most robust is to use the same style of noreturn attribute as +// is used by the library providing the function. That way it doesn't matter +// in which order the inclusion of the library header and the forbidding are +// performed. We could use configure to determine which to use and provide a +// macro to select on here. +// +// Another approach is to always use __attribute__ noreturn in the forbidding +// declaration, but ensure the relevant library header has been included +// before the forbidding declaration. Since there are currently only a couple +// of affected functions, this is easier to implement. So this is the +// approach being taken for now. +// +// clang's failure to treat the compiler-specific form as counting toward the +// [[noreturn]] requirement is arguably a clang bug. +// https://github.com/llvm/llvm-project/issues/131700 + +#ifdef __clang__ +#define FORBIDDEN_FUNCTION_NORETURN_ATTRIBUTE __attribute__((__noreturn__)) +#endif -// Use "warning" attribute to detect uses of "forbidden" functions. +// This macro is used to suppress a warning for some uses of FORBID_C_FUNCTION. // -// Note: The warning attribute is available since GCC 9, but disabling pragmas -// does not work reliably in ALLOW_C_FUNCTION. GCC 10+ and up work fine. +// libstdc++ provides inline definitions of some functions to support +// _FORTIFY_SOURCE. clang warns about our forbidding declaration adding the +// [[deprecated]] attribute following such a definition: +// "warning: attribute declaration must precede definition [-Wignored-attributes]" +// Use this macro to suppress the warning, not getting protection when using +// that combination. Other build combinations should provide sufficient +// coverage. // -// Note: _FORTIFY_SOURCE transforms calls to certain functions into calls to -// associated "checking" functions, and that transformation seems to occur -// *before* the attribute check. We use fortification in fastdebug builds, -// so uses of functions that are both forbidden and fortified won't cause -// forbidden warnings in such builds. -#define FORBID_C_FUNCTION(signature, alternative) \ - extern "C" __attribute__((__warning__(alternative))) signature; - -// Disable warning attribute over the scope of the affected statement. -// The name serves only to document the intended function. -#define ALLOW_C_FUNCTION(name, ...) \ - PRAGMA_DIAG_PUSH \ - PRAGMA_DISABLE_GCC_WARNING("-Wattribute-warning") \ - __VA_ARGS__ \ - PRAGMA_DIAG_POP - -#endif // gcc10+ +// clang's warning in this case is arguably a clang bug. +// https://github.com/llvm/llvm-project/issues/135481 +// This issue has been fixed, with the fix probably appearing in clang 21. +#if defined(__clang__) && defined(_FORTIFY_SOURCE) +#if _FORTIFY_SOURCE > 0 +#define FORBIDDEN_FUNCTION_IGNORE_CLANG_FORTIFY_WARNING \ + PRAGMA_DISABLE_GCC_WARNING("-Wignored-attributes") +#endif +#endif #endif // SHARE_UTILITIES_COMPILERWARNINGS_GCC_HPP diff --git a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/utilities/globalDefinitions.hpp b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/utilities/globalDefinitions.hpp index 5223f0f8ed4b..b475b02dbcee 100644 --- a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/utilities/globalDefinitions.hpp +++ b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/utilities/globalDefinitions.hpp @@ -28,6 +28,9 @@ #include "utilities/compilerWarnings.hpp" #include "utilities/debug.hpp" +#ifndef NATIVE_IMAGE +#include "utilities/forbiddenFunctions.hpp" +#endif // !NATIVE_IMAGE #include "utilities/macros.hpp" #ifndef NATIVE_IMAGE @@ -190,35 +193,6 @@ inline uintptr_t p2u(const volatile void* p) { #define BOOL_TO_STR(_b_) ((_b_) ? "true" : "false") -//---------------------------------------------------------------------------------------------------- -// Forbid the use of various C library functions. -// Some of these have os:: replacements that should normally be used instead. -// Others are considered security concerns, with preferred alternatives. - -FORBID_C_FUNCTION(void exit(int), "use os::exit"); -FORBID_C_FUNCTION(void _exit(int), "use os::exit"); -FORBID_C_FUNCTION(char* strerror(int), "use os::strerror"); -FORBID_C_FUNCTION(char* strtok(char*, const char*), "use strtok_r"); -FORBID_C_FUNCTION(int sprintf(char*, const char*, ...), "use os::snprintf"); -FORBID_C_FUNCTION(int vsprintf(char*, const char*, va_list), "use os::vsnprintf"); -FORBID_C_FUNCTION(int vsnprintf(char*, size_t, const char*, va_list), "use os::vsnprintf"); - -// All of the following functions return raw C-heap pointers (sometimes as an option, e.g. realpath or getwd) -// or, in case of free(), take raw C-heap pointers. Don't use them unless you are really sure you must. -FORBID_C_FUNCTION(void* malloc(size_t size), "use os::malloc"); -FORBID_C_FUNCTION(void* calloc(size_t nmemb, size_t size), "use os::malloc and zero out manually"); -FORBID_C_FUNCTION(void free(void *ptr), "use os::free"); -FORBID_C_FUNCTION(void* realloc(void *ptr, size_t size), "use os::realloc"); -FORBID_C_FUNCTION(char* strdup(const char *s), "use os::strdup"); -FORBID_C_FUNCTION(char* strndup(const char *s, size_t n), "don't use"); -FORBID_C_FUNCTION(int posix_memalign(void **memptr, size_t alignment, size_t size), "don't use"); -FORBID_C_FUNCTION(void* aligned_alloc(size_t alignment, size_t size), "don't use"); -FORBID_C_FUNCTION(char* realpath(const char* path, char* resolved_path), "use os::realpath"); -FORBID_C_FUNCTION(char* get_current_dir_name(void), "use os::get_current_directory()"); -FORBID_C_FUNCTION(char* getwd(char *buf), "use os::get_current_directory()"); -FORBID_C_FUNCTION(wchar_t* wcsdup(const wchar_t *s), "don't use"); -FORBID_C_FUNCTION(void* reallocf(void *ptr, size_t size), "don't use"); - //---------------------------------------------------------------------------------------------------- // Constants @@ -321,6 +295,9 @@ namespace svm_container { const jlong min_jlong = CONST64(0x8000000000000000); const jlong max_jlong = CONST64(0x7fffffffffffffff); +// for timer info max values which include all bits, 0xffffffffffffffff +const jlong all_bits_jlong = ~jlong(0); + #ifndef NATIVE_IMAGE //------------------------------------------- // Constant for jdouble @@ -334,7 +311,6 @@ const size_t K = 1024; const size_t M = K*K; const size_t G = M*K; #ifndef NATIVE_IMAGE -const size_t HWperKB = K / sizeof(HeapWord); // Constants for converting from a base unit to milli-base units. For // example from seconds to milliseconds and microseconds @@ -1207,7 +1183,7 @@ inline bool is_even(intx x) { return !is_odd(x); } // abs methods which cannot overflow and so are well-defined across // the entire domain of integer types. -static inline unsigned int uabs(unsigned int n) { +static inline unsigned int g_uabs(unsigned int n) { union { unsigned int result; int value; @@ -1216,7 +1192,7 @@ static inline unsigned int uabs(unsigned int n) { if (value < 0) result = 0-result; return result; } -static inline julong uabs(julong n) { +static inline julong g_uabs(julong n) { union { julong result; jlong value; @@ -1225,8 +1201,8 @@ static inline julong uabs(julong n) { if (value < 0) result = 0-result; return result; } -static inline julong uabs(jlong n) { return uabs((julong)n); } -static inline unsigned int uabs(int n) { return uabs((unsigned int)n); } +static inline julong g_uabs(jlong n) { return g_uabs((julong)n); } +static inline unsigned int g_uabs(int n) { return g_uabs((unsigned int)n); } // "to" should be greater than "from." inline size_t byte_size(void* from, void* to) { diff --git a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/utilities/macros.hpp b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/utilities/macros.hpp index 5dbec43da020..876af4e1e74c 100644 --- a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/utilities/macros.hpp +++ b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/utilities/macros.hpp @@ -378,13 +378,10 @@ #define DEBUG_ONLY(code) code #define NOT_DEBUG(code) #define NOT_DEBUG_RETURN /*next token must be ;*/ -// Historical. -#define debug_only(code) code #else // ASSERT #define DEBUG_ONLY(code) #define NOT_DEBUG(code) code #define NOT_DEBUG_RETURN {} -#define debug_only(code) #endif // ASSERT #ifdef _LP64 diff --git a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/utilities/ostream.cpp b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/utilities/ostream.cpp index 812c6ba19afb..d1b72989cb67 100644 --- a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/utilities/ostream.cpp +++ b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/utilities/ostream.cpp @@ -170,6 +170,12 @@ void outputStream::do_vsnprintf_and_write(const char* format, va_list ap, bool a } } +bool outputStream::set_autoindent(bool value) { + const bool old = _autoindent; + _autoindent = value; + return old; +} + void outputStream::print(const char* format, ...) { va_list ap; va_start(ap, format); @@ -244,10 +250,6 @@ void outputStream::cr() { this->write("\n", 1); } -void outputStream::cr_indent() { - cr(); indent(); -} - void outputStream::stamp() { if (! _stamp.is_updated()) { _stamp.update(); // start at 0 on first call to stamp() @@ -296,12 +298,6 @@ outputStream& outputStream::indent() { return *this; } -bool outputStream::set_autoindent(bool value) { - const bool old = _autoindent; - _autoindent = value; - return old; -} - void outputStream::print_jlong(jlong value) { print(JLONG_FORMAT, value); } @@ -319,16 +315,16 @@ void outputStream::print_julong(julong value) { * 0000020: 0000 0000 0000 0040 0000 0000 0000 015d .......@.......] * ... * - * indent is applied to each line. Ends with a CR. + * Ends with a CR. */ void outputStream::print_data(void* data, size_t len, bool with_ascii, bool rel_addr) { size_t limit = (len + 16) / 16 * 16; for (size_t i = 0; i < limit; ++i) { if (i % 16 == 0) { if (rel_addr) { - indent().print("%07" PRIxPTR ":", i); + print("%07" PRIxPTR ":", i); } else { - indent().print(PTR_FORMAT ":", p2i((unsigned char*)data + i)); + print(PTR_FORMAT ":", p2i((unsigned char*)data + i)); } } if (i % 2 == 0) { diff --git a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/utilities/ostream.hpp b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/utilities/ostream.hpp index 494d90d5ce8d..24c66ca59d74 100644 --- a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/utilities/ostream.hpp +++ b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/utilities/ostream.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2025, 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 @@ -58,6 +58,8 @@ DEBUG_ONLY(class ResourceMark;) namespace svm_container { class outputStream : public CHeapObjBase { + friend class StreamIndentor; + #ifndef NATIVE_IMAGE private: NONCOPYABLE(outputStream); @@ -103,6 +105,9 @@ class outputStream : public CHeapObjBase { void do_vsnprintf_and_write(const char* format, va_list ap, bool add_cr) ATTRIBUTE_PRINTF(2, 0); #endif // !NATIVE_IMAGE + // Automatic indentation. Returns old autoindent state. + bool set_autoindent(bool value); + public: #ifndef NATIVE_IMAGE class TestSupport; // Unit test support @@ -121,14 +126,6 @@ class outputStream : public CHeapObjBase { int fill_to(int col); void move_to(int col, int slop = 6, int min_space = 2); - // Automatic indentation: - // If autoindent mode is on, the following APIs will automatically indent - // line starts depending on the current indentation level: - // print(), print_cr(), print_raw(), print_raw_cr() - // Other APIs are unaffected - // Returns old autoindent state. - bool set_autoindent(bool value); - // sizing int position() const { return _position; } julong count() const { return _precount + _position; } @@ -154,7 +151,6 @@ class outputStream : public CHeapObjBase { void put(char ch); void sp(int count = 1); void cr(); - void cr_indent(); void bol() { if (_position > 0) cr(); } @@ -198,25 +194,29 @@ class outputStream : public CHeapObjBase { // ANSI C++ name collision extern outputStream* tty; // tty output -class streamIndentor : public StackObj { - outputStream* const _str; - const int _amount; - NONCOPYABLE(streamIndentor); -public: - streamIndentor(outputStream* str, int amt = 2) : _str(str), _amount(amt) { - _str->inc(_amount); - } - ~streamIndentor() { _str->dec(_amount); } -}; +// outputStream indentation. When used, indentation is automatically applied +// when printing on the stream using the following APIs: +// print(), print_cr(), print_raw(), print_raw_cr() +class StreamIndentor { + private: + outputStream* const _stream; + const int _indentation; + const bool _old_autoindent; + NONCOPYABLE(StreamIndentor); -class StreamAutoIndentor : public StackObj { - outputStream* const _os; - const bool _old; - NONCOPYABLE(StreamAutoIndentor); public: - StreamAutoIndentor(outputStream* os) : - _os(os), _old(os->set_autoindent(true)) {} - ~StreamAutoIndentor() { _os->set_autoindent(_old); } + StreamIndentor(outputStream* os, int indentation) : + _stream(os), + _indentation(indentation), + _old_autoindent(_stream->set_autoindent(true)) { + + _stream->inc(_indentation); + } + + ~StreamIndentor() { + _stream->dec(_indentation); + _stream->set_autoindent(_old_autoindent); + } }; // advisory locking for the shared tty stream: diff --git a/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/utilities/permitForbiddenFunctions.hpp b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/utilities/permitForbiddenFunctions.hpp new file mode 100644 index 000000000000..6b9e406bcdee --- /dev/null +++ b/substratevm/src/com.oracle.svm.native.libcontainer/src/hotspot/share/utilities/permitForbiddenFunctions.hpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2024, 2025, 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +#ifndef SHARE_UTILITIES_PERMITFORBIDDENFUNCTIONS_HPP +#define SHARE_UTILITIES_PERMITFORBIDDENFUNCTIONS_HPP + +#include "utilities/compilerWarnings.hpp" +#include "utilities/globalDefinitions.hpp" + +#ifndef NATIVE_IMAGE +#ifdef _WINDOWS +#include "permitForbiddenFunctions_windows.hpp" +#else +#include "permitForbiddenFunctions_posix.hpp" +#endif +#endif // !NATIVE_IMAGE + +// Provide wrappers for some functions otherwise forbidden from use in HotSpot. +// +// There may be special circumstances where an otherwise forbidden function +// really does need to be used. One example is in the implementation of a +// corresponding os:: function. +// +// Wrapper functions are provided for such forbidden functions. These +// wrappers are defined in a context where the forbidding warnings are +// suppressed. They are defined in a special namespace, to highlight uses as +// unusual and requiring increased scrutiny. +// +// Note that there are several seemingly plausible shorter alternatives to +// these written-out wrapper functions. All that have been tried don't work +// for one reason or another. + + +namespace svm_container { + +namespace permit_forbidden_function { +BEGIN_ALLOW_FORBIDDEN_FUNCTIONS + +#ifndef NATIVE_IMAGE +[[noreturn]] inline void exit(int status) { ::exit(status); } +[[noreturn]] inline void _exit(int status) { ::_exit(status); } +#endif // !NATIVE_IMAGE + +ATTRIBUTE_PRINTF(3, 0) +inline int vsnprintf(char* str, size_t size, const char* format, va_list ap) { + return ::vsnprintf(str, size, format, ap); +} + +#ifndef NATIVE_IMAGE +inline void* malloc(size_t size) { return ::malloc(size); } +inline void free(void* ptr) { return ::free(ptr); } +inline void* calloc(size_t nmemb, size_t size) { return ::calloc(nmemb, size); } +inline void* realloc(void* ptr, size_t size) { return ::realloc(ptr, size); } + +inline char* strdup(const char* s) { return ::strdup(s); } +#endif // !NATIVE_IMAGE + +END_ALLOW_FORBIDDEN_FUNCTIONS +} // namespace permit_forbidden_function + + +} // namespace svm_container + +#endif // SHARE_UTILITIES_PERMITFORBIDDENFUNCTIONS_HPP