Skip to content

Commit

Permalink
JFR getStackTraceId implementation. JFR Tests. Code cleanup.
Browse files Browse the repository at this point in the history
  • Loading branch information
jovanstevanovic committed Oct 7, 2022
1 parent 6ce58a6 commit 6f6188c
Show file tree
Hide file tree
Showing 22 changed files with 397 additions and 87 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public static boolean hasHeapDumpSupport() {
}

@Fold
public static boolean hasJFRSupport() {
public static boolean hasJfrSupport() {
return hasAllOrKeywordMonitoringSupport(MONITORING_JFR_NAME) && !Platform.includedIn(WINDOWS.class);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
import org.graalvm.word.UnsignedWord;
import org.graalvm.word.WordFactory;

import com.oracle.svm.core.UnmanagedMemoryUtil;
import com.oracle.svm.core.Uninterruptible;
import com.oracle.svm.core.UnmanagedMemoryUtil;

/**
* An uninterruptible hashtable with a fixed size that uses chaining in case of a collision.
Expand Down Expand Up @@ -76,6 +76,7 @@ protected UninterruptibleEntry copyToHeap(UninterruptibleEntry pointerOnStack, U

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
protected void free(UninterruptibleEntry entry) {
size--;
ImageSingletons.lookup(UnmanagedMemorySupport.class).free(entry);
}

Expand Down Expand Up @@ -158,7 +159,7 @@ public void clear() {
}
table[i] = WordFactory.nullPointer();
}
size = 0;
assert size == 0 : "The table is not empty!";
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import java.nio.charset.StandardCharsets;
import java.util.concurrent.locks.ReentrantLock;

import com.oracle.svm.core.heap.VMOperationInfos;
import org.graalvm.compiler.api.replacements.Fold;
import org.graalvm.compiler.core.common.NumUtil;
import org.graalvm.nativeimage.IsolateThread;
Expand All @@ -38,12 +37,14 @@
import org.graalvm.word.WordFactory;

import com.oracle.svm.core.Uninterruptible;
import com.oracle.svm.core.heap.VMOperationInfos;
import com.oracle.svm.core.jfr.traceid.JfrTraceIdEpoch;
import com.oracle.svm.core.os.RawFileOperationSupport;
import com.oracle.svm.core.sampler.SamplerBuffersAccess;
import com.oracle.svm.core.thread.JavaVMOperation;
import com.oracle.svm.core.thread.VMOperation;
import com.oracle.svm.core.thread.VMOperationControl;
import com.oracle.svm.core.thread.VMThreads;
import com.oracle.svm.core.jfr.traceid.JfrTraceIdEpoch;

/**
* This class is used when writing the in-memory JFR data to a file. For all operations, except
Expand Down Expand Up @@ -383,6 +384,9 @@ protected void operate() {
*/
@Uninterruptible(reason = "Prevent pollution of the current thread's thread local JFR buffer.")
private void changeEpoch() {
/* Process all unprocessed sampler buffers before changing the epoch. */
SamplerBuffersAccess.processSamplerBuffers();

// Write unflushed data from the thread local buffers but do *not* reinitialize them
// The thread local code will handle space reclamation on their own time
for (IsolateThread thread = VMThreads.firstThread(); thread.isNonNull(); thread = VMThreads.nextThread(thread)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ public final class JfrEvent {
public static final JfrEvent JavaMonitorWait = create("jdk.JavaMonitorWait");

private final long id;
private final String name;

@Platforms(Platform.HOSTED_ONLY.class)
private static JfrEvent create(String name) {
Expand All @@ -84,10 +85,16 @@ private static JfrEvent create(String name, Class<? extends BooleanSupplier> onl
@Platforms(Platform.HOSTED_ONLY.class)
private JfrEvent(String name) {
this.id = JfrMetadataTypeLibrary.lookupPlatformEvent(name);
this.name = name;
}

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public long getId() {
return id;
}

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public String getName() {
return name;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ public static boolean isInConfiguration(boolean allowPrinting) {
throw UserError.abort("FlightRecorder cannot be used to profile the image generator on this platform. " +
"The image generator can only be profiled on platforms where FlightRecoder is also supported at run time.");
}
boolean runtimeEnabled = VMInspectionOptions.hasJFRSupport();
boolean runtimeEnabled = VMInspectionOptions.hasJfrSupport();
if (HOSTED_ENABLED && !runtimeEnabled) {
if (allowPrinting) {
System.err.println("Warning: When FlightRecoder is used to profile the image generator, it is also automatically enabled in the native image at run time. " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@
*/
package com.oracle.svm.core.jfr;

import com.oracle.svm.core.Uninterruptible;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
import org.graalvm.nativeimage.StackValue;
import org.graalvm.word.WordFactory;

import com.oracle.svm.core.SubstrateUtil;
import com.oracle.svm.core.Uninterruptible;
import com.oracle.svm.core.code.FrameInfoQueryResult;
import com.oracle.svm.core.hub.DynamicHub;
import com.oracle.svm.core.jfr.traceid.JfrTraceIdEpoch;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,12 @@
/**
* A daemon thread that is created during JFR startup and torn down by
* {@link SubstrateJVM#destroyJFR}.
*
* <p>
* It is used for persisting the {@link JfrGlobalMemory} buffers to a file and for processing the
* pool of {@link SamplerBuffer}s collected in signal handler (see {@link SubstrateSigprofHandler}).
* With that in mind, the thread is using {@link VMSemaphore} for a synchronization between threads
* as it is async signal safe.
* </p>
*/
public class JfrRecorderThread extends Thread {
private static final int BUFFER_FULL_ENOUGH_PERCENTAGE = 50;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;
import org.graalvm.nativeimage.StackValue;
import org.graalvm.nativeimage.c.function.CodePointer;
import org.graalvm.nativeimage.c.struct.RawField;
import org.graalvm.nativeimage.c.struct.RawStructure;
import org.graalvm.nativeimage.c.struct.SizeOf;
Expand All @@ -35,6 +36,8 @@
import org.graalvm.word.UnsignedWord;
import org.graalvm.word.WordFactory;

import com.oracle.svm.core.FrameAccess;
import com.oracle.svm.core.NeverInline;
import com.oracle.svm.core.SubstrateOptions;
import com.oracle.svm.core.Uninterruptible;
import com.oracle.svm.core.UnmanagedMemoryUtil;
Expand All @@ -47,6 +50,13 @@
import com.oracle.svm.core.locks.VMMutex;
import com.oracle.svm.core.sampler.SamplerBuffer;
import com.oracle.svm.core.sampler.SamplerBufferAccess;
import com.oracle.svm.core.sampler.SamplerSampleWriter;
import com.oracle.svm.core.sampler.SamplerSampleWriterData;
import com.oracle.svm.core.sampler.SamplerSampleWriterDataAccess;
import com.oracle.svm.core.sampler.SamplerThreadLocal;
import com.oracle.svm.core.sampler.SubstrateSigprofHandler;
import com.oracle.svm.core.snippets.KnownIntrinsics;
import com.oracle.svm.core.stack.JavaStackWalker;

/**
* Repository that collects all metadata about stacktraces.
Expand Down Expand Up @@ -88,14 +98,40 @@ public void teardown() {
epochData1.teardown();
}

@Uninterruptible(reason = "Epoch must not change while in this method.")
public long getStackTraceId(@SuppressWarnings("unused") int skipCount) {
@NeverInline("Starting a stack walk in the caller frame.")
@Uninterruptible(reason = "All code that accesses a sampler buffer must be uninterruptible.")
public long getStackTraceId(int skipCount) {
assert maxDepth >= 0;
return 0;
long stackTraceId = 0;

/* Initialize stack walk. */
SamplerSampleWriterData data = StackValue.get(SamplerSampleWriterData.class);
SamplerThreadLocal.setSignalHandlerLocallyDisabled(true);
if (SamplerSampleWriterDataAccess.initialize(data, skipCount, maxDepth)) {
SamplerSampleWriter.begin(data);
/* Walk the stack. */
Pointer sp = KnownIntrinsics.readCallerStackPointer();
CodePointer ip = FrameAccess.singleton().readReturnAddress(sp);
if (JavaStackWalker.walkCurrentThread(sp, ip, SubstrateSigprofHandler.visitor()) || data.getTruncated()) {
acquireLock();
try {
CIntPointer status = StackValue.get(CIntPointer.class);
Pointer start = data.getStartPos().add(SamplerSampleWriter.getHeaderSize());
stackTraceId = getStackTraceId(start, data.getCurrentPos(), data.getHashCode(), status, false);
if (JfrStackTraceTableEntryStatus.get(status, JfrStackTraceTableEntryStatus.NEW)) {
SamplerSampleWriter.end(data);
}
} finally {
releaseLock();
}
}
}
SamplerThreadLocal.setSignalHandlerLocallyDisabled(false);
return stackTraceId;
}

@Uninterruptible(reason = "All code that accesses a sampler buffer must be uninterruptible.")
public long getStackTraceId(Pointer start, Pointer end, int hashCode, CIntPointer isRecorded) {
public long getStackTraceId(Pointer start, Pointer end, int hashCode, CIntPointer status, boolean isSerializationInProgress) {
JfrStackTraceEpochData epochData = getEpochData(false);
JfrStackTraceTableEntry entry = StackValue.get(JfrStackTraceTableEntry.class);

Expand All @@ -107,7 +143,7 @@ public long getStackTraceId(Pointer start, Pointer end, int hashCode, CIntPointe

JfrStackTraceTableEntry result = (JfrStackTraceTableEntry) epochData.visitedStackTraces.get(entry);
if (result.isNonNull()) {
isRecorded.write(1);
JfrStackTraceTableEntryStatus.update(result, status, false, result.getSerialized(), isSerializationInProgress);
return result.getId();
} else {
/* Replace the previous pointer with new one (entry size and hash remains the same). */
Expand All @@ -118,7 +154,7 @@ public long getStackTraceId(Pointer start, Pointer end, int hashCode, CIntPointe
UnmanagedMemoryUtil.copy(start, to, size);

JfrStackTraceTableEntry newEntry = (JfrStackTraceTableEntry) epochData.visitedStackTraces.getOrPut(entry);
isRecorded.write(0);
JfrStackTraceTableEntryStatus.update(newEntry, status, true, false, isSerializationInProgress);
return newEntry.getId();
}
}
Expand All @@ -141,6 +177,7 @@ public void serializeStackTraceHeader(long stackTraceId, boolean isTruncated, in
/* Stacktrace size. */
JfrNativeEventWriter.putInt(data, stackTraceLength);

epochData.numberOfSerializedStackTraces++;
JfrNativeEventWriter.commit(data);

/*
Expand Down Expand Up @@ -195,13 +232,12 @@ public int write(JfrChunkWriter writer) {
}

private static int writeStackTraces(JfrChunkWriter writer, JfrStackTraceEpochData epochData) {
int stackTraceCount = epochData.visitedStackTraces.getSize();
if (stackTraceCount == 0) {
if (epochData.numberOfSerializedStackTraces == 0) {
return EMPTY;
}

writer.writeCompressedLong(JfrType.StackTrace.getId());
writer.writeCompressedInt(stackTraceCount);
writer.writeCompressedInt(epochData.numberOfSerializedStackTraces);
writer.write(epochData.stackTraceBuffer);

return NON_EMPTY;
Expand All @@ -226,6 +262,12 @@ public interface JfrStackTraceTableEntry extends JfrVisited {

@RawField
void setSize(int size);

@RawField
boolean getSerialized();

@RawField
void setSerialized(boolean serialized);
}

public static final class JfrStackTraceTable extends AbstractUninterruptibleHashtable {
Expand Down Expand Up @@ -254,8 +296,31 @@ protected UninterruptibleEntry copyToHeap(UninterruptibleEntry valueOnStack) {
}
}

public static class JfrStackTraceTableEntryStatus {
public static final int NEW = 1;
public static final int SHOULD_SERIALIZE = NEW << 1;
public static final int SERIALIZED = SHOULD_SERIALIZE << 1;

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static void update(JfrStackTraceTableEntry entry, CIntPointer status, boolean setNew, boolean isAlreadySerialized, boolean isSerializationInProgress) {
int isRecorded = setNew ? NEW : 0;
int shouldSerialize = !isAlreadySerialized ? SHOULD_SERIALIZE : 0;
int isSerialized = isAlreadySerialized ? SERIALIZED : 0;
status.write(isRecorded | shouldSerialize | isSerialized);
if (!entry.getSerialized() && isSerializationInProgress) {
entry.setSerialized(true);
}
}

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
public static boolean get(CIntPointer status, int check) {
return (status.read() & check) != 0;
}
}

private static class JfrStackTraceEpochData {
private JfrBuffer stackTraceBuffer;
private int numberOfSerializedStackTraces;
private final JfrStackTraceTable visitedStackTraces;

@Platforms(Platform.HOSTED_ONLY.class)
Expand All @@ -265,6 +330,7 @@ private static class JfrStackTraceEpochData {

void clear() {
visitedStackTraces.clear();
numberOfSerializedStackTraces = 0;
if (stackTraceBuffer.isNonNull()) {
JfrBufferAccess.reinitialize(stackTraceBuffer);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@
import java.util.Map;
import java.util.Properties;

import com.oracle.svm.core.jfr.JfrEventWriteStatus;
import org.graalvm.nativeimage.StackValue;

import com.oracle.svm.core.Uninterruptible;
import com.oracle.svm.core.heap.Heap;
import com.oracle.svm.core.jfr.JfrEvent;
import com.oracle.svm.core.jfr.JfrEventWriteStatus;
import com.oracle.svm.core.jfr.JfrNativeEventWriter;
import com.oracle.svm.core.jfr.JfrNativeEventWriterData;
import com.oracle.svm.core.jfr.JfrNativeEventWriterDataAccess;
Expand All @@ -55,7 +55,7 @@ private static String formatOSInformation() {
}

public static void emit() {
emitClassLoadingStatistics(Heap.getHeap().getClassCount(), 0);
emitClassLoadingStatistics(Heap.getHeap().getClassCount());
emitJVMInformation(JVMInformation.getJVMInfo());
emitOSInformation(formatOSInformation());
emitInitialEnvironmentVariables(getEnvironmentVariables());
Expand Down Expand Up @@ -113,15 +113,15 @@ private static JfrEventWriteStatus emitInitialSystemProperty(JfrNativeEventWrite
}

@Uninterruptible(reason = "Accesses a JFR buffer.")
private static void emitClassLoadingStatistics(long loadedClassCount, long unloadedClassCount) {
private static void emitClassLoadingStatistics(long loadedClassCount) {
if (SubstrateJVM.isRecording() && SubstrateJVM.get().isEnabled(JfrEvent.ClassLoadingStatistics)) {
JfrNativeEventWriterData data = StackValue.get(JfrNativeEventWriterData.class);
JfrNativeEventWriterDataAccess.initializeThreadLocalNativeBuffer(data);

JfrNativeEventWriter.beginSmallEvent(data, JfrEvent.ClassLoadingStatistics);
JfrNativeEventWriter.putLong(data, JfrTicks.elapsedTicks());
JfrNativeEventWriter.putLong(data, loadedClassCount);
JfrNativeEventWriter.putLong(data, unloadedClassCount);
JfrNativeEventWriter.putLong(data, 0); /* unloadedClassCount */
JfrNativeEventWriter.endSmallEvent(data);
}
}
Expand Down
Loading

0 comments on commit 6f6188c

Please sign in to comment.