Skip to content

Commit 06c6404

Browse files
Add NMT events to JFR and add peak tracking and some tests.
add peak tracking and tests
1 parent 4b1e416 commit 06c6404

File tree

13 files changed

+453
-11
lines changed

13 files changed

+453
-11
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrEvent.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ public final class JfrEvent {
7171
public static final JfrEvent AllocationRequiringGC = create("jdk.AllocationRequiringGC");
7272
public static final JfrEvent OldObjectSample = create("jdk.OldObjectSample");
7373
public static final JfrEvent ObjectAllocationSample = create("jdk.ObjectAllocationSample", JfrEventFlags.SupportsThrottling);
74+
public static final JfrEvent NativeMemoryUsage = create("jdk.NativeMemoryUsage");
75+
public static final JfrEvent NativeMemoryUsageTotal = create("jdk.NativeMemoryUsageTotal");
7476

7577
private final long id;
7678
private final String name;

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrFeature.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,9 @@ public void afterRegistration(AfterRegistrationAccess access) {
172172
JfrSerializerSupport.get().register(new JfrGCNameSerializer());
173173
JfrSerializerSupport.get().register(new JfrVMOperationNameSerializer());
174174
JfrSerializerSupport.get().register(new JfrGCWhenSerializer());
175-
175+
if (VMInspectionOptions.hasNativeMemoryTrackingSupport()) {
176+
JfrSerializerSupport.get().register(new JfrNmtCategorySerializer());
177+
}
176178
ThreadListenerSupport.get().register(SubstrateJVM.getThreadLocal());
177179

178180
RuntimeClassInitializationSupport rci = ImageSingletons.lookup(RuntimeClassInitializationSupport.class);
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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.jfr;
28+
29+
import com.oracle.svm.core.nmt.NmtCategory;
30+
import org.graalvm.nativeimage.Platform;
31+
import org.graalvm.nativeimage.Platforms;
32+
33+
public class JfrNmtCategorySerializer implements JfrSerializer {
34+
@Platforms(Platform.HOSTED_ONLY.class)
35+
public JfrNmtCategorySerializer() {
36+
}
37+
38+
@Override
39+
public void write(JfrChunkWriter writer) {
40+
writer.writeCompressedLong(JfrType.NMTType.getId());
41+
42+
NmtCategory[] nmtCategories = NmtCategory.values();
43+
writer.writeCompressedLong(nmtCategories.length);
44+
for (int i = 0; i < nmtCategories.length; i++) {
45+
writer.writeCompressedInt(i);
46+
writer.writeString(nmtCategories[i].getName());
47+
}
48+
}
49+
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrType.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ public enum JfrType {
4747
GCWhen("jdk.types.GCWhen"),
4848
VMOperation("jdk.types.VMOperationType"),
4949
MonitorInflationCause("jdk.types.InflateCause"),
50-
OldObject("jdk.types.OldObject");
50+
OldObject("jdk.types.OldObject"),
51+
NMTType("jdk.types.NMTType");
5152

5253
private final long id;
5354

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/EveryChunkNativePeriodicEvents.java

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/*
2-
* Copyright (c) 2021, 2021, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
3+
* Copyright (c) 2024, 2024, Red Hat Inc. All rights reserved.
34
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
45
*
56
* This code is free software; you can redistribute it and/or modify it
@@ -38,8 +39,11 @@
3839
import com.oracle.svm.core.jfr.JfrNativeEventWriterData;
3940
import com.oracle.svm.core.jfr.JfrNativeEventWriterDataAccess;
4041
import com.oracle.svm.core.jfr.JfrTicks;
42+
import com.oracle.svm.core.nmt.NmtCategory;
43+
import com.oracle.svm.core.nmt.NativeMemoryTracking;
4144
import com.oracle.svm.core.thread.JavaVMOperation;
4245
import com.oracle.svm.core.thread.VMThreads;
46+
import com.oracle.svm.core.VMInspectionOptions;
4347

4448
import jdk.jfr.Event;
4549
import jdk.jfr.Name;
@@ -54,6 +58,7 @@ public static void emit() {
5458
emitPhysicalMemory();
5559
emitClassLoadingStatistics();
5660
emitPerThreadEvents();
61+
emitNativeMemoryTrackingEvents();
5762
}
5863

5964
@Uninterruptible(reason = "Accesses a JFR buffer.")
@@ -102,6 +107,66 @@ private static void emitClassLoadingStatistics() {
102107
}
103108
}
104109

110+
/**
111+
* Emit events for NativeMemoryUsage and NativeMemoryUsageTotal. We do not guarantee consistent
112+
* measurements across NMT categories and the total. Each individual NMT category uses atomic
113+
* counters which may change while we are in this method. Similar to OpenJDK, it is only a
114+
* best-effort approach.
115+
*/
116+
private static void emitNativeMemoryTrackingEvents() {
117+
if (VMInspectionOptions.hasNativeMemoryTrackingSupport()) {
118+
emitJdkNmtEvents(NmtCategory.values());
119+
emitNmtPeakEvents();
120+
}
121+
}
122+
123+
/** These peak events are specific to SubstrateVM. */
124+
private static void emitNmtPeakEvents() {
125+
NativeMemoryUsageTotalPeakEvent nmtTotalPeakEvent = new NativeMemoryUsageTotalPeakEvent();
126+
nmtTotalPeakEvent.countAtPeak = NativeMemoryTracking.singleton().getCountAtTotalPeakUsage();
127+
nmtTotalPeakEvent.peakUsage = NativeMemoryTracking.singleton().getPeakTotalUsedMemory();
128+
nmtTotalPeakEvent.commit();
129+
130+
for (NmtCategory nmtCategory : NmtCategory.values()) {
131+
NativeMemoryUsagePeakEvent nmtPeakEvent = new NativeMemoryUsagePeakEvent();
132+
nmtPeakEvent.type = nmtCategory.getName();
133+
nmtPeakEvent.countAtPeak = NativeMemoryTracking.singleton().getCountAtPeakUsage(nmtCategory);
134+
nmtPeakEvent.peakUsage = NativeMemoryTracking.singleton().getPeakUsedMemory(nmtCategory);
135+
nmtPeakEvent.commit();
136+
}
137+
}
138+
139+
@Uninterruptible(reason = "Accesses a JFR buffer.")
140+
private static void emitJdkNmtEvents(NmtCategory[] nmtCategories) {
141+
long timestamp = JfrTicks.elapsedTicks();
142+
JfrNativeEventWriterData data = StackValue.get(JfrNativeEventWriterData.class);
143+
JfrNativeEventWriterDataAccess.initializeThreadLocalNativeBuffer(data);
144+
145+
if (JfrEvent.NativeMemoryUsage.shouldEmit()) {
146+
for (NmtCategory nmtCategory : nmtCategories) {
147+
JfrNativeEventWriter.beginSmallEvent(data, JfrEvent.NativeMemoryUsage);
148+
JfrNativeEventWriter.putLong(data, timestamp);
149+
/* Category */
150+
JfrNativeEventWriter.putLong(data, nmtCategory.ordinal());
151+
/* Reserved usage */
152+
JfrNativeEventWriter.putLong(data, NativeMemoryTracking.singleton().getUsedMemory(nmtCategory));
153+
/* Committed usage */
154+
JfrNativeEventWriter.putLong(data, NativeMemoryTracking.singleton().getUsedMemory(nmtCategory));
155+
JfrNativeEventWriter.endSmallEvent(data);
156+
}
157+
}
158+
if (JfrEvent.NativeMemoryUsageTotal.shouldEmit()) {
159+
JfrNativeEventWriter.beginSmallEvent(data, JfrEvent.NativeMemoryUsageTotal);
160+
JfrNativeEventWriter.putLong(data, timestamp);
161+
/* Reserved usage */
162+
JfrNativeEventWriter.putLong(data, NativeMemoryTracking.singleton().getTotalUsedMemory());
163+
/* Committed usage */
164+
JfrNativeEventWriter.putLong(data, NativeMemoryTracking.singleton().getTotalUsedMemory());
165+
JfrNativeEventWriter.endSmallEvent(data);
166+
}
167+
168+
}
169+
105170
private static void emitPerThreadEvents() {
106171
if (needsVMOperation()) {
107172
EmitPeriodicPerThreadEventsOperation vmOp = new EmitPeriodicPerThreadEventsOperation();
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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+
package com.oracle.svm.core.jfr.events;
27+
28+
import jdk.jfr.Category;
29+
import jdk.jfr.Description;
30+
import jdk.jfr.Event;
31+
import jdk.jfr.Label;
32+
import jdk.jfr.Name;
33+
import jdk.jfr.StackTrace;
34+
35+
@Name("svm.NativeMemoryUsagePeak")
36+
@Label("Native Memory Usage Peak")
37+
@Description("Information about native memory peak usage of committed virtual memory and malloc.")
38+
@Category("Native Image")
39+
@StackTrace(false)
40+
public class NativeMemoryUsagePeakEvent extends Event {
41+
@Label("Memory Type") public String type;
42+
@Label("Peak Usage") public long peakUsage;
43+
@Label("Count At Peak") public long countAtPeak;
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
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+
package com.oracle.svm.core.jfr.events;
27+
28+
import jdk.jfr.Category;
29+
import jdk.jfr.Description;
30+
import jdk.jfr.Event;
31+
import jdk.jfr.Label;
32+
import jdk.jfr.Name;
33+
import jdk.jfr.StackTrace;
34+
35+
@Name("svm.NativeMemoryUsageTotalPeak")
36+
@Label("Native Memory Usage Total Peak")
37+
@Description("Information about native memory peak usage of committed virtual memory and malloc.")
38+
@Category("Native Image")
39+
@StackTrace(false)
40+
public class NativeMemoryUsageTotalPeakEvent extends Event {
41+
@Label("Peak Usage") public long peakUsage;
42+
@Label("Count At Peak") public long countAtPeak;
43+
}

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

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,41 @@ private static Pointer getInnerPointer(NmtMallocHeader mallocHeader) {
144144
return ((Pointer) mallocHeader).add(sizeOfNmtHeader());
145145
}
146146

147+
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
147148
public long getUsedMemory(NmtCategory category) {
148149
return mallocMemorySnapshot.getInfoByCategory(category).getUsed();
149150
}
150151

152+
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
153+
public long getPeakUsedMemory(NmtCategory category) {
154+
return mallocMemorySnapshot.getInfoByCategory(category).getPeakUsed();
155+
}
156+
157+
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
158+
public long getCountAtPeakUsage(NmtCategory category) {
159+
return mallocMemorySnapshot.getInfoByCategory(category).getCountAtPeakUsage();
160+
}
161+
162+
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
163+
public long getTotalCount() {
164+
return mallocMemorySnapshot.getTotalInfo().getCount();
165+
}
166+
167+
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
168+
public long getTotalUsedMemory() {
169+
return mallocMemorySnapshot.getTotalInfo().getUsed();
170+
}
171+
172+
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
173+
public long getPeakTotalUsedMemory() {
174+
return mallocMemorySnapshot.getTotalInfo().getPeakUsed();
175+
}
176+
177+
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
178+
public long getCountAtTotalPeakUsage() {
179+
return mallocMemorySnapshot.getTotalInfo().getCountAtPeakUsage();
180+
}
181+
151182
public static RuntimeSupport.Hook shutdownHook() {
152183
return isFirstIsolate -> NativeMemoryTracking.singleton().printStatistics();
153184
}
@@ -156,13 +187,17 @@ public void printStatistics() {
156187
if (VMInspectionOptions.PrintNMTStatistics.getValue()) {
157188
System.out.println();
158189
System.out.println("Native memory tracking");
159-
System.out.println(" Total used memory: " + mallocMemorySnapshot.getTotalInfo().getUsed() + " bytes");
160-
System.out.println(" Total alive allocations: " + mallocMemorySnapshot.getTotalInfo().getCount());
190+
System.out.println(" Peak total used memory: " + getPeakTotalUsedMemory() + " bytes");
191+
System.out.println(" Total alive allocations at peak usage: " + getCountAtTotalPeakUsage());
192+
System.out.println(" Total used memory: " + getTotalUsedMemory() + " bytes");
193+
System.out.println(" Total alive allocations: " + getTotalCount());
161194

162195
for (int i = 0; i < NmtCategory.values().length; i++) {
163196
String name = NmtCategory.values()[i].getName();
164197
NmtMallocMemoryInfo info = mallocMemorySnapshot.getInfoByCategory(i);
165198

199+
System.out.println(" " + name + " peak used memory: " + info.getPeakUsed() + " bytes");
200+
System.out.println(" " + name + " alive allocations at peak: " + info.getCountAtPeakUsage());
166201
System.out.println(" " + name + " used memory: " + info.getUsed() + " bytes");
167202
System.out.println(" " + name + " alive allocations: " + info.getCount());
168203
}

0 commit comments

Comments
 (0)