JBR-6742 Record resident set size in JVM fatal error log

(cherry picked from commit 5cc72b464b)
This commit is contained in:
Maxim Kartashev
2024-02-27 12:27:44 +04:00
committed by jbrbot
parent e9fa5dc41f
commit 1ef8b4df91
4 changed files with 229 additions and 0 deletions

View File

@@ -82,6 +82,18 @@
#include <signal.h>
#endif // PRODUCT
#ifdef LINUX
#include "os_linux.hpp"
#endif
#ifdef _WINDOWS
#include <psapi.h>
#endif
#ifdef __APPLE__
#include <mach/mach.h>
#endif
bool VMError::coredump_status;
char VMError::coredump_message[O_BUFLEN];
int VMError::_current_step;
@@ -1271,6 +1283,9 @@ void VMError::report(outputStream* st, bool _verbose) {
JNIHandles::print_on_unsafe(st);
JNIHandles::print_memory_usage_on(st);
STEP_IF("Process memory usage", _verbose)
print_process_memory_usage(st);
STEP("OOME stack traces")
st->print_cr("OOME stack traces (most recent first):");
print_oome_stacks(st);
@@ -1462,6 +1477,7 @@ void VMError::print_vm_info(outputStream* st) {
NativeHeapTrimmer::print_state(st);
st->cr();
print_process_memory_usage(st);
// STEP("printing system")
st->print_cr("--------------- S Y S T E M ---------------");
@@ -2315,3 +2331,72 @@ void VMError::print_dup_classes(outputStream *st) {
}
}
#ifdef LINUX
static void print_process_memory_usage_platform(outputStream *st)
{
// See also os::Linux::print_process_memory_info()
os::Linux::meminfo_t info;
if (os::Linux::query_process_memory_info(&info)) {
ssize_t phys_total_kb = os::physical_memory() / K;
ssize_t phys_avail_kb = os::available_memory() / K;
int rss_percentile = (int)(info.vmrss * 100.0 / phys_total_kb);
st->print_cr("Resident Set Size: %zdK (%d%% of "
"%zdK total physical memory with %zdK free physical memory)",
info.vmrss, rss_percentile, phys_total_kb, phys_avail_kb);
} else {
st->print_cr("Could not open /proc/self/status to get process memory related information");
}
}
#endif
#ifdef _WINDOWS
static void print_process_memory_usage_platform(outputStream *st)
{
// See os::jfr_report_memory_info() in os_windows.cpp
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) {
ssize_t rss_kb = pmex.WorkingSetSize / K;
ssize_t phys_total_kb = os::physical_memory() / K;
ssize_t phys_avail_kb = os::available_memory() / K;
int rss_percentile = (int)(rss_kb * 100.0 / phys_total_kb);
st->print_cr("Resident Set Size: %zdK (%d%% of "
"%zdK total physical memory with %zdK free physical memory)",
rss_kb, rss_percentile, phys_total_kb, phys_avail_kb);
} else {
st->print_cr("GetProcessMemoryInfo() call did not succeed");
}
}
#endif
#ifdef __APPLE__
static void print_process_memory_usage_platform(outputStream *st)
{
// See os::jfr_report_memory_info() in os_bsd.cpp
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) {
ssize_t rss_kb = info.resident_size / K;
ssize_t phys_total_kb = os::physical_memory() / K;
ssize_t phys_avail_kb = os::available_memory() / K;
int rss_percentile = (int)(rss_kb * 100.0 / phys_total_kb);
st->print_cr("Resident Set Size: %zdK (%d%% of "
"%zdK total physical memory with %zdK free physical memory)",
rss_kb, rss_percentile, phys_total_kb, phys_avail_kb);
} else {
st->print_cr("task_info() call did not succeed");
}
}
#endif
void VMError::print_process_memory_usage(outputStream *st)
{
st->print_cr("Process memory usage:");
print_process_memory_usage_platform(st);
st->cr();
}

View File

@@ -232,6 +232,7 @@ public:
static void print_oome_stacks(outputStream *st);
static void print_classloaders_stats(outputStream *st);
static void print_dup_classes(outputStream *st);
static void print_process_memory_usage(outputStream *st);
};
class VMErrorCallback {

View File

@@ -0,0 +1,79 @@
/*
* Copyright 2024 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* @test
* @summary Verifies that the HotSpot crash log includes RSS information.
* @library /test/lib
* @run main RSSInCrashLog
*/
import jdk.test.lib.process.ProcessTools;
import jdk.test.lib.process.OutputAnalyzer;
import java.util.ArrayList;
import java.util.Optional;
public class RSSInCrashLog {
public static void main(String args[]) throws Exception {
if (args.length > 0 && args[0].equals("--test")) {
System.out.println("Proceeding to crash JVM with OOME");
crashJVM();
System.out.println("...shouldn't reach here");
} else {
generateAndVerifyCrashLogContents();
}
}
static void crashJVM() {
System.out.println("------- first attempt to crash -------");
long[][][] array = new long[100][][];
for (int i = 0; i < 100; i++) {
System.out.println("------- crash attempt #" + i + "-------");
array[i] = new long[1000][1000];
}
int random = (int) (Math.random() * 100);
System.out.println(array[random][42][0]);
}
public static void generateAndVerifyCrashLogContents() throws Exception {
ArrayList<String> opts = new ArrayList<>();
opts.add("-Xmx20m");
opts.add("-XX:-CreateCoredumpOnCrash");
opts.add("-XX:+CrashOnOutOfMemoryError");
opts.add("-XX:+ErrorFileToStdout");
opts.add(RSSInCrashLog.class.getName());
opts.add("--test");
ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(opts);
OutputAnalyzer output = new OutputAnalyzer(pb.start());
output.outputTo(System.out);
output.shouldContain("Process memory usage");
output.shouldContain("Resident Set Size: ");
Optional<String> rssLine = output.asLines().stream().filter(it -> it.startsWith("Resident Set Size:")).findAny();
String rssValue = rssLine.get().split(" ")[3];
if (!rssValue.endsWith("K")) {
throw new RuntimeException("RSS '" + rssValue + "' does not end with 'K'");
}
rssValue = rssValue.substring(0, rssValue.length() - 1);
long rss = Long.parseLong(rssValue);
System.out.println("Parsed RSS: " + rss);
if (rss < 10) {
throw new RuntimeException("RSS is unusually small: " + rss + "K");
}
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2024 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* @test
* @summary Verifies that the VM.info jcmd includes RSS information.
* @library /test/lib
* @run main/othervm RSSInVmInfo
*/
import jdk.test.lib.JDKToolFinder;
import jdk.test.lib.process.OutputAnalyzer;
import java.io.IOException;
import java.util.Optional;
public class RSSInVmInfo {
public static void main(String args[]) throws Exception {
long pid = ProcessHandle.current().pid();
final OutputAnalyzer output = runJCmd(pid);
output.outputTo(System.out);
output.shouldContain("Process memory usage");
output.shouldContain("Resident Set Size: ");
Optional<String> rssLine = output.asLines().stream().filter(it -> it.startsWith("Resident Set Size:")).findAny();
String rssValue = rssLine.get().split(" ")[3];
if (!rssValue.endsWith("K")) {
throw new RuntimeException("RSS '" + rssValue + "' does not end with 'K'");
}
rssValue = rssValue.substring(0, rssValue.length() - 1);
long rss = Long.parseLong(rssValue);
System.out.println("Parsed RSS: " + rss);
if (rss < 10) {
throw new RuntimeException("RSS is unusually small: " + rss + "K");
}
}
static OutputAnalyzer runJCmd(long pid) {
try {
final String jcmd = JDKToolFinder.getTestJDKTool("jcmd");
final ProcessBuilder pb = new ProcessBuilder(jcmd, String.valueOf(pid), "VM.info");
OutputAnalyzer output = new OutputAnalyzer(pb.start());
output.outputTo(System.out);
output.shouldHaveExitValue(0);
return output;
} catch (IOException e) {
throw new RuntimeException("Launching jcmd failed", e);
}
}
}