25
25
26
26
package com .oracle .svm .core .sampler ;
27
27
28
- import jdk .graal .compiler .word .Word ;
28
+ import static com .oracle .svm .core .Uninterruptible .CALLED_FROM_UNINTERRUPTIBLE_CODE ;
29
+
29
30
import org .graalvm .nativeimage .StackValue ;
30
31
import org .graalvm .nativeimage .c .function .CodePointer ;
31
32
import org .graalvm .nativeimage .c .type .CIntPointer ;
49
50
import com .oracle .svm .core .jfr .events .ExecutionSampleEvent ;
50
51
import com .oracle .svm .core .util .VMError ;
51
52
53
+ import jdk .graal .compiler .word .Word ;
54
+
52
55
/**
53
56
* A concrete implementation of {@link SamplerStackTraceSerializer} designed for JFR stack trace
54
57
* serialization.
58
+ *
59
+ * All static mutable state in this class is preallocated and reused by multiple threads. This is
60
+ * safe because only one thread at a time may serialize stack traces.
55
61
*/
56
62
public final class SamplerJfrStackTraceSerializer implements SamplerStackTraceSerializer {
57
- /** This value is used by multiple threads but only by a single thread at a time. */
58
63
private static final CodeInfoDecoder .FrameInfoCursor FRAME_INFO_CURSOR = new CodeInfoDecoder .FrameInfoCursor ();
59
-
60
- /*
61
- * This is static so that a single instance can be preallocated and reused. Only one thread ever
62
- * serializes at a given time.
63
- */
64
- private static final FrameCountData FRAME_COUNT_DATA = new FrameCountData ();
64
+ private static final StackTraceVisitorData VISITOR_DATA = new StackTraceVisitorData ();
65
65
66
66
@ Override
67
67
@ Uninterruptible (reason = "Prevent JFR recording and epoch change." )
@@ -117,20 +117,19 @@ private static boolean serializeStackTrace(Pointer rawStackTrace, int sampleSize
117
117
/*
118
118
* One IP may correspond to multiple Java-level stack frames. We need to precompute the
119
119
* number of stack trace elements because the count can't be patched later on
120
- * (JfrNativeEventWriter.putInt() would not necessarily reserve enough bytes).
121
- *
122
- * The first pass-through also sets FRAME_COUNT_DATA.isTruncated().
120
+ * (JfrNativeEventWriter.putInt() would not necessarily reserve enough bytes). We also
121
+ * precompute if the stack trace was truncated.
123
122
*/
124
- int numStackTraceElements = visitRawStackTrace (rawStackTrace , sampleSize , Word .nullPointer (), skipCount );
125
- if (numStackTraceElements == 0 ) {
123
+ visitRawStackTrace (rawStackTrace , sampleSize , Word .nullPointer (), skipCount );
124
+ if (VISITOR_DATA . getUsedFrames () == 0 ) {
126
125
return false ;
127
126
}
128
127
129
128
JfrNativeEventWriterData data = StackValue .get (JfrNativeEventWriterData .class );
130
129
JfrNativeEventWriterDataAccess .initialize (data , targetBuffer );
131
130
JfrNativeEventWriter .putLong (data , stackTraceId );
132
- JfrNativeEventWriter .putBoolean (data , isTruncated || FRAME_COUNT_DATA .isTruncated ());
133
- JfrNativeEventWriter .putInt (data , numStackTraceElements );
131
+ JfrNativeEventWriter .putBoolean (data , isTruncated || VISITOR_DATA .isTruncated ());
132
+ JfrNativeEventWriter .putInt (data , VISITOR_DATA . getUsedFrames () );
134
133
visitRawStackTrace (rawStackTrace , sampleSize , data , skipCount );
135
134
boolean success = JfrNativeEventWriter .commit (data );
136
135
@@ -140,24 +139,20 @@ private static boolean serializeStackTrace(Pointer rawStackTrace, int sampleSize
140
139
}
141
140
142
141
@ Uninterruptible (reason = "Prevent JFR recording and epoch change." )
143
- private static int visitRawStackTrace (Pointer rawStackTrace , int sampleSize , JfrNativeEventWriterData data , int skipCount ) {
144
- int numStackTraceElements = 0 ;
145
- Pointer rawStackTraceEnd = rawStackTrace .add (sampleSize );
146
- Pointer ipPtr = rawStackTrace ;
147
-
148
- // Reset FrameCountData before every serialization of a new stacktrace.
149
- FRAME_COUNT_DATA .reset (skipCount );
142
+ private static void visitRawStackTrace (Pointer rawStackTrace , int sampleSize , JfrNativeEventWriterData data , int skipCount ) {
143
+ VISITOR_DATA .reset (skipCount );
150
144
145
+ Pointer ipPtr = rawStackTrace ;
146
+ Pointer rawStackTraceEnd = rawStackTrace .add (sampleSize );
151
147
while (ipPtr .belowThan (rawStackTraceEnd )) {
152
148
long ip = ipPtr .readLong (0 );
153
- numStackTraceElements += visitFrame (data , ip );
149
+ visitFrame (data , ip );
154
150
ipPtr = ipPtr .add (Long .BYTES );
155
151
}
156
- return numStackTraceElements ;
157
152
}
158
153
159
154
@ Uninterruptible (reason = "Prevent JFR recording, epoch change, and that the GC frees the CodeInfo." )
160
- private static int visitFrame (JfrNativeEventWriterData data , long address ) {
155
+ private static void visitFrame (JfrNativeEventWriterData data , long address ) {
161
156
CodePointer ip = Word .pointer (address );
162
157
UntetheredCodeInfo untetheredInfo = CodeInfoTable .lookupCodeInfo (ip );
163
158
if (untetheredInfo .isNull ()) {
@@ -168,32 +163,30 @@ private static int visitFrame(JfrNativeEventWriterData data, long address) {
168
163
Object tether = CodeInfoAccess .acquireTether (untetheredInfo );
169
164
try {
170
165
CodeInfo tetheredCodeInfo = CodeInfoAccess .convert (untetheredInfo , tether );
171
- return visitFrame (data , tetheredCodeInfo , ip );
166
+ visitFrame (data , tetheredCodeInfo , ip );
172
167
} finally {
173
168
CodeInfoAccess .releaseTether (untetheredInfo , tether );
174
169
}
175
170
}
176
171
177
172
@ Uninterruptible (reason = "Prevent JFR recording and epoch change." )
178
- private static int visitFrame (JfrNativeEventWriterData data , CodeInfo codeInfo , CodePointer ip ) {
179
- int numStackTraceElements = 0 ;
173
+ private static void visitFrame (JfrNativeEventWriterData data , CodeInfo codeInfo , CodePointer ip ) {
180
174
FRAME_INFO_CURSOR .initialize (codeInfo , ip , false );
181
175
while (FRAME_INFO_CURSOR .advance ()) {
182
- if (FRAME_COUNT_DATA . shouldSkip ()) {
183
- FRAME_COUNT_DATA . incrementSkipped ();
176
+ if (VISITOR_DATA . shouldSkipFrame ()) {
177
+ VISITOR_DATA . incrementSkippedFrames ();
184
178
continue ;
185
- } else if (FRAME_COUNT_DATA .shouldTruncate ()) {
186
- FRAME_COUNT_DATA .setTruncated ();
179
+ } else if (VISITOR_DATA .shouldTruncate ()) {
180
+ VISITOR_DATA .setTruncated ();
187
181
break ;
188
182
}
183
+
184
+ VISITOR_DATA .incrementUsedFrames ();
189
185
if (data .isNonNull ()) {
190
186
FrameInfoQueryResult frame = FRAME_INFO_CURSOR .get ();
191
187
serializeStackTraceElement (data , frame );
192
188
}
193
- FRAME_COUNT_DATA .incrementTotal ();
194
- numStackTraceElements ++;
195
189
}
196
- return numStackTraceElements ;
197
190
}
198
191
199
192
@ Uninterruptible (reason = "Prevent JFR recording and epoch change." )
@@ -206,48 +199,53 @@ private static void serializeStackTraceElement(JfrNativeEventWriterData data, Fr
206
199
JfrNativeEventWriter .putLong (data , JfrFrameType .FRAME_AOT_COMPILED .getId ());
207
200
}
208
201
209
- private static final class FrameCountData {
210
- private int skipcount ;
211
- private int totalCount ;
212
- private int skippedCount ;
202
+ private static final class StackTraceVisitorData {
203
+ private int framesToSkip ;
204
+ private int skippedFrames ;
205
+ private int usedFrames ;
213
206
private boolean truncated ;
214
207
215
- @ Uninterruptible (reason = Uninterruptible . CALLED_FROM_UNINTERRUPTIBLE_CODE , mayBeInlined = true )
208
+ @ Uninterruptible (reason = CALLED_FROM_UNINTERRUPTIBLE_CODE , mayBeInlined = true )
216
209
public void reset (int skipCount ) {
217
- this . skipcount = skipCount ;
218
- totalCount = 0 ;
219
- skippedCount = 0 ;
210
+ framesToSkip = skipCount ;
211
+ skippedFrames = 0 ;
212
+ usedFrames = 0 ;
220
213
truncated = false ;
221
214
}
222
215
223
- @ Uninterruptible (reason = Uninterruptible .CALLED_FROM_UNINTERRUPTIBLE_CODE , mayBeInlined = true ) //
224
- public boolean shouldSkip () {
225
- return skippedCount < skipcount ;
216
+ @ Uninterruptible (reason = CALLED_FROM_UNINTERRUPTIBLE_CODE , mayBeInlined = true ) //
217
+ public boolean shouldSkipFrame () {
218
+ return skippedFrames < framesToSkip ;
219
+ }
220
+
221
+ @ Uninterruptible (reason = CALLED_FROM_UNINTERRUPTIBLE_CODE , mayBeInlined = true )
222
+ public void incrementSkippedFrames () {
223
+ skippedFrames ++;
226
224
}
227
225
228
- @ Uninterruptible (reason = Uninterruptible . CALLED_FROM_UNINTERRUPTIBLE_CODE , mayBeInlined = true ) //
226
+ @ Uninterruptible (reason = CALLED_FROM_UNINTERRUPTIBLE_CODE , mayBeInlined = true ) //
229
227
public boolean shouldTruncate () {
230
- return totalCount > SubstrateJVM .getStackTraceRepo ().getStackTraceDepth ();
228
+ return usedFrames >= SubstrateJVM .getStackTraceRepo ().getStackTraceDepth ();
231
229
}
232
230
233
- @ Uninterruptible (reason = Uninterruptible . CALLED_FROM_UNINTERRUPTIBLE_CODE , mayBeInlined = true )
231
+ @ Uninterruptible (reason = CALLED_FROM_UNINTERRUPTIBLE_CODE , mayBeInlined = true )
234
232
public void setTruncated () {
235
233
truncated = true ;
236
234
}
237
235
238
- @ Uninterruptible (reason = Uninterruptible . CALLED_FROM_UNINTERRUPTIBLE_CODE , mayBeInlined = true )
236
+ @ Uninterruptible (reason = CALLED_FROM_UNINTERRUPTIBLE_CODE , mayBeInlined = true )
239
237
public boolean isTruncated () {
240
238
return truncated ;
241
239
}
242
240
243
- @ Uninterruptible (reason = Uninterruptible . CALLED_FROM_UNINTERRUPTIBLE_CODE , mayBeInlined = true )
244
- public void incrementSkipped () {
245
- skippedCount ++;
241
+ @ Uninterruptible (reason = CALLED_FROM_UNINTERRUPTIBLE_CODE , mayBeInlined = true )
242
+ public void incrementUsedFrames () {
243
+ usedFrames ++;
246
244
}
247
245
248
- @ Uninterruptible (reason = Uninterruptible . CALLED_FROM_UNINTERRUPTIBLE_CODE , mayBeInlined = true )
249
- public void incrementTotal () {
250
- totalCount ++ ;
246
+ @ Uninterruptible (reason = CALLED_FROM_UNINTERRUPTIBLE_CODE , mayBeInlined = true )
247
+ public int getUsedFrames () {
248
+ return usedFrames ;
251
249
}
252
250
}
253
251
}
0 commit comments