Skip to content

Commit 4a5c404

Browse files
event emission
1 parent 0ad9f8d commit 4a5c404

File tree

4 files changed

+119
-71
lines changed

4 files changed

+119
-71
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
@@ -65,6 +65,8 @@ public final class JfrEvent {
6565
public static final JfrEvent ThreadAllocationStatistics = create("jdk.ThreadAllocationStatistics", false);
6666
public static final JfrEvent SystemGC = create("jdk.SystemGC", true);
6767
public static final JfrEvent AllocationRequiringGC = create("jdk.AllocationRequiringGC", false);
68+
public static final JfrEvent ObjectCount = create("jdk.ObjectCount", false);
69+
public static final JfrEvent ObjectCountAfterGC = create("jdk.ObjectCountAfterGC", false);
6870

6971
private final long id;
7072
private final String name;

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

Lines changed: 99 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -28,57 +28,102 @@
2828

2929
import com.oracle.svm.core.Uninterruptible;
3030
import com.oracle.svm.core.UnmanagedMemoryUtil;
31-
import com.oracle.svm.core.thread.NativeVMOperation;
32-
import org.graalvm.nativeimage.Platform.HOSTED_ONLY;
33-
import org.graalvm.nativeimage.Platforms;
34-
import com.oracle.svm.core.thread.VMOperation.SystemEffect;
35-
import com.oracle.svm.core.thread.VMOperation;
36-
import com.oracle.svm.core.thread.NativeVMOperationData;
37-
import com.oracle.svm.core.heap.VMOperationInfos;
38-
import com.oracle.svm.core.heap.ObjectVisitor;
3931
import com.oracle.svm.core.heap.Heap;
40-
import org.graalvm.nativeimage.StackValue;
32+
import com.oracle.svm.core.heap.ObjectVisitor;
33+
import com.oracle.svm.core.heap.VMOperationInfos;
4134
import com.oracle.svm.core.hub.DynamicHub;
42-
import org.graalvm.word.WordFactory;
43-
import org.graalvm.nativeimage.c.struct.RawStructure;
44-
import org.graalvm.nativeimage.c.struct.RawField;
45-
import org.graalvm.word.Pointer;
35+
import com.oracle.svm.core.hub.DynamicHubSupport;
4636
import com.oracle.svm.core.hub.LayoutEncoding;
37+
import com.oracle.svm.core.jfr.JfrEvent;
38+
import com.oracle.svm.core.jfr.SubstrateJVM;
39+
import com.oracle.svm.core.jfr.events.ObjectCountEvents;
40+
import com.oracle.svm.core.thread.NativeVMOperation;
41+
import com.oracle.svm.core.thread.NativeVMOperationData;
42+
import com.oracle.svm.core.thread.VMOperation.SystemEffect;
43+
import com.oracle.svm.core.thread.VMOperation;
4744
import com.oracle.svm.core.util.VMError;
45+
4846
import org.graalvm.nativeimage.ImageSingletons;
47+
import org.graalvm.nativeimage.Platform.HOSTED_ONLY;
48+
import org.graalvm.nativeimage.Platforms;
49+
import org.graalvm.nativeimage.StackValue;
50+
import org.graalvm.nativeimage.c.struct.RawField;
51+
import org.graalvm.nativeimage.c.struct.RawStructure;
4952
import org.graalvm.nativeimage.c.struct.SizeOf;
50-
import com.oracle.svm.core.hub.DynamicHubSupport;
5153
import org.graalvm.nativeimage.impl.UnmanagedMemorySupport;
54+
55+
import org.graalvm.word.Pointer;
5256
import org.graalvm.word.PointerBase;
53-
import com.oracle.svm.core.jfr.events.PointerArrayAccess;
54-
import com.oracle.svm.core.jfr.events.PointerArray;
57+
import org.graalvm.word.WordFactory;
5558

5659
public class ObjectCountEventSupport {
5760
private final static ObjectCountVisitor objectCountVisitor = new ObjectCountVisitor();
5861
private static final ObjectCountOperation objectCountOperation = new ObjectCountOperation();
5962
private static int debugCount1 = 0;
63+
private static double cutoffPercentage = 0.005;
64+
private static long totalSize;
65+
66+
// *** should be volatile because periodic thread writes it and VM op thread reads it
67+
private static volatile boolean shouldSendRequestableEvent = false;
6068

6169
@Platforms(HOSTED_ONLY.class)
6270
ObjectCountEventSupport() {
6371
}
72+
/** This is to be called by the JFR periodic task as part of the JFR periodic events */
73+
// @Uninterruptible(reason = "Set and unset should be atomic with invoked GC to avoid races.", callerMustBe = true) //TODO revisit
74+
public static void setShouldSendRequestableEvent(boolean value){
75+
shouldSendRequestableEvent = value;
76+
}
77+
78+
public static void emitEvents(int gcId, long startTicks){
79+
if (com.oracle.svm.core.jfr.HasJfrSupport.get() && shouldEmitEvents()) {
80+
emitEvents0(gcId, startTicks);
81+
if (shouldSendRequestableEvent){
82+
shouldSendRequestableEvent = false;
83+
}
84+
}
85+
}
86+
87+
/** ShouldEmit will be checked again later. This is merely an optimization.*/
88+
@Uninterruptible(reason = "Caller of JfrEvent#shouldEmit must be uninterruptible.")
89+
private static boolean shouldEmitEvents(){
90+
return (shouldSendRequestableEvent && JfrEvent.ObjectCount.shouldEmit()) || JfrEvent.ObjectCountAfterGC.shouldEmit();
91+
}
92+
private static void emitEvents0(int gcId, long startTicks) {
93+
PointerArray objectCounts = StackValue.get(PointerArray.class);
94+
countObjects(objectCounts);
95+
96+
for (int i = 0; i < objectCounts.getSize(); i++) {
97+
emitForTypeId(i, objectCounts, gcId, startTicks);
98+
}
99+
PointerArrayAccess.freeData(objectCounts);
100+
}
101+
102+
private static void emitForTypeId(int typeId, PointerArray objectCounts, int gcId, long startTicks){
103+
ObjectCountData objectCountData = (ObjectCountData) PointerArrayAccess.get(objectCounts, typeId);
104+
if (objectCountData.isNonNull() && objectCountData.getSize() / (double) totalSize > cutoffPercentage) {
105+
VMError.guarantee(objectCountData.getSize() > 0 && objectCountData.getTraceId() >0 && objectCountData.getSize()>0, "size should be > 0 if count is > 0");
106+
107+
ObjectCountEvents.emit(JfrEvent.ObjectCount, startTicks, objectCountData.getTraceId(), objectCountData.getCount(), objectCountData.getSize(), gcId);
108+
ObjectCountEvents.emit(JfrEvent.ObjectCountAfterGC, startTicks, objectCountData.getTraceId(), objectCountData.getCount(), objectCountData.getSize(), gcId);
109+
}
110+
}
64111

65-
public static void countObjects() {
112+
private static PointerArray countObjects(PointerArray objectCounts) {
66113
assert VMOperation.isInProgressAtSafepoint();
67114
int size = SizeOf.get(ObjectCountVMOperationData.class);
68-
ObjectCountVMOperationData data = StackValue.get(size);
69-
UnmanagedMemoryUtil.fill((Pointer) data, WordFactory.unsigned(size), (byte) 0);
70-
PointerArray objectCounts = StackValue.get(PointerArray.class);
71-
// int initialCapacity = Heap.getHeap().getClassCount();
72-
// max type id:9218 classes count:7909
115+
ObjectCountVMOperationData vmOpData = StackValue.get(size);
116+
UnmanagedMemoryUtil.fill((Pointer) vmOpData, WordFactory.unsigned(size), (byte) 0);
117+
73118
int initialCapacity = ImageSingletons.lookup(DynamicHubSupport.class).getMaxTypeId();
74119
PointerArrayAccess.initialize(objectCounts, initialCapacity);
75120

76121
// Throw an error, otherwise we'll probably get a segfault later
77122
VMError.guarantee(objectCounts.getSize() == initialCapacity, "init not done properly");
78123

79-
data.setObjectCounts(objectCounts);
80-
objectCountOperation.enqueue(data);
81-
VMError.guarantee(debugCount1 > 2000, "debug count1 should be > 2000");
124+
vmOpData.setObjectCounts(objectCounts);
125+
objectCountOperation.enqueue(vmOpData);
126+
VMError.guarantee(debugCount1 > 500, "debug count1 should be > 500");
82127

83128
int sizeSum = 0;
84129
int countSum = 0;
@@ -89,19 +134,18 @@ public static void countObjects() {
89134
}
90135
countSum += objectCountData.getCount();
91136
sizeSum += objectCountData.getSize();
92-
if (objectCountData.getCount() > 0) {
93-
VMError.guarantee(objectCountData.getSize() > 0, "size should be > 0 if count is > 0");
94-
}
137+
VMError.guarantee(objectCountData.getSize() > 0, "size should be > 0 if count is > 0");
95138
}
96-
// *** Do NOT add prints. Segfault at index 2927
139+
97140
VMError.guarantee(countSum > 0, "countSum should be >0");
98141
VMError.guarantee(sizeSum > 0, "sizeSum should be >0");
99142

100143
int typeId = DynamicHub.fromClass(String.class).getTypeID();
101144
ObjectCountData stringOcd = objectCounts.getData().addressOf(typeId).read();
102145
VMError.guarantee(stringOcd.getCount() > 0, "should have more than 1 String in heap");
103146
VMError.guarantee(stringOcd.getSize() > 0, "string size should be positive");
104-
PointerArrayAccess.freeData(objectCounts);
147+
148+
return objectCounts;
105149
}
106150

107151
private static class ObjectCountOperation extends NativeVMOperation {
@@ -111,25 +155,35 @@ private static class ObjectCountOperation extends NativeVMOperation {
111155
}
112156

113157
@Override
114-
// @RestrictHeapAccess(access = NO_ALLOCATION, reason = "Must not allocate during GC.")
115158
protected void operate(NativeVMOperationData data) {
116159
ObjectCountVMOperationData objectCountVMOperationData = (ObjectCountVMOperationData) data;
117160
objectCountVisitor.initialize(objectCountVMOperationData.getObjectCounts());
161+
totalSize = 0; // compute anew each time we operate
118162
Heap.getHeap().walkImageHeapObjects(objectCountVisitor);
119163
}
120164
}
121165

122-
private static boolean initializeObjectCountData(PointerArray pointerArray, int idx) {
166+
167+
private static boolean initializeObjectCountData(PointerArray pointerArray, int idx, Object obj) {
123168
ObjectCountData objectCountData = ImageSingletons.lookup(UnmanagedMemorySupport.class).malloc(SizeOf.unsigned(ObjectCountData.class));
124169
if (objectCountData.isNull()) {
125170
return false;
126171
}
127172
objectCountData.setCount(0);
128173
objectCountData.setSize(0);
174+
objectCountData.setTraceId(getTraceId(obj.getClass()));
129175
PointerArrayAccess.write(pointerArray, idx, objectCountData);
130176
return true;
131177
}
132178

179+
/** JFR epoch will not change before associated ObjectCount event is committed because this code runs within a
180+
* GC safepoint.*/ // TODO revisit this logic
181+
@Uninterruptible(reason = "Caller of SubstrateJVM#getClassId must be uninterruptible.")
182+
private static long getTraceId(Class<?> c){
183+
assert VMOperation.isInProgressAtSafepoint();
184+
return SubstrateJVM.get().getClassId(c);
185+
}
186+
133187
private static class ObjectCountVisitor implements ObjectVisitor {
134188
PointerArray objectCounts;
135189

@@ -152,7 +206,7 @@ public boolean visitObject(Object obj) { // *** Can't allocate in here no matter
152206
ObjectCountData objectCountData = objectCounts.getData().addressOf(typeId).read();
153207
if (objectCountData.isNull()) { // *** this is working as expected
154208
debugCount1++;
155-
if (!initializeObjectCountData(objectCounts, typeId)) {
209+
if (!initializeObjectCountData(objectCounts, typeId, obj)) {
156210
return false;
157211
}
158212
// read it again to refresh the value
@@ -163,29 +217,15 @@ public boolean visitObject(Object obj) { // *** Can't allocate in here no matter
163217
objectCountData.setCount(objectCountData.getCount() + 1);
164218

165219
// Get size
166-
long size = objectCountData.getSize();
167-
// int layoutEncoding = KnownIntrinsics.readHub(obj).getLayoutEncoding();
168-
// if (LayoutEncoding.isArray(layoutEncoding)) {
169-
// int elementSize;
170-
// if (LayoutEncoding.isPrimitiveArray(layoutEncoding)) {
171-
// elementSize = LayoutEncoding.getArrayIndexScale(layoutEncoding);
172-
// } else {
173-
// elementSize = ConfigurationValues.getTarget().wordSize;
174-
// }
175-
// int length = ArrayLengthNode.arrayLength(obj);
176-
// size += WordFactory.unsigned(length).multiply(elementSize).rawValue();
177-
// } else if (LayoutEncoding.isPureInstance(layoutEncoding)) {
178-
// size += LayoutEncoding.getPureInstanceAllocationSize(layoutEncoding).rawValue();
179-
// } else if (LayoutEncoding.isHybrid(layoutEncoding)){
180-
// size += LayoutEncoding.getArrayBaseOffsetAsInt(layoutEncoding);
181-
// }
182-
size += uninterruptibleGetSize(obj); // there's also getSizeFromObjectInGC and
183-
// getMomentarySizeFromObject
184-
objectCountData.setSize(size);
220+
long additionalSize = uninterruptibleGetSize(obj);
221+
totalSize += additionalSize;
222+
objectCountData.setSize(objectCountData.getSize() + additionalSize);
223+
185224
return true;
186225
}
187226

188-
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
227+
/** GC should not touch this object again before we are done with it.*/ // TODO revisit this logic
228+
@Uninterruptible(reason = "Caller of LayoutEncoding#getSizeFromObject must be uninterruptible.")
189229
private long uninterruptibleGetSize(Object obj) {
190230
return LayoutEncoding.getSizeFromObject(obj).rawValue();
191231
}
@@ -213,5 +253,11 @@ public interface ObjectCountData extends PointerBase {
213253

214254
@RawField
215255
void setSize(long value);
256+
257+
@RawField
258+
long getTraceId();
259+
260+
@RawField
261+
void setTraceId(long value);
216262
}
217263
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ObjectCountEvent.java renamed to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/events/ObjectCountEvents.java

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -32,30 +32,30 @@
3232
import com.oracle.svm.core.jfr.JfrNativeEventWriter;
3333
import com.oracle.svm.core.jfr.JfrNativeEventWriterData;
3434
import com.oracle.svm.core.jfr.JfrNativeEventWriterDataAccess;
35-
import com.oracle.svm.core.jfr.JfrTicks;
36-
import com.oracle.svm.core.jfr.SubstrateJVM;
37-
import org.graalvm.compiler.word.Word;
38-
import org.graalvm.nativeimage.StackValue;
3935
import com.oracle.svm.core.thread.VMOperation;
36+
import org.graalvm.nativeimage.StackValue;
4037

41-
import static com.oracle.svm.core.heap.RestrictHeapAccess.Access.NO_ALLOCATION;
42-
43-
public class ObjectCountEvent {
44-
/** Should only be called during garbage collection at a safepoint */
45-
// @com.oracle.svm.core.heap.RestrictHeapAccess(access = NO_ALLOCATION, reason = "Object Counting
46-
// must not allocate.")
47-
public static void emit(long startTick) {
38+
/** This class is used for both jdk.ObjectCount and jdk.ObjectCountAfterGC since they contain identical information.*/
39+
public class ObjectCountEvents {
40+
public static void emit(JfrEvent eventType, long startTick, long traceId, long count, long size, int gcId) {
4841
assert VMOperation.isInProgressAtSafepoint();
4942
if (HasJfrSupport.get()) {
50-
// TODO check here if we should emit before we do an expensive heap walk (similar to
51-
// objectAllocationSample)
52-
com.oracle.svm.core.jfr.events.ObjectCountEventSupport.countObjects();
53-
// For each object in data structure
54-
emit0(null, 0, 0);
43+
emit0(eventType, startTick, traceId, count, size, gcId);
5544
}
5645
}
5746

5847
@Uninterruptible(reason = "Accesses a JFR buffer.")
59-
private static void emit0(Class<?> clazz, long count, long size) {
48+
public static void emit0(JfrEvent eventType, long startTick, long traceId, long count, long size, int gcId) {
49+
if (eventType.shouldEmit()){
50+
JfrNativeEventWriterData data = StackValue.get(JfrNativeEventWriterData.class);
51+
JfrNativeEventWriterDataAccess.initializeThreadLocalNativeBuffer(data);
52+
JfrNativeEventWriter.beginSmallEvent(data, eventType);
53+
JfrNativeEventWriter.putLong(data, startTick);
54+
JfrNativeEventWriter.putInt(data, gcId);
55+
JfrNativeEventWriter.putLong(data, traceId);
56+
JfrNativeEventWriter.putLong(data, count);
57+
JfrNativeEventWriter.putLong(data, size);
58+
JfrNativeEventWriter.endSmallEvent(data);
59+
}
6060
}
6161
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public static void initialize(PointerArray array, int initialCapacity) {
4848

4949
public static PointerBase get(PointerArray array, int i) {
5050
assert i >= 0 && i < array.getSize();
51-
return array.getData().addressOf(i).read();
51+
return array.getData().addressOf(i).read();//*** compute address of i'th element. Read the value of that address (which is a pointer to c struct)
5252
}
5353

5454
public static void write(PointerArray array, int i, WordBase word) {

0 commit comments

Comments
 (0)