Skip to content

Commit e5bcf34

Browse files
fix old object constant bug
1 parent 787c80a commit e5bcf34

14 files changed

+391
-47
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/collections/UninterruptiblePriorityQueue.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ public boolean isFull() {
5151
return size == queue.length;
5252
}
5353

54+
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
55+
public boolean isEmpty() {
56+
return size == 0;
57+
}
58+
5459
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
5560
public boolean add(UninterruptibleComparable e) {
5661
return offer(e);

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrStackTraceRepository.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import com.oracle.svm.core.locks.VMMutex;
5353
import com.oracle.svm.core.memory.NullableNativeMemory;
5454
import com.oracle.svm.core.nmt.NmtCategory;
55+
import com.oracle.svm.core.sampler.SamplerBuffer;
5556
import com.oracle.svm.core.sampler.SamplerSampleWriter;
5657
import com.oracle.svm.core.sampler.SamplerSampleWriterData;
5758
import com.oracle.svm.core.sampler.SamplerSampleWriterDataAccess;
@@ -135,6 +136,49 @@ public long getStackTraceId(int skipCount) {
135136
}
136137
}
137138

139+
/** Don't store to the stacktrace repo here (it may not belong in this epoch). */
140+
@NeverInline("Starting a stack walk in the caller frame.")
141+
@Uninterruptible(reason = "Result is only valid until epoch changes.", callerMustBe = true)
142+
public boolean recordStackTraceDataToBuffer(int skipCount, SamplerBuffer rawStackTraceData) {
143+
if (DeoptimizationSupport.enabled() || rawStackTraceData.isNull()) {
144+
/* Stack traces are not supported if JIT compilation is used (GR-43686). */
145+
return false;
146+
}
147+
148+
/**
149+
* The JFR sampler does not share the same buffers as the leak profiler. There is no risk of
150+
* races between them.
151+
*/
152+
SamplerSampleWriterData data = StackValue.get(SamplerSampleWriterData.class);
153+
if (rawStackTraceData.isNull() || !SamplerSampleWriterDataAccess.initialize(data, skipCount, true, rawStackTraceData)) {
154+
return false;
155+
}
156+
157+
assert SamplerSampleWriterDataAccess.verify(data);
158+
assert data.getCurrentPos().unsignedRemainder(Long.BYTES).equal(0);
159+
160+
/*
161+
* Start a stack trace and do a stack walk. Note that the data will only be committed to the
162+
* buffer if it is a new stack trace.
163+
*/
164+
SamplerSampleWriter.begin(data);
165+
Pointer sp = KnownIntrinsics.readCallerStackPointer();
166+
CodePointer ip = FrameAccess.singleton().readReturnAddress(CurrentIsolate.getCurrentThread(), sp);
167+
int errorCode = JfrStackWalker.walkCurrentThread(data, ip, sp, false);
168+
169+
/*
170+
* If writing the raw data to the buffer succeeded, commit it here. Do not attempt
171+
* deduplication yet since the event data this stack trace corresponds to may not be emitted
172+
* until a much later epoch.
173+
*/
174+
return switch (errorCode) {
175+
case JfrStackWalker.NO_ERROR, JfrStackWalker.TRUNCATED -> SamplerSampleWriter.end(data, SamplerSampleWriter.JFR_STACK_TRACE_END);
176+
case JfrStackWalker.BUFFER_SIZE_EXCEEDED -> false;
177+
case JfrStackWalker.UNPARSEABLE_STACK -> throw VMError.shouldNotReachHere("Only the async sampler may encounter an unparseable stack.");
178+
default -> throw VMError.shouldNotReachHere("Unexpected return value");
179+
};
180+
}
181+
138182
@Uninterruptible(reason = "Result is only valid until epoch changes.", callerMustBe = true)
139183
private long storeDeduplicatedStackTrace(SamplerSampleWriterData data) {
140184
if (SamplerSampleWriter.isValid(data)) {

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/SubstrateJVM.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import com.oracle.svm.core.jfr.sampler.JfrExecutionSampler;
4444
import com.oracle.svm.core.jfr.throttling.JfrEventThrottling;
4545
import com.oracle.svm.core.log.Log;
46+
import com.oracle.svm.core.sampler.SamplerBuffer;
4647
import com.oracle.svm.core.sampler.SamplerBufferPool;
4748
import com.oracle.svm.core.sampler.SamplerBuffersAccess;
4849
import com.oracle.svm.core.sampler.SamplerStatistics;
@@ -281,6 +282,18 @@ public long getStackTraceId(long eventTypeId, int skipCount) {
281282
}
282283
}
283284

285+
/*
286+
* Buffers are not shared with the sampler or other JFR event types. This data will not yet be
287+
* tied to a specific epoch until event emission.
288+
*/
289+
@Uninterruptible(reason = Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
290+
public boolean recordStackTraceDataToBuffer(JfrEvent eventType, int skipCount, SamplerBuffer rawStackTraceData) {
291+
if (isRecording() && isStackTraceEnabled(eventType.getId())) {
292+
return stackTraceRepo.recordStackTraceDataToBuffer(skipCount, rawStackTraceData);
293+
}
294+
return false;
295+
}
296+
284297
/**
285298
* See {@link JVM#getStackTraceId}.
286299
*/

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/oldobject/JfrOldObject.java

Lines changed: 55 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,18 @@
3232

3333
import jdk.graal.compiler.word.Word;
3434
import org.graalvm.word.UnsignedWord;
35+
import org.graalvm.word.WordFactory;
3536

3637
import com.oracle.svm.core.Uninterruptible;
3738
import com.oracle.svm.core.collections.UninterruptibleComparable;
3839
import com.oracle.svm.core.collections.UninterruptibleLinkedList;
3940
import com.oracle.svm.core.heap.ReferenceInternals;
4041
import com.oracle.svm.core.jfr.JfrTicks;
42+
import com.oracle.svm.core.memory.NullableNativeMemory;
43+
import com.oracle.svm.core.nmt.NmtCategory;
44+
import com.oracle.svm.core.sampler.SamplerBuffer;
45+
import com.oracle.svm.core.sampler.SamplerBufferAccess;
46+
import com.oracle.svm.core.jfr.SubstrateJVM;
4147

4248
/**
4349
* Holds information about a sampled object. This data may only be accessed while holding the
@@ -50,26 +56,29 @@ public final class JfrOldObject implements UninterruptibleComparable, Uninterrup
5056
private UnsignedWord span;
5157
private UnsignedWord objectSize;
5258
private long allocationTicks;
53-
private long threadId;
54-
private long stackTraceId;
5559
private UnsignedWord heapUsedAfterLastGC;
5660
private int arrayLength;
61+
private SamplerBuffer rawStackTraceData;
62+
private Thread thread;
63+
private boolean stackTraceValid;
5764

5865
JfrOldObject() {
5966
this.reference = new WeakReference<>(null);
67+
this.rawStackTraceData = WordFactory.nullPointer();
6068
}
6169

6270
@SuppressWarnings("hiding")
6371
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
64-
void initialize(Object obj, UnsignedWord span, UnsignedWord allocatedSize, long threadId, long stackTraceId, UnsignedWord heapUsedAfterLastGC, int arrayLength) {
72+
void initialize(Object obj, UnsignedWord span, UnsignedWord allocatedSize, Thread thread, UnsignedWord heapUsedAfterLastGC, int arrayLength) {
6573
ReferenceInternals.setReferent(reference, obj);
6674
this.span = span;
6775
this.objectSize = allocatedSize;
6876
this.allocationTicks = JfrTicks.elapsedTicks();
69-
this.threadId = threadId;
70-
this.stackTraceId = stackTraceId;
7177
this.heapUsedAfterLastGC = heapUsedAfterLastGC;
7278
this.arrayLength = arrayLength;
79+
this.thread = thread;
80+
this.stackTraceValid = false;
81+
this.rawStackTraceData = WordFactory.nullPointer();
7382
}
7483

7584
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
@@ -78,11 +87,12 @@ void reset() {
7887
this.span = Word.zero();
7988
this.objectSize = Word.zero();
8089
this.allocationTicks = 0L;
81-
this.threadId = 0L;
82-
this.stackTraceId = 0L;
8390
this.heapUsedAfterLastGC = Word.zero();
8491
this.arrayLength = 0;
8592
this.next = null;
93+
this.thread = null;
94+
this.stackTraceValid = false;
95+
freeRawStackTraceData();
8696
}
8797

8898
@Override
@@ -118,23 +128,53 @@ public long getAllocationTicks() {
118128
}
119129

120130
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
121-
public long getThreadId() {
122-
return threadId;
131+
public UnsignedWord getHeapUsedAfterLastGC() {
132+
return heapUsedAfterLastGC;
123133
}
124134

125135
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
126-
public long getStackTraceId() {
127-
return stackTraceId;
136+
public int getArrayLength() {
137+
return arrayLength;
128138
}
129139

130140
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
131-
public UnsignedWord getHeapUsedAfterLastGC() {
132-
return heapUsedAfterLastGC;
141+
public Thread getThread() {
142+
return thread;
133143
}
134144

135145
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
136-
public int getArrayLength() {
137-
return arrayLength;
146+
public boolean getStackTraceValid() {
147+
return stackTraceValid;
148+
}
149+
150+
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
151+
public void setStackTraceValid(boolean valid) {
152+
stackTraceValid = valid;
153+
}
154+
155+
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
156+
public SamplerBuffer getRawStackTraceData() {
157+
if (rawStackTraceData.isNull()) {
158+
// TODO add OldObjectSample event skipcount here too.
159+
long bufferDataSize = Long.SIZE * SubstrateJVM.getStackTraceRepo().getStackTraceDepth();
160+
rawStackTraceData = NullableNativeMemory.malloc(SamplerBufferAccess.getTotalBufferSize(WordFactory.unsigned(bufferDataSize)), NmtCategory.JFR);
161+
if (rawStackTraceData.isNonNull()) {
162+
rawStackTraceData.setSize(WordFactory.unsigned(bufferDataSize));
163+
rawStackTraceData.setNext(Word.nullPointer());
164+
SamplerBufferAccess.reinitialize(rawStackTraceData);
165+
} else {
166+
this.stackTraceValid = false;
167+
}
168+
}
169+
return rawStackTraceData;
170+
}
171+
172+
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
173+
private void freeRawStackTraceData() {
174+
if (rawStackTraceData.isNonNull()) {
175+
NullableNativeMemory.free(rawStackTraceData);
176+
this.rawStackTraceData = WordFactory.nullPointer();
177+
}
138178
}
139179

140180
@Override

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/oldobject/JfrOldObjectProfiler.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,15 @@ public void configure(int oldObjectQueueSize) {
6464
}
6565

6666
public void reset() {
67+
if (this.sampler != null) {
68+
this.sampler.reset();
69+
}
6770
this.sampler = new JfrOldObjectSampler(queueSize);
6871
}
6972

7073
public void teardown() {
74+
// Reset to free buffers.
75+
this.sampler.reset();
7176
this.sampler = null;
7277
}
7378

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/oldobject/JfrOldObjectSampler.java

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@
2828

2929
import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE;
3030

31+
import org.graalvm.word.Pointer;
3132
import org.graalvm.word.UnsignedWord;
33+
import org.graalvm.word.WordFactory;
3234

3335
import com.oracle.svm.core.util.BasedOnJDKFile;
3436
import com.oracle.svm.core.Uninterruptible;
@@ -39,6 +41,10 @@
3941
import com.oracle.svm.core.jfr.JfrTicks;
4042
import com.oracle.svm.core.jfr.SubstrateJVM;
4143
import com.oracle.svm.core.jfr.events.OldObjectSampleEvent;
44+
import com.oracle.svm.core.sampler.SamplerBuffer;
45+
import com.oracle.svm.core.sampler.SamplerBufferAccess;
46+
import com.oracle.svm.core.sampler.SamplerJfrStackTraceSerializer;
47+
import com.oracle.svm.core.sampler.SamplerSampleWriter;
4248
import com.oracle.svm.core.thread.JavaThreads;
4349

4450
/**
@@ -145,20 +151,21 @@ private void evict() {
145151
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
146152
private void release(JfrOldObject sample) {
147153
usedList.remove(sample);
148-
freeList.append(sample);
149154
sample.reset();
155+
freeList.append(sample);
150156
}
151157

152158
@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+26/src/hotspot/share/jfr/leakprofiler/sampling/samplePriorityQueue.cpp#L44-L53")
153159
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
154160
private void store(Object obj, UnsignedWord span, UnsignedWord allocatedSize, int arrayLength) {
155161
Thread thread = JavaThreads.getCurrentThreadOrNull();
156-
long threadId = thread == null ? 0L : JavaThreads.getThreadId(thread);
157-
long stackTraceId = thread == null ? 0L : SubstrateJVM.get().getStackTraceId(JfrEvent.OldObjectSample, 0);
158162
UnsignedWord heapUsedAfterLastGC = Heap.getHeap().getUsedMemoryAfterLastGC();
159163

160164
JfrOldObject sample = (JfrOldObject) freeList.pop();
161-
sample.initialize(obj, span, allocatedSize, threadId, stackTraceId, heapUsedAfterLastGC, arrayLength);
165+
sample.initialize(obj, span, allocatedSize, thread, heapUsedAfterLastGC, arrayLength);
166+
// Allocate a raw stacktrace buffer and write to it.
167+
boolean stackTraceValid = SubstrateJVM.get().recordStackTraceDataToBuffer(JfrEvent.OldObjectSample, 0, sample.getRawStackTraceData());
168+
sample.setStackTraceValid(stackTraceValid);
162169
queue.add(sample);
163170
usedList.append(sample);
164171
totalInQueue = totalInQueue.add(span);
@@ -184,8 +191,16 @@ private void emitUnchained() {
184191
long objectId = SubstrateJVM.getOldObjectRepository().serializeOldObject(obj);
185192
UnsignedWord objectSize = cur.getObjectSize();
186193
long allocationTicks = cur.getAllocationTicks();
187-
long threadId = cur.getThreadId();
188-
long stackTraceId = cur.getStackTraceId();
194+
Thread thread = cur.getThread();
195+
long threadId = 0L;
196+
if (thread != null) {
197+
threadId = JavaThreads.getThreadId(thread);
198+
SubstrateJVM.getThreadRepo().registerThread(thread);
199+
}
200+
long stackTraceId = 0L;
201+
if (cur.getStackTraceValid()) {
202+
stackTraceId = deduplicateAndSerializeStackTrace(cur.getRawStackTraceData());
203+
}
189204
UnsignedWord heapUsedAfterLastGC = cur.getHeapUsedAfterLastGC();
190205
int arrayLength = cur.getArrayLength();
191206

@@ -196,6 +211,39 @@ private void emitUnchained() {
196211
}
197212
}
198213

214+
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
215+
private static long deduplicateAndSerializeStackTrace(SamplerBuffer buffer) {
216+
if (buffer.isNull()) {
217+
// Buffer may have failed allocation.
218+
return 0L;
219+
}
220+
221+
// decode the buffer
222+
Pointer bufferEnd = buffer.getPos();
223+
Pointer current = SamplerBufferAccess.getDataStart(buffer);
224+
Pointer entryStart = current;
225+
assert entryStart.unsignedRemainder(Long.BYTES).equal(0);
226+
227+
/* Sample hash. */
228+
int sampleHash = current.readInt(0);
229+
current = current.add(Integer.BYTES);
230+
231+
/* Is truncated. */
232+
boolean isTruncated = current.readInt(0) == 1;
233+
current = current.add(Integer.BYTES);
234+
235+
/* Sample size, excluding the header and the end marker. */
236+
int sampleSize = current.readInt(0);
237+
current = current.add(Integer.BYTES);
238+
239+
/* skip over padding, ticks, event thread, and thread state. */
240+
current = current.add(Integer.BYTES).add(Long.BYTES * 3);
241+
242+
assert current.subtract(entryStart).equal(SamplerSampleWriter.getHeaderSize());
243+
244+
return SamplerJfrStackTraceSerializer.deduplicateAndSerializeStackTrace(current, bufferEnd, sampleSize, sampleHash, isTruncated);
245+
}
246+
199247
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
200248
JfrOldObject getOldestObject() {
201249
return (JfrOldObject) usedList.getHead();
@@ -205,4 +253,14 @@ JfrOldObject getOldestObject() {
205253
private JfrOldObject getObjectWithSmallestSpan() {
206254
return (JfrOldObject) queue.peek();
207255
}
256+
257+
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
258+
public void reset() {
259+
while (!queue.isEmpty()) {
260+
evict();
261+
}
262+
assert usedList.isEmpty();
263+
totalAllocated = WordFactory.unsigned(0);
264+
totalInQueue = WordFactory.unsigned(0);
265+
}
208266
}

0 commit comments

Comments
 (0)