@@ -57,10 +57,16 @@ public final class SamplerJfrStackTraceSerializer implements SamplerStackTraceSe
5757 /** This value is used by multiple threads but only by a single thread at a time. */
5858 private static final CodeInfoDecoder .FrameInfoCursor FRAME_INFO_CURSOR = new CodeInfoDecoder .FrameInfoCursor ();
5959
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+
6066 @ Override
6167 @ Uninterruptible (reason = "Prevent JFR recording and epoch change." )
6268 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 ) {
6470 Pointer current = rawStackTrace ;
6571 CIntPointer statusPtr = StackValue .get (CIntPointer .class );
6672 JfrStackTraceRepository .JfrStackTraceTableEntry entry = SubstrateJVM .getStackTraceRepo ().getOrPutStackTrace (current , Word .unsigned (sampleSize ), sampleHash , statusPtr );
@@ -70,7 +76,7 @@ public Pointer serializeStackTrace(Pointer rawStackTrace, Pointer bufferEnd, int
7076 if (status == JfrStackTraceRepository .JfrStackTraceTableEntryStatus .INSERTED || status == JfrStackTraceRepository .JfrStackTraceTableEntryStatus .EXISTING_RAW ) {
7177 /* Walk the IPs and serialize the stacktrace. */
7278 assert current .add (sampleSize ).belowThan (bufferEnd );
73- boolean serialized = serializeStackTrace (current , sampleSize , isTruncated , stackTraceId );
79+ boolean serialized = serializeStackTrace (current , sampleSize , isTruncated , stackTraceId , skipCount );
7480 if (serialized ) {
7581 SubstrateJVM .getStackTraceRepo ().commitSerializedStackTrace (entry );
7682 }
@@ -100,7 +106,7 @@ public Pointer serializeStackTrace(Pointer rawStackTrace, Pointer bufferEnd, int
100106 }
101107
102108 @ 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 ) {
104110 assert sampleSize % Long .BYTES == 0 ;
105111
106112 JfrBuffer targetBuffer = SubstrateJVM .getStackTraceRepo ().getCurrentBuffer ();
@@ -112,18 +118,20 @@ private static boolean serializeStackTrace(Pointer rawStackTrace, int sampleSize
112118 * One IP may correspond to multiple Java-level stack frames. We need to precompute the
113119 * number of stack trace elements because the count can't be patched later on
114120 * (JfrNativeEventWriter.putInt() would not necessarily reserve enough bytes).
121+ *
122+ * The first pass-through also sets FRAME_COUNT_DATA.isTruncated().
115123 */
116- int numStackTraceElements = visitRawStackTrace (rawStackTrace , sampleSize , Word .nullPointer ());
124+ int numStackTraceElements = visitRawStackTrace (rawStackTrace , sampleSize , Word .nullPointer (), skipCount );
117125 if (numStackTraceElements == 0 ) {
118126 return false ;
119127 }
120128
121129 JfrNativeEventWriterData data = StackValue .get (JfrNativeEventWriterData .class );
122130 JfrNativeEventWriterDataAccess .initialize (data , targetBuffer );
123131 JfrNativeEventWriter .putLong (data , stackTraceId );
124- JfrNativeEventWriter .putBoolean (data , isTruncated );
132+ JfrNativeEventWriter .putBoolean (data , isTruncated || FRAME_COUNT_DATA . isTruncated () );
125133 JfrNativeEventWriter .putInt (data , numStackTraceElements );
126- visitRawStackTrace (rawStackTrace , sampleSize , data );
134+ visitRawStackTrace (rawStackTrace , sampleSize , data , skipCount );
127135 boolean success = JfrNativeEventWriter .commit (data );
128136
129137 /* Buffer can get replaced with a larger one. */
@@ -132,10 +140,14 @@ private static boolean serializeStackTrace(Pointer rawStackTrace, int sampleSize
132140 }
133141
134142 @ 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 ) {
136144 int numStackTraceElements = 0 ;
137145 Pointer rawStackTraceEnd = rawStackTrace .add (sampleSize );
138146 Pointer ipPtr = rawStackTrace ;
147+
148+ // Reset FrameCountData before every serialization of a new stacktrace.
149+ FRAME_COUNT_DATA .reset (skipCount );
150+
139151 while (ipPtr .belowThan (rawStackTraceEnd )) {
140152 long ip = ipPtr .readLong (0 );
141153 numStackTraceElements += visitFrame (data , ip );
@@ -167,10 +179,18 @@ private static int visitFrame(JfrNativeEventWriterData data, CodeInfo codeInfo,
167179 int numStackTraceElements = 0 ;
168180 FRAME_INFO_CURSOR .initialize (codeInfo , ip , false );
169181 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+ }
170189 if (data .isNonNull ()) {
171190 FrameInfoQueryResult frame = FRAME_INFO_CURSOR .get ();
172191 serializeStackTraceElement (data , frame );
173192 }
193+ FRAME_COUNT_DATA .incrementTotal ();
174194 numStackTraceElements ++;
175195 }
176196 return numStackTraceElements ;
@@ -185,4 +205,49 @@ private static void serializeStackTraceElement(JfrNativeEventWriterData data, Fr
185205 JfrNativeEventWriter .putInt (data , stackTraceElement .getBci ());
186206 JfrNativeEventWriter .putLong (data , JfrFrameType .FRAME_AOT_COMPILED .getId ());
187207 }
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+ }
188253}
0 commit comments