Skip to content

Commit 1f38ec0

Browse files
dump nmt on signal
1 parent ee40fc1 commit 1f38ec0

File tree

5 files changed

+144
-39
lines changed

5 files changed

+144
-39
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved.
3+
* Copyright (c) 2024, 2024, Red Hat Inc. All rights reserved.
4+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5+
*
6+
* This code is free software; you can redistribute it and/or modify it
7+
* under the terms of the GNU General Public License version 2 only, as
8+
* published by the Free Software Foundation. Oracle designates this
9+
* particular file as subject to the "Classpath" exception as provided
10+
* by Oracle in the LICENSE file that accompanied this code.
11+
*
12+
* This code is distributed in the hope that it will be useful, but WITHOUT
13+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15+
* version 2 for more details (a copy is included in the LICENSE file that
16+
* accompanied this code).
17+
*
18+
* You should have received a copy of the GNU General Public License version
19+
* 2 along with this work; if not, write to the Free Software Foundation,
20+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21+
*
22+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23+
* or visit www.oracle.com if you need additional information or have any
24+
* questions.
25+
*/
26+
27+
package com.oracle.svm.core;
28+
29+
import com.oracle.svm.core.log.Log;
30+
import com.oracle.svm.core.heap.dump.HeapDumping;
31+
import com.oracle.svm.core.nmt.NativeMemoryTracking;
32+
import java.io.IOException;
33+
34+
import jdk.internal.misc.Signal;
35+
36+
public class Sigusr1Handler implements Signal.Handler {
37+
static boolean installed;
38+
39+
/**
40+
* Depending on which features are included in the image, this method may be called multiple
41+
* times by the thread executing startup hooks.
42+
*/
43+
public static void install() {
44+
if (!installed) {
45+
installed = true;
46+
Signal.handle(new Signal("USR1"), new Sigusr1Handler());
47+
}
48+
}
49+
50+
@Override
51+
public void handle(Signal arg0) {
52+
if (VMInspectionOptions.hasNativeMemoryTrackingSupport()) {
53+
try {
54+
NativeMemoryTracking.singleton().dumpReport();
55+
} catch (IOException e) {
56+
Log.log().string("IOException during NMT report dump: ").string(e.getMessage()).newline();
57+
}
58+
}
59+
60+
if (VMInspectionOptions.hasHeapDumpSupport()) {
61+
try {
62+
HeapDumping.singleton().dumpHeap(true);
63+
} catch (IOException e) {
64+
Log.log().string("IOException during dumpHeap: ").string(e.getMessage()).newline();
65+
}
66+
}
67+
68+
}
69+
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/VMInspectionOptions.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949
import jdk.graal.compiler.options.OptionKey;
5050
import jdk.graal.compiler.options.OptionType;
5151

52+
import static com.oracle.svm.core.option.RuntimeOptionKey.RuntimeOptionKeyFlag.Immutable;
53+
5254
public final class VMInspectionOptions {
5355
private static final String ENABLE_MONITORING_OPTION = "enable-monitoring";
5456
private static final String MONITORING_DEFAULT_NAME = "<deprecated-default>";
@@ -83,6 +85,11 @@ public final class VMInspectionOptions {
8385
@Option(help = "Print native memory tracking statistics on shutdown if native memory tracking is enabled.", type = OptionType.User) //
8486
public static final RuntimeOptionKey<Boolean> PrintNMTStatistics = new RuntimeOptionKey<>(false);
8587

88+
@Option(help = "Path of the file or directory in which NMT report dumps are created. An empty value means a default file " +
89+
"name will be used. An existing directory means the dump will be placed in the directory and have " +
90+
"the default file name.") //
91+
public static final RuntimeOptionKey<String> NmtDumpPath = new RuntimeOptionKey<>("", Immutable);
92+
8693
@Platforms(Platform.HOSTED_ONLY.class)
8794
public static void validateEnableMonitoringFeatures(@SuppressWarnings("unused") OptionKey<?> optionKey) {
8895
Set<String> enabledFeatures = getEnabledMonitoringFeatures();

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/dump/HeapDumpStartupHook.java

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,38 +24,20 @@
2424
*/
2525
package com.oracle.svm.core.heap.dump;
2626

27-
import java.io.IOException;
2827

28+
import com.oracle.svm.core.Sigusr1Handler;
2929
import com.oracle.svm.core.SubstrateOptions;
3030
import com.oracle.svm.core.jdk.RuntimeSupport;
31-
import com.oracle.svm.core.log.Log;
32-
33-
import jdk.internal.misc.Signal;
3431

3532
public class HeapDumpStartupHook implements RuntimeSupport.Hook {
3633
@Override
3734
public void execute(boolean isFirstIsolate) {
3835
if (isFirstIsolate && SubstrateOptions.EnableSignalHandling.getValue()) {
39-
DumpHeapReport.install();
36+
Sigusr1Handler.install();
4037
}
4138

4239
if (SubstrateOptions.HeapDumpOnOutOfMemoryError.getValue()) {
4340
HeapDumping.singleton().initializeDumpHeapOnOutOfMemoryError();
4441
}
4542
}
4643
}
47-
48-
class DumpHeapReport implements Signal.Handler {
49-
static void install() {
50-
Signal.handle(new Signal("USR1"), new DumpHeapReport());
51-
}
52-
53-
@Override
54-
public void handle(Signal arg0) {
55-
try {
56-
HeapDumping.singleton().dumpHeap(true);
57-
} catch (IOException e) {
58-
Log.log().string("IOException during dumpHeap: ").string(e.getMessage()).newline();
59-
}
60-
}
61-
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/nmt/NativeMemoryTracking.java

Lines changed: 65 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
2-
* Copyright (c) 2023, 2023, Oracle and/or its affiliates. All rights reserved.
3-
* Copyright (c) 2023, 2023, Red Hat Inc. All rights reserved.
2+
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
3+
* Copyright (c) 2023, 2024, Red Hat Inc. All rights reserved.
44
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
55
*
66
* This code is free software; you can redistribute it and/or modify it
@@ -28,9 +28,12 @@
2828

2929
import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE;
3030

31+
import com.oracle.svm.core.Sigusr1Handler;
32+
import com.oracle.svm.core.SubstrateOptions;
3133
import org.graalvm.nativeimage.ImageSingletons;
3234
import org.graalvm.nativeimage.Platform;
3335
import org.graalvm.nativeimage.Platforms;
36+
import org.graalvm.nativeimage.ProcessProperties;
3437
import org.graalvm.nativeimage.c.struct.SizeOf;
3538
import org.graalvm.word.Pointer;
3639
import org.graalvm.word.PointerBase;
@@ -45,6 +48,11 @@
4548

4649
import jdk.graal.compiler.api.replacements.Fold;
4750

51+
import java.io.FileWriter;
52+
import java.io.IOException;
53+
import java.nio.file.Files;
54+
import java.nio.file.Paths;
55+
4856
/**
4957
* This class implements native memory tracking (NMT). There are two components to NMT: tracking
5058
* memory allocations (malloc/realloc/calloc), and tracking virtual memory usage (not supported
@@ -197,28 +205,66 @@ private NmtMallocMemoryInfo getInfo(int category) {
197205
return categories[category];
198206
}
199207

208+
public static RuntimeSupport.Hook startupHook() {
209+
return isFirstIsolate -> {
210+
if (isFirstIsolate && SubstrateOptions.EnableSignalHandling.getValue()) {
211+
Sigusr1Handler.install();
212+
}
213+
};
214+
}
215+
200216
public static RuntimeSupport.Hook shutdownHook() {
201217
return isFirstIsolate -> NativeMemoryTracking.singleton().printStatistics();
202218
}
203219

204-
private void printStatistics() {
220+
public void printStatistics() {
205221
if (VMInspectionOptions.PrintNMTStatistics.getValue()) {
206-
System.out.println();
207-
System.out.println("Native memory tracking");
208-
System.out.println(" Peak total used memory: " + getPeakTotalUsedMemory() + " bytes");
209-
System.out.println(" Total alive allocations at peak usage: " + getCountAtTotalPeakUsage());
210-
System.out.println(" Total used memory: " + getTotalUsedMemory() + " bytes");
211-
System.out.println(" Total alive allocations: " + getTotalCount());
212-
213-
for (int i = 0; i < NmtCategory.values().length; i++) {
214-
String name = NmtCategory.values()[i].getName();
215-
NmtMallocMemoryInfo info = getInfo(i);
216-
217-
System.out.println(" " + name + " peak used memory: " + info.getPeakUsed() + " bytes");
218-
System.out.println(" " + name + " alive allocations at peak: " + info.getCountAtPeakUsage());
219-
System.out.println(" " + name + " currently used memory: " + info.getUsed() + " bytes");
220-
System.out.println(" " + name + " currently alive allocations: " + info.getCount());
221-
}
222+
System.out.println(generateReportString());
223+
}
224+
}
225+
226+
public void dumpReport() throws IOException {
227+
String path = reportPath();
228+
FileWriter writer = new FileWriter(path);
229+
writer.write(generateReportString());
230+
writer.close();
231+
232+
}
233+
234+
private String reportPath() {
235+
String time = Long.toString(System.currentTimeMillis());
236+
String pid = Long.toString(ProcessProperties.getProcessID());
237+
String defaultFilename = "nmt_report_" + pid + "_" + time + ".txt";
238+
239+
String filenameOrDirectory = VMInspectionOptions.NmtDumpPath.getValue();
240+
if (filenameOrDirectory.isEmpty()) {
241+
return defaultFilename;
242+
}
243+
var targetPath = Paths.get(filenameOrDirectory);
244+
if (Files.isDirectory(targetPath)) {
245+
targetPath = targetPath.resolve(defaultFilename);
246+
}
247+
return targetPath.toFile().getAbsolutePath();
248+
}
249+
250+
private String generateReportString() {
251+
StringBuilder stringBuilder = new StringBuilder(2500);
252+
stringBuilder.append("\n");
253+
stringBuilder.append("Native memory tracking\n");
254+
stringBuilder.append(" Peak total used memory: " + getPeakTotalUsedMemory() + " bytes\n");
255+
stringBuilder.append(" Total alive allocations at peak usage: " + getCountAtTotalPeakUsage() + "\n");
256+
stringBuilder.append(" Total used memory: " + getTotalUsedMemory() + " bytes\n");
257+
stringBuilder.append(" Total alive allocations: " + getTotalCount() + "\n");
258+
259+
for (int i = 0; i < NmtCategory.values().length; i++) {
260+
String name = NmtCategory.values()[i].getName();
261+
NmtMallocMemoryInfo info = getInfo(i);
262+
263+
stringBuilder.append(" " + name + " peak used memory: " + info.getPeakUsed() + " bytes\n");
264+
stringBuilder.append(" " + name + " alive allocations at peak: " + info.getCountAtPeakUsage() + "\n");
265+
stringBuilder.append(" " + name + " currently used memory: " + info.getUsed() + " bytes\n");
266+
stringBuilder.append(" " + name + " currently alive allocations: " + info.getCount() + "\n");
222267
}
268+
return stringBuilder.toString();
223269
}
224270
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/nmt/NmtFeature.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public void afterRegistration(AfterRegistrationAccess access) {
4747

4848
@Override
4949
public void beforeAnalysis(BeforeAnalysisAccess access) {
50+
RuntimeSupport.getRuntimeSupport().addStartupHook(NativeMemoryTracking.startupHook());
5051
RuntimeSupport.getRuntimeSupport().addShutdownHook(NativeMemoryTracking.shutdownHook());
5152
}
5253
}

0 commit comments

Comments
 (0)