diff --git a/src/hotspot/os/aix/os_aix.cpp b/src/hotspot/os/aix/os_aix.cpp index da7e94af19b..91a9bbcff71 100644 --- a/src/hotspot/os/aix/os_aix.cpp +++ b/src/hotspot/os/aix/os_aix.cpp @@ -3002,3 +3002,9 @@ bool os::supports_map_sync() { } void os::print_memory_mappings(char* addr, size_t bytes, outputStream* st) {} + +#if INCLUDE_JFR + +void os::jfr_report_memory_info() {} + +#endif // INCLUDE_JFR diff --git a/src/hotspot/os/bsd/os_bsd.cpp b/src/hotspot/os/bsd/os_bsd.cpp index 4e3d53ee942..79ea61c2536 100644 --- a/src/hotspot/os/bsd/os_bsd.cpp +++ b/src/hotspot/os/bsd/os_bsd.cpp @@ -69,6 +69,9 @@ #include "utilities/events.hpp" #include "utilities/growableArray.hpp" #include "utilities/vmError.hpp" +#if INCLUDE_JFR +#include "jfr/jfrEvents.hpp" +#endif // put OS-includes here # include @@ -101,6 +104,7 @@ #endif #ifdef __APPLE__ + #include #include #endif @@ -2453,3 +2457,31 @@ bool os::start_debugging(char *buf, int buflen) { } void os::print_memory_mappings(char* addr, size_t bytes, outputStream* st) {} + +#if INCLUDE_JFR + +void os::jfr_report_memory_info() { +#ifdef __APPLE__ + mach_task_basic_info info; + mach_msg_type_number_t count = MACH_TASK_BASIC_INFO_COUNT; + + kern_return_t ret = task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &count); + if (ret == KERN_SUCCESS) { + // Send the RSS JFR event + EventResidentSetSize event; + event.set_size(info.resident_size); + event.set_peak(info.resident_size_max); + event.commit(); + } else { + // Log a warning + static bool first_warning = true; + if (first_warning) { + log_warning(jfr)("Error fetching RSS values: task_info failed"); + first_warning = false; + } + } + +#endif // __APPLE__ +} + +#endif // INCLUDE_JFR diff --git a/src/hotspot/os/linux/os_linux.cpp b/src/hotspot/os/linux/os_linux.cpp index 6c659e6c7cc..a77476e218a 100644 --- a/src/hotspot/os/linux/os_linux.cpp +++ b/src/hotspot/os/linux/os_linux.cpp @@ -77,6 +77,9 @@ #include "utilities/macros.hpp" #include "utilities/powerOfTwo.hpp" #include "utilities/vmError.hpp" +#if INCLUDE_JFR +#include "jfr/jfrEvents.hpp" +#endif // put OS-includes here # include @@ -2470,6 +2473,28 @@ void os::pd_print_cpu_info(outputStream* st, char* buf, size_t buflen) { print_sys_devices_cpu_info(st); } +#if INCLUDE_JFR + +void os::jfr_report_memory_info() { + os::Linux::meminfo_t info; + if (os::Linux::query_process_memory_info(&info)) { + // Send the RSS JFR event + EventResidentSetSize event; + event.set_size(info.vmrss * K); + event.set_peak(info.vmhwm * K); + event.commit(); + } else { + // Log a warning + static bool first_warning = true; + if (first_warning) { + log_warning(jfr)("Error fetching RSS values: query_process_memory_info failed"); + first_warning = false; + } + } +} + +#endif // INCLUDE_JFR + #if defined(AMD64) || defined(IA32) || defined(X32) const char* search_string = "model name"; #elif defined(M68K) diff --git a/src/hotspot/os/windows/os_windows.cpp b/src/hotspot/os/windows/os_windows.cpp index bb5b6f6490c..69c957135c0 100644 --- a/src/hotspot/os/windows/os_windows.cpp +++ b/src/hotspot/os/windows/os_windows.cpp @@ -78,6 +78,9 @@ #include "utilities/macros.hpp" #include "utilities/vmError.hpp" #include "windbghelp.hpp" +#if INCLUDE_JFR +#include "jfr/jfrEvents.hpp" +#endif #ifdef _DEBUG #include @@ -6022,6 +6025,33 @@ void os::print_memory_mappings(char* addr, size_t bytes, outputStream* st) { } } +#if INCLUDE_JFR + +void os::jfr_report_memory_info() { + PROCESS_MEMORY_COUNTERS_EX pmex; + ZeroMemory(&pmex, sizeof(PROCESS_MEMORY_COUNTERS_EX)); + pmex.cb = sizeof(pmex); + + BOOL ret = GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*) &pmex, sizeof(pmex)); + if (ret != 0) { + // Send the RSS JFR event + EventResidentSetSize event; + event.set_size(pmex.WorkingSetSize); + event.set_peak(pmex.PeakWorkingSetSize); + event.commit(); + } else { + // Log a warning + static bool first_warning = true; + if (first_warning) { + log_warning(jfr)("Error fetching RSS values: GetProcessMemoryInfo failed"); + first_warning = false; + } + } +} + +#endif // INCLUDE_JFR + + // File conventions const char* os::file_separator() { return "\\"; } const char* os::line_separator() { return "\r\n"; } diff --git a/src/hotspot/share/jfr/metadata/metadata.xml b/src/hotspot/share/jfr/metadata/metadata.xml index f2a7b802528..458dfe5bc60 100644 --- a/src/hotspot/share/jfr/metadata/metadata.xml +++ b/src/hotspot/share/jfr/metadata/metadata.xml @@ -73,6 +73,11 @@ + + + + + diff --git a/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp b/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp index 1730e429bab..ff52b4104fd 100644 --- a/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp +++ b/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp @@ -95,6 +95,10 @@ PeriodicType JfrPeriodicEventSet::type(void) { return _type; } +TRACE_REQUEST_FUNC(ResidentSetSize) { + os::jfr_report_memory_info(); +} + TRACE_REQUEST_FUNC(JVMInformation) { ResourceMark rm; EventJVMInformation event; diff --git a/src/hotspot/share/runtime/os.hpp b/src/hotspot/share/runtime/os.hpp index 8ef72268f1f..6a275beb18e 100644 --- a/src/hotspot/share/runtime/os.hpp +++ b/src/hotspot/share/runtime/os.hpp @@ -791,6 +791,9 @@ class os: AllStatic { static size_t lasterror(char *buf, size_t len); static int get_last_error(); + // Send JFR memory info event + static void jfr_report_memory_info() NOT_JFR_RETURN(); + // Replacement for strerror(). // Will return the english description of the error (e.g. "File not found", as // suggested in the POSIX standard. diff --git a/src/jdk.jfr/share/conf/jfr/default.jfc b/src/jdk.jfr/share/conf/jfr/default.jfc index acdb11ca61a..0ed815f2611 100644 --- a/src/jdk.jfr/share/conf/jfr/default.jfc +++ b/src/jdk.jfr/share/conf/jfr/default.jfc @@ -7,6 +7,11 @@ + + true + 1000 ms + + true everyChunk diff --git a/src/jdk.jfr/share/conf/jfr/profile.jfc b/src/jdk.jfr/share/conf/jfr/profile.jfc index 10e5518422c..ca24acb809c 100644 --- a/src/jdk.jfr/share/conf/jfr/profile.jfc +++ b/src/jdk.jfr/share/conf/jfr/profile.jfc @@ -7,6 +7,11 @@ + + true + 1000 ms + + true everyChunk diff --git a/test/jdk/jdk/jfr/event/runtime/TestResidentSetSizeEvent.java b/test/jdk/jdk/jfr/event/runtime/TestResidentSetSizeEvent.java new file mode 100644 index 00000000000..facf222279c --- /dev/null +++ b/test/jdk/jdk/jfr/event/runtime/TestResidentSetSizeEvent.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2022, 2023, 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. + */ + +package jdk.jfr.event.runtime; + +import static jdk.test.lib.Asserts.assertGreaterThan; +import static jdk.test.lib.Asserts.assertLessThanOrEqual; + +import java.util.ArrayList; +import java.util.List; + +import jdk.jfr.Recording; +import jdk.jfr.consumer.RecordedEvent; +import jdk.test.lib.jfr.EventNames; +import jdk.test.lib.jfr.Events; + +/** + * @test + * @key jfr + * @requires vm.hasJFR + * @library /test/lib + * @modules jdk.jfr + * jdk.management + * @run main/othervm -Xms16m -Xmx128m -Xlog:gc jdk.jfr.event.runtime.TestResidentSetSizeEvent true + */ +public class TestResidentSetSizeEvent { + private final static String ResidentSetSizeEvent = EventNames.ResidentSetSize; + + private final static int Period = 1000; + private final static int K = 1024; + + private static ArrayList data = new ArrayList(); + + private static void generateHeapContents() { + for (int i = 0 ; i < 64; i++) { + for (int j = 0; j < K; j++) { + data.add(new byte[K]); + } + } + } + + private static void generateEvents(Recording recording) throws Exception { + recording.enable(ResidentSetSizeEvent).with("period", "everyChunk"); + + recording.start(); + + // Generate data to force heap to grow. + generateHeapContents(); + + recording.stop(); + } + + private static void verifyExpectedEventTypes(List events) throws Exception { + List filteredEvents = events.stream().filter(e -> e.getEventType().getName().equals(ResidentSetSizeEvent)).toList(); + + assertGreaterThan(filteredEvents.size(), 0, "Should exist events of type: " + ResidentSetSizeEvent); + + for (RecordedEvent event : filteredEvents) { + long size = event.getLong("size"); + long peak = event.getLong("peak"); + assertGreaterThan(size, 0L, "Should be non-zero"); + assertGreaterThan(peak, 0L, "Should be non-zero"); + assertLessThanOrEqual(size, peak, "The size should be less than or equal to peak"); + } + } + + public static void main(String[] args) throws Exception { + try (Recording recording = new Recording()) { + generateEvents(recording); + + var events = Events.fromRecording(recording); + verifyExpectedEventTypes(events); + } + } +} diff --git a/test/lib/jdk/test/lib/jfr/EventNames.java b/test/lib/jdk/test/lib/jfr/EventNames.java index 9edeb3ff9a6..10c7ee20a01 100644 --- a/test/lib/jdk/test/lib/jfr/EventNames.java +++ b/test/lib/jdk/test/lib/jfr/EventNames.java @@ -186,6 +186,7 @@ public class EventNames { public static final String PhysicalMemory = PREFIX + "PhysicalMemory"; public static final String NetworkUtilization = PREFIX + "NetworkUtilization"; public static final String ProcessStart = PREFIX + "ProcessStart"; + public static final String ResidentSetSize = PREFIX + "ResidentSetSize"; // JDK public static final String FileForce = PREFIX + "FileForce";