2727import org .graalvm .nativeimage .Platform ;
2828import org .graalvm .nativeimage .Platforms ;
2929import org .graalvm .nativeimage .StackValue ;
30+ import org .graalvm .nativeimage .c .function .CodePointer ;
3031import org .graalvm .nativeimage .c .struct .RawField ;
3132import org .graalvm .nativeimage .c .struct .RawStructure ;
3233import org .graalvm .nativeimage .c .struct .SizeOf ;
3536import org .graalvm .word .UnsignedWord ;
3637import org .graalvm .word .WordFactory ;
3738
39+ import com .oracle .svm .core .FrameAccess ;
40+ import com .oracle .svm .core .NeverInline ;
3841import com .oracle .svm .core .SubstrateOptions ;
3942import com .oracle .svm .core .Uninterruptible ;
4043import com .oracle .svm .core .UnmanagedMemoryUtil ;
4750import com .oracle .svm .core .locks .VMMutex ;
4851import com .oracle .svm .core .sampler .SamplerBuffer ;
4952import com .oracle .svm .core .sampler .SamplerBufferAccess ;
53+ import com .oracle .svm .core .sampler .SamplerSampleWriter ;
54+ import com .oracle .svm .core .sampler .SamplerSampleWriterData ;
55+ import com .oracle .svm .core .sampler .SamplerSampleWriterDataAccess ;
56+ import com .oracle .svm .core .sampler .SamplerThreadLocal ;
57+ import com .oracle .svm .core .sampler .SubstrateSigprofHandler ;
58+ import com .oracle .svm .core .snippets .KnownIntrinsics ;
59+ import com .oracle .svm .core .stack .JavaStackWalker ;
5060
5161/**
5262 * Repository that collects all metadata about stacktraces.
@@ -88,14 +98,40 @@ public void teardown() {
8898 epochData1 .teardown ();
8999 }
90100
91- @ Uninterruptible (reason = "Epoch must not change while in this method." )
92- public long getStackTraceId (@ SuppressWarnings ("unused" ) int skipCount ) {
101+ @ NeverInline ("Starting a stack walk in the caller frame." )
102+ @ Uninterruptible (reason = "All code that accesses a sampler buffer must be uninterruptible." )
103+ public long getStackTraceId (int skipCount ) {
93104 assert maxDepth >= 0 ;
94- return 0 ;
105+ long stackTraceId = 0 ;
106+
107+ /* Initialize stack walk. */
108+ SamplerSampleWriterData data = StackValue .get (SamplerSampleWriterData .class );
109+ SamplerThreadLocal .setSignalHandlerLocallyDisabled (true );
110+ if (SamplerSampleWriterDataAccess .initialize (data , skipCount , maxDepth )) {
111+ SamplerSampleWriter .begin (data );
112+ /* Walk the stack. */
113+ Pointer sp = KnownIntrinsics .readCallerStackPointer ();
114+ CodePointer ip = FrameAccess .singleton ().readReturnAddress (sp );
115+ if (JavaStackWalker .walkCurrentThread (sp , ip , SubstrateSigprofHandler .visitor ()) || data .getTruncated ()) {
116+ acquireLock ();
117+ try {
118+ CIntPointer status = StackValue .get (CIntPointer .class );
119+ Pointer start = data .getStartPos ().add (SamplerSampleWriter .getHeaderSize ());
120+ stackTraceId = getStackTraceId (start , data .getCurrentPos (), data .getHashCode (), status , false );
121+ if (JfrStackTraceTableEntryStatus .get (status , JfrStackTraceTableEntryStatus .NEW )) {
122+ SamplerSampleWriter .end (data );
123+ }
124+ } finally {
125+ releaseLock ();
126+ }
127+ }
128+ }
129+ SamplerThreadLocal .setSignalHandlerLocallyDisabled (false );
130+ return stackTraceId ;
95131 }
96132
97133 @ Uninterruptible (reason = "All code that accesses a sampler buffer must be uninterruptible." )
98- public long getStackTraceId (Pointer start , Pointer end , int hashCode , CIntPointer isRecorded ) {
134+ public long getStackTraceId (Pointer start , Pointer end , int hashCode , CIntPointer status , boolean isSerializationInProgress ) {
99135 JfrStackTraceEpochData epochData = getEpochData (false );
100136 JfrStackTraceTableEntry entry = StackValue .get (JfrStackTraceTableEntry .class );
101137
@@ -107,7 +143,7 @@ public long getStackTraceId(Pointer start, Pointer end, int hashCode, CIntPointe
107143
108144 JfrStackTraceTableEntry result = (JfrStackTraceTableEntry ) epochData .visitedStackTraces .get (entry );
109145 if (result .isNonNull ()) {
110- isRecorded . write ( 1 );
146+ JfrStackTraceTableEntryStatus . update ( result , status , false , result . getSerialized (), isSerializationInProgress );
111147 return result .getId ();
112148 } else {
113149 /* Replace the previous pointer with new one (entry size and hash remains the same). */
@@ -118,7 +154,7 @@ public long getStackTraceId(Pointer start, Pointer end, int hashCode, CIntPointe
118154 UnmanagedMemoryUtil .copy (start , to , size );
119155
120156 JfrStackTraceTableEntry newEntry = (JfrStackTraceTableEntry ) epochData .visitedStackTraces .getOrPut (entry );
121- isRecorded . write ( 0 );
157+ JfrStackTraceTableEntryStatus . update ( newEntry , status , true , false , isSerializationInProgress );
122158 return newEntry .getId ();
123159 }
124160 }
@@ -141,6 +177,7 @@ public void serializeStackTraceHeader(long stackTraceId, boolean isTruncated, in
141177 /* Stacktrace size. */
142178 JfrNativeEventWriter .putInt (data , stackTraceLength );
143179
180+ epochData .numberOfSerializedStackTraces ++;
144181 JfrNativeEventWriter .commit (data );
145182
146183 /*
@@ -195,13 +232,12 @@ public int write(JfrChunkWriter writer) {
195232 }
196233
197234 private static int writeStackTraces (JfrChunkWriter writer , JfrStackTraceEpochData epochData ) {
198- int stackTraceCount = epochData .visitedStackTraces .getSize ();
199- if (stackTraceCount == 0 ) {
235+ if (epochData .numberOfSerializedStackTraces == 0 ) {
200236 return EMPTY ;
201237 }
202238
203239 writer .writeCompressedLong (JfrType .StackTrace .getId ());
204- writer .writeCompressedInt (stackTraceCount );
240+ writer .writeCompressedInt (epochData . numberOfSerializedStackTraces );
205241 writer .write (epochData .stackTraceBuffer );
206242
207243 return NON_EMPTY ;
@@ -226,6 +262,12 @@ public interface JfrStackTraceTableEntry extends JfrVisited {
226262
227263 @ RawField
228264 void setSize (int size );
265+
266+ @ RawField
267+ boolean getSerialized ();
268+
269+ @ RawField
270+ void setSerialized (boolean serialized );
229271 }
230272
231273 public static final class JfrStackTraceTable extends AbstractUninterruptibleHashtable {
@@ -254,8 +296,31 @@ protected UninterruptibleEntry copyToHeap(UninterruptibleEntry valueOnStack) {
254296 }
255297 }
256298
299+ public static class JfrStackTraceTableEntryStatus {
300+ public static final int NEW = 1 ;
301+ public static final int SHOULD_SERIALIZE = NEW << 1 ;
302+ public static final int SERIALIZED = SHOULD_SERIALIZE << 1 ;
303+
304+ @ Uninterruptible (reason = "Called from uninterruptible code." , mayBeInlined = true )
305+ public static void update (JfrStackTraceTableEntry entry , CIntPointer status , boolean setNew , boolean isAlreadySerialized , boolean isSerializationInProgress ) {
306+ int isRecorded = setNew ? NEW : 0 ;
307+ int shouldSerialize = !isAlreadySerialized ? SHOULD_SERIALIZE : 0 ;
308+ int isSerialized = isAlreadySerialized ? SERIALIZED : 0 ;
309+ status .write (isRecorded | shouldSerialize | isSerialized );
310+ if (!entry .getSerialized () && isSerializationInProgress ) {
311+ entry .setSerialized (true );
312+ }
313+ }
314+
315+ @ Uninterruptible (reason = "Called from uninterruptible code." , mayBeInlined = true )
316+ public static boolean get (CIntPointer status , int check ) {
317+ return (status .read () & check ) != 0 ;
318+ }
319+ }
320+
257321 private static class JfrStackTraceEpochData {
258322 private JfrBuffer stackTraceBuffer ;
323+ private int numberOfSerializedStackTraces ;
259324 private final JfrStackTraceTable visitedStackTraces ;
260325
261326 @ Platforms (Platform .HOSTED_ONLY .class )
@@ -265,6 +330,7 @@ private static class JfrStackTraceEpochData {
265330
266331 void clear () {
267332 visitedStackTraces .clear ();
333+ numberOfSerializedStackTraces = 0 ;
268334 if (stackTraceBuffer .isNonNull ()) {
269335 JfrBufferAccess .reinitialize (stackTraceBuffer );
270336 }
0 commit comments