@@ -57,10 +57,16 @@ public final class SamplerJfrStackTraceSerializer implements SamplerStackTraceSe
57
57
/** This value is used by multiple threads but only by a single thread at a time. */
58
58
private static final CodeInfoDecoder .FrameInfoCursor FRAME_INFO_CURSOR = new CodeInfoDecoder .FrameInfoCursor ();
59
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 ();
65
+
60
66
@ Override
61
67
@ Uninterruptible (reason = "Prevent JFR recording and epoch change." )
62
68
public Pointer serializeStackTrace (Pointer rawStackTrace , Pointer bufferEnd , int sampleSize , int sampleHash ,
63
- boolean isTruncated , long sampleTick , long threadId , long threadState ) {
69
+ boolean isTruncated , long sampleTick , long threadId , long threadState , int skipCount ) {
64
70
Pointer current = rawStackTrace ;
65
71
CIntPointer statusPtr = StackValue .get (CIntPointer .class );
66
72
JfrStackTraceRepository .JfrStackTraceTableEntry entry = SubstrateJVM .getStackTraceRepo ().getOrPutStackTrace (current , Word .unsigned (sampleSize ), sampleHash , statusPtr );
@@ -70,7 +76,7 @@ public Pointer serializeStackTrace(Pointer rawStackTrace, Pointer bufferEnd, int
70
76
if (status == JfrStackTraceRepository .JfrStackTraceTableEntryStatus .INSERTED || status == JfrStackTraceRepository .JfrStackTraceTableEntryStatus .EXISTING_RAW ) {
71
77
/* Walk the IPs and serialize the stacktrace. */
72
78
assert current .add (sampleSize ).belowThan (bufferEnd );
73
- boolean serialized = serializeStackTrace (current , sampleSize , isTruncated , stackTraceId );
79
+ boolean serialized = serializeStackTrace (current , sampleSize , isTruncated , stackTraceId , skipCount );
74
80
if (serialized ) {
75
81
SubstrateJVM .getStackTraceRepo ().commitSerializedStackTrace (entry );
76
82
}
@@ -100,7 +106,7 @@ public Pointer serializeStackTrace(Pointer rawStackTrace, Pointer bufferEnd, int
100
106
}
101
107
102
108
@ Uninterruptible (reason = "Prevent JFR recording and epoch change." )
103
- private static boolean serializeStackTrace (Pointer rawStackTrace , int sampleSize , boolean isTruncated , long stackTraceId ) {
109
+ private static boolean serializeStackTrace (Pointer rawStackTrace , int sampleSize , boolean isTruncated , long stackTraceId , int skipCount ) {
104
110
assert sampleSize % Long .BYTES == 0 ;
105
111
106
112
JfrBuffer targetBuffer = SubstrateJVM .getStackTraceRepo ().getCurrentBuffer ();
@@ -112,18 +118,20 @@ private static boolean serializeStackTrace(Pointer rawStackTrace, int sampleSize
112
118
* One IP may correspond to multiple Java-level stack frames. We need to precompute the
113
119
* number of stack trace elements because the count can't be patched later on
114
120
* (JfrNativeEventWriter.putInt() would not necessarily reserve enough bytes).
121
+ *
122
+ * The first pass-through also sets FRAME_COUNT_DATA.isTruncated().
115
123
*/
116
- int numStackTraceElements = visitRawStackTrace (rawStackTrace , sampleSize , Word .nullPointer ());
124
+ int numStackTraceElements = visitRawStackTrace (rawStackTrace , sampleSize , Word .nullPointer (), skipCount );
117
125
if (numStackTraceElements == 0 ) {
118
126
return false ;
119
127
}
120
128
121
129
JfrNativeEventWriterData data = StackValue .get (JfrNativeEventWriterData .class );
122
130
JfrNativeEventWriterDataAccess .initialize (data , targetBuffer );
123
131
JfrNativeEventWriter .putLong (data , stackTraceId );
124
- JfrNativeEventWriter .putBoolean (data , isTruncated );
132
+ JfrNativeEventWriter .putBoolean (data , isTruncated || FRAME_COUNT_DATA . isTruncated () );
125
133
JfrNativeEventWriter .putInt (data , numStackTraceElements );
126
- visitRawStackTrace (rawStackTrace , sampleSize , data );
134
+ visitRawStackTrace (rawStackTrace , sampleSize , data , skipCount );
127
135
boolean success = JfrNativeEventWriter .commit (data );
128
136
129
137
/* Buffer can get replaced with a larger one. */
@@ -132,10 +140,14 @@ private static boolean serializeStackTrace(Pointer rawStackTrace, int sampleSize
132
140
}
133
141
134
142
@ Uninterruptible (reason = "Prevent JFR recording and epoch change." )
135
- private static int visitRawStackTrace (Pointer rawStackTrace , int sampleSize , JfrNativeEventWriterData data ) {
143
+ private static int visitRawStackTrace (Pointer rawStackTrace , int sampleSize , JfrNativeEventWriterData data , int skipCount ) {
136
144
int numStackTraceElements = 0 ;
137
145
Pointer rawStackTraceEnd = rawStackTrace .add (sampleSize );
138
146
Pointer ipPtr = rawStackTrace ;
147
+
148
+ // Reset FrameCountData before every serialization of a new stacktrace.
149
+ FRAME_COUNT_DATA .reset (skipCount );
150
+
139
151
while (ipPtr .belowThan (rawStackTraceEnd )) {
140
152
long ip = ipPtr .readLong (0 );
141
153
numStackTraceElements += visitFrame (data , ip );
@@ -167,10 +179,18 @@ private static int visitFrame(JfrNativeEventWriterData data, CodeInfo codeInfo,
167
179
int numStackTraceElements = 0 ;
168
180
FRAME_INFO_CURSOR .initialize (codeInfo , ip , false );
169
181
while (FRAME_INFO_CURSOR .advance ()) {
182
+ if (FRAME_COUNT_DATA .shouldSkip ()) {
183
+ FRAME_COUNT_DATA .incrementSkipped ();
184
+ continue ;
185
+ } else if (FRAME_COUNT_DATA .shouldTruncate ()) {
186
+ FRAME_COUNT_DATA .setTruncated ();
187
+ break ;
188
+ }
170
189
if (data .isNonNull ()) {
171
190
FrameInfoQueryResult frame = FRAME_INFO_CURSOR .get ();
172
191
serializeStackTraceElement (data , frame );
173
192
}
193
+ FRAME_COUNT_DATA .incrementTotal ();
174
194
numStackTraceElements ++;
175
195
}
176
196
return numStackTraceElements ;
@@ -185,4 +205,49 @@ private static void serializeStackTraceElement(JfrNativeEventWriterData data, Fr
185
205
JfrNativeEventWriter .putInt (data , stackTraceElement .getBci ());
186
206
JfrNativeEventWriter .putLong (data , JfrFrameType .FRAME_AOT_COMPILED .getId ());
187
207
}
208
+
209
+ private static final class FrameCountData {
210
+ private int skipcount ;
211
+ private int totalCount ;
212
+ private int skippedCount ;
213
+ private boolean truncated ;
214
+
215
+ @ Uninterruptible (reason = Uninterruptible .CALLED_FROM_UNINTERRUPTIBLE_CODE , mayBeInlined = true )
216
+ public void reset (int skipCount ) {
217
+ this .skipcount = skipCount ;
218
+ totalCount = 0 ;
219
+ skippedCount = 0 ;
220
+ truncated = false ;
221
+ }
222
+
223
+ @ Uninterruptible (reason = Uninterruptible .CALLED_FROM_UNINTERRUPTIBLE_CODE , mayBeInlined = true ) //
224
+ public boolean shouldSkip () {
225
+ return skippedCount < skipcount ;
226
+ }
227
+
228
+ @ Uninterruptible (reason = Uninterruptible .CALLED_FROM_UNINTERRUPTIBLE_CODE , mayBeInlined = true ) //
229
+ public boolean shouldTruncate () {
230
+ return totalCount > SubstrateJVM .getStackTraceRepo ().getStackTraceDepth ();
231
+ }
232
+
233
+ @ Uninterruptible (reason = Uninterruptible .CALLED_FROM_UNINTERRUPTIBLE_CODE , mayBeInlined = true )
234
+ public void setTruncated () {
235
+ truncated = true ;
236
+ }
237
+
238
+ @ Uninterruptible (reason = Uninterruptible .CALLED_FROM_UNINTERRUPTIBLE_CODE , mayBeInlined = true )
239
+ public boolean isTruncated () {
240
+ return truncated ;
241
+ }
242
+
243
+ @ Uninterruptible (reason = Uninterruptible .CALLED_FROM_UNINTERRUPTIBLE_CODE , mayBeInlined = true )
244
+ public void incrementSkipped () {
245
+ skippedCount ++;
246
+ }
247
+
248
+ @ Uninterruptible (reason = Uninterruptible .CALLED_FROM_UNINTERRUPTIBLE_CODE , mayBeInlined = true )
249
+ public void incrementTotal () {
250
+ totalCount ++;
251
+ }
252
+ }
188
253
}
0 commit comments