28
28
29
29
import com .oracle .svm .core .Uninterruptible ;
30
30
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 ;
39
31
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 ;
41
34
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 ;
46
36
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 ;
47
44
import com .oracle .svm .core .util .VMError ;
45
+
48
46
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 ;
49
52
import org .graalvm .nativeimage .c .struct .SizeOf ;
50
- import com .oracle .svm .core .hub .DynamicHubSupport ;
51
53
import org .graalvm .nativeimage .impl .UnmanagedMemorySupport ;
54
+
55
+ import org .graalvm .word .Pointer ;
52
56
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 ;
55
58
56
59
public class ObjectCountEventSupport {
57
60
private final static ObjectCountVisitor objectCountVisitor = new ObjectCountVisitor ();
58
61
private static final ObjectCountOperation objectCountOperation = new ObjectCountOperation ();
59
62
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 ;
60
68
61
69
@ Platforms (HOSTED_ONLY .class )
62
70
ObjectCountEventSupport () {
63
71
}
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
+ }
64
111
65
- public static void countObjects () {
112
+ private static PointerArray countObjects (PointerArray objectCounts ) {
66
113
assert VMOperation .isInProgressAtSafepoint ();
67
114
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
+
73
118
int initialCapacity = ImageSingletons .lookup (DynamicHubSupport .class ).getMaxTypeId ();
74
119
PointerArrayAccess .initialize (objectCounts , initialCapacity );
75
120
76
121
// Throw an error, otherwise we'll probably get a segfault later
77
122
VMError .guarantee (objectCounts .getSize () == initialCapacity , "init not done properly" );
78
123
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 " );
82
127
83
128
int sizeSum = 0 ;
84
129
int countSum = 0 ;
@@ -89,19 +134,18 @@ public static void countObjects() {
89
134
}
90
135
countSum += objectCountData .getCount ();
91
136
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" );
95
138
}
96
- // *** Do NOT add prints. Segfault at index 2927
139
+
97
140
VMError .guarantee (countSum > 0 , "countSum should be >0" );
98
141
VMError .guarantee (sizeSum > 0 , "sizeSum should be >0" );
99
142
100
143
int typeId = DynamicHub .fromClass (String .class ).getTypeID ();
101
144
ObjectCountData stringOcd = objectCounts .getData ().addressOf (typeId ).read ();
102
145
VMError .guarantee (stringOcd .getCount () > 0 , "should have more than 1 String in heap" );
103
146
VMError .guarantee (stringOcd .getSize () > 0 , "string size should be positive" );
104
- PointerArrayAccess .freeData (objectCounts );
147
+
148
+ return objectCounts ;
105
149
}
106
150
107
151
private static class ObjectCountOperation extends NativeVMOperation {
@@ -111,25 +155,35 @@ private static class ObjectCountOperation extends NativeVMOperation {
111
155
}
112
156
113
157
@ Override
114
- // @RestrictHeapAccess(access = NO_ALLOCATION, reason = "Must not allocate during GC.")
115
158
protected void operate (NativeVMOperationData data ) {
116
159
ObjectCountVMOperationData objectCountVMOperationData = (ObjectCountVMOperationData ) data ;
117
160
objectCountVisitor .initialize (objectCountVMOperationData .getObjectCounts ());
161
+ totalSize = 0 ; // compute anew each time we operate
118
162
Heap .getHeap ().walkImageHeapObjects (objectCountVisitor );
119
163
}
120
164
}
121
165
122
- private static boolean initializeObjectCountData (PointerArray pointerArray , int idx ) {
166
+
167
+ private static boolean initializeObjectCountData (PointerArray pointerArray , int idx , Object obj ) {
123
168
ObjectCountData objectCountData = ImageSingletons .lookup (UnmanagedMemorySupport .class ).malloc (SizeOf .unsigned (ObjectCountData .class ));
124
169
if (objectCountData .isNull ()) {
125
170
return false ;
126
171
}
127
172
objectCountData .setCount (0 );
128
173
objectCountData .setSize (0 );
174
+ objectCountData .setTraceId (getTraceId (obj .getClass ()));
129
175
PointerArrayAccess .write (pointerArray , idx , objectCountData );
130
176
return true ;
131
177
}
132
178
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
+
133
187
private static class ObjectCountVisitor implements ObjectVisitor {
134
188
PointerArray objectCounts ;
135
189
@@ -152,7 +206,7 @@ public boolean visitObject(Object obj) { // *** Can't allocate in here no matter
152
206
ObjectCountData objectCountData = objectCounts .getData ().addressOf (typeId ).read ();
153
207
if (objectCountData .isNull ()) { // *** this is working as expected
154
208
debugCount1 ++;
155
- if (!initializeObjectCountData (objectCounts , typeId )) {
209
+ if (!initializeObjectCountData (objectCounts , typeId , obj )) {
156
210
return false ;
157
211
}
158
212
// read it again to refresh the value
@@ -163,29 +217,15 @@ public boolean visitObject(Object obj) { // *** Can't allocate in here no matter
163
217
objectCountData .setCount (objectCountData .getCount () + 1 );
164
218
165
219
// 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
+
185
224
return true ;
186
225
}
187
226
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." )
189
229
private long uninterruptibleGetSize (Object obj ) {
190
230
return LayoutEncoding .getSizeFromObject (obj ).rawValue ();
191
231
}
@@ -213,5 +253,11 @@ public interface ObjectCountData extends PointerBase {
213
253
214
254
@ RawField
215
255
void setSize (long value );
256
+
257
+ @ RawField
258
+ long getTraceId ();
259
+
260
+ @ RawField
261
+ void setTraceId (long value );
216
262
}
217
263
}
0 commit comments