From 93553d435a5d4f42d33d4c07769525075d336da8 Mon Sep 17 00:00:00 2001 From: Jaroslav Bachorik Date: Thu, 13 Feb 2025 16:12:26 +0000 Subject: [PATCH 1/4] Enable ASGCT by default on fairly safe J9 JDK versions --- ddprof-lib/src/main/cpp/arguments.cpp | 10 ++ ddprof-lib/src/main/cpp/j9Ext.h | 35 +++-- ddprof-lib/src/main/cpp/javaApi.cpp | 8 ++ ddprof-lib/src/main/cpp/profiler.cpp | 35 ++++- ddprof-lib/src/main/cpp/profiler.h | 3 + ddprof-lib/src/main/cpp/vmEntry.cpp | 4 - .../com/datadoghq/profiler/JavaProfiler.java | 6 + .../profiler/AbstractProcessProfilerTest.java | 94 ++++++++++++++ .../datadoghq/profiler/ExternalLauncher.java | 28 +++- .../com/datadoghq/profiler/JVMAccessTest.java | 99 +++++--------- .../datadoghq/profiler/JavaProfilerTest.java | 121 ++++++++++++------ 11 files changed, 314 insertions(+), 129 deletions(-) create mode 100644 ddprof-test/src/test/java/com/datadoghq/profiler/AbstractProcessProfilerTest.java diff --git a/ddprof-lib/src/main/cpp/arguments.cpp b/ddprof-lib/src/main/cpp/arguments.cpp index 051755aff..5de272d9e 100644 --- a/ddprof-lib/src/main/cpp/arguments.cpp +++ b/ddprof-lib/src/main/cpp/arguments.cpp @@ -15,6 +15,8 @@ */ #include "arguments.h" +#include "vmEntry.h" + #include #include #include @@ -342,6 +344,14 @@ Error Arguments::parse(const char *args) { _event = EVENT_CPU; } + if (VM::isOpenJ9()) { + if (_cstack == CSTACK_FP) { + // J9 is compiled without FP + // switch to DWARF for better results + _cstack = CSTACK_DWARF; + } + } + return Error::OK; } diff --git a/ddprof-lib/src/main/cpp/j9Ext.h b/ddprof-lib/src/main/cpp/j9Ext.h index 0e5c10a4b..028d0aa7a 100644 --- a/ddprof-lib/src/main/cpp/j9Ext.h +++ b/ddprof-lib/src/main/cpp/j9Ext.h @@ -20,7 +20,9 @@ #include +#include "log.h" #include "vmEntry.h" +#include "vmStructs.h" #define JVMTI_EXT(f, ...) ((jvmtiError(*)(jvmtiEnv *, __VA_ARGS__))f) @@ -54,25 +56,40 @@ class J9Ext { public: static bool can_use_ASGCT() { + // as of 21.0.6 the use of ASGCT will lead to almost immediate crash + // or livelock on J9 return (VM::java_version() == 8 && VM::java_update_version() >= 362) || (VM::java_version() == 11 && VM::java_update_version() >= 18) || (VM::java_version() == 17 && VM::java_update_version() >= 6) || - (VM::java_version() >= 18); - } - - static bool is_jmethodid_safe() { - return VM::java_version() == 8 || - (VM::java_version() == 11 && VM::java_update_version() >= 23) || - (VM::java_version() == 17 && VM::java_update_version() >= 11) || - (VM::java_version() == 21 && VM::java_update_version() >= 3) || - (VM::java_version() >= 22); + (VM::java_version() >= 18 && VM::java_version() < 21); } static bool is_jvmti_jmethodid_safe() { // only JDK 8 is safe to use jmethodID in JVMTI for deferred resolution + // unless -XX:+KeepJNIIDs=true is provided return VM::java_version() == 8; } + static bool shouldUseAsgct() { + char *sampler = NULL; + + jvmtiEnv *jvmti = VM::jvmti(); + jvmti->GetSystemProperty("dd.profiling.ddprof.j9.sampler", &sampler); + + bool asgct = true; + if (sampler != nullptr) { + if (!strncmp("asgct", sampler, 5)) { + asgct = true; + } else if (!strncmp("jvmti", sampler, 5)) { + asgct = false; + } else { + fprintf(stdout, "[ddprof] [WARN] Invalid J9 sampler: %s, supported values are [jvmti, asgct]", sampler); + } + } + jvmti->Deallocate((unsigned char *)sampler); + return asgct; + } + static bool initialize(jvmtiEnv *jvmti, const void *j9thread_self); static int GetOSThreadID(jthread thread) { diff --git a/ddprof-lib/src/main/cpp/javaApi.cpp b/ddprof-lib/src/main/cpp/javaApi.cpp index 15fd4ae79..365d1dc45 100644 --- a/ddprof-lib/src/main/cpp/javaApi.cpp +++ b/ddprof-lib/src/main/cpp/javaApi.cpp @@ -109,6 +109,14 @@ Java_com_datadoghq_profiler_JavaProfiler_execute0(JNIEnv *env, jobject unused, return NULL; } +extern "C" DLLEXPORT jstring JNICALL +Java_com_datadoghq_profiler_JavaProfiler_getStatus0(JNIEnv* env, + jobject unused) { + char msg[2048]; + int ret = Profiler::instance()->status((char*)msg, sizeof(msg) - 1); + return env->NewStringUTF(msg); +} + extern "C" DLLEXPORT jlong JNICALL Java_com_datadoghq_profiler_JavaProfiler_getSamples(JNIEnv *env, jobject unused) { diff --git a/ddprof-lib/src/main/cpp/profiler.cpp b/ddprof-lib/src/main/cpp/profiler.cpp index deec41fd0..4d60f3d3e 100644 --- a/ddprof-lib/src/main/cpp/profiler.cpp +++ b/ddprof-lib/src/main/cpp/profiler.cpp @@ -985,11 +985,16 @@ Engine *Profiler::selectCpuEngine(Arguments &args) { return &noop_engine; } else if (args._cpu >= 0 || strcmp(args._event, EVENT_CPU) == 0) { if (VM::isOpenJ9()) { - if (!J9Ext::is_jvmti_jmethodid_safe()) { - Log::warn("Safe jmethodID access is not available on this JVM. Using " - "CPU profiler on your own risk."); + if (!J9Ext::shouldUseAsgct() || !J9Ext::can_use_ASGCT()) { + if (!J9Ext::is_jvmti_jmethodid_safe()) { + fprintf(stderr, "[ddprof] [WARN] Safe jmethodID access is not available on this JVM. Using " + "CPU profiler on your own risk. Use -XX:+KeepJNIIDs=true JVM " + "flag to make access to jmethodIDs safe, if your JVM supports it\n"); + } + TEST_LOG("J9[cpu]=jvmti"); + return &j9_engine; } - return &j9_engine; + TEST_LOG("J9[cpu]=asgct"); } return !ctimer.check(args) ? (Engine *)&ctimer @@ -1012,14 +1017,17 @@ Engine *Profiler::selectWallEngine(Arguments &args) { return &noop_engine; } if (VM::isOpenJ9()) { - if (args._wallclock_sampler == JVMTI) { + if (args._wallclock_sampler == JVMTI || !J9Ext::shouldUseAsgct() || !J9Ext::can_use_ASGCT()) { if (!J9Ext::is_jvmti_jmethodid_safe()) { - Log::warn("Safe jmethodID access is not available on this JVM. Using " - "wallclock profiler on your own risk."); + fprintf(stderr, "[ddprof] [WARN] Safe jmethodID access is not available on this JVM. Using " + "wallclock profiler on your own risk. Use -XX:+KeepJNIIDs=true JVM " + "flag to make access to jmethodIDs safe, if your JVM supports it\n"); } j9_engine.sampleIdleThreads(); + TEST_LOG("J9[wall]=jvmti"); return (Engine *)&j9_engine; } else { + TEST_LOG("J9[wall]=asgct"); return (Engine *)&wall_asgct_engine; } } @@ -1474,3 +1482,16 @@ int Profiler::lookupClass(const char *key, size_t length) { // unable to lookup the class return -1; } + +int Profiler::status(char* status, int max_len) { + return snprintf(status, max_len, + "== Java-Profiler Status ===\n" + " Running : %s\n" + " CPU Engine : %s\n" + " WallClock Engine : %s\n" + " Allocations : %s\n", + _state == RUNNING ? "true" : "false", + _cpu_engine != nullptr ? _cpu_engine->name() : "None", + _wall_engine != nullptr ? _wall_engine->name() : "None", + _alloc_engine != nullptr ? _alloc_engine->name() : "None"); +} diff --git a/ddprof-lib/src/main/cpp/profiler.h b/ddprof-lib/src/main/cpp/profiler.h index 7e90918e2..82926ec34 100644 --- a/ddprof-lib/src/main/cpp/profiler.h +++ b/ddprof-lib/src/main/cpp/profiler.h @@ -135,6 +135,7 @@ class Profiler { void updateJavaThreadNames(); void updateNativeThreadNames(); void mangle(const char *name, char *buf, size_t size); + Engine *selectCpuEngine(Arguments &args); Engine *selectWallEngine(Arguments &args); Engine *selectAllocEngine(Arguments &args); @@ -169,6 +170,8 @@ class Profiler { return _instance; } + int status(char* status, int max_len); + const char* cstack() { switch (_cstack) { case CSTACK_DEFAULT: return "default"; diff --git a/ddprof-lib/src/main/cpp/vmEntry.cpp b/ddprof-lib/src/main/cpp/vmEntry.cpp index dcd42f7ac..321476279 100644 --- a/ddprof-lib/src/main/cpp/vmEntry.cpp +++ b/ddprof-lib/src/main/cpp/vmEntry.cpp @@ -383,10 +383,6 @@ bool VM::initProfilerBridge(JavaVM *vm, bool attach) { *flag_addr = 1; } } - char *flag_addr = (char *)JVMFlag::find("KeepJNIIDs", {JVMFlag::Type::Bool}); - if (flag_addr != NULL) { - *flag_addr = 1; - } // if the user sets -XX:+UseAdaptiveGCBoundary we will just disable the // profiler to avoid the risk of crashing flag was made obsolete (inert) in 15 diff --git a/ddprof-lib/src/main/java/com/datadoghq/profiler/JavaProfiler.java b/ddprof-lib/src/main/java/com/datadoghq/profiler/JavaProfiler.java index db8d4b80a..b1a2795bf 100644 --- a/ddprof-lib/src/main/java/com/datadoghq/profiler/JavaProfiler.java +++ b/ddprof-lib/src/main/java/com/datadoghq/profiler/JavaProfiler.java @@ -167,6 +167,10 @@ public String getVersion() { } } + public String getStatus() { + return getStatus0(); + } + /** * Execute an agent-compatible profiling command - * the comma-separated list of arguments described in arguments.cpp @@ -472,4 +476,6 @@ public Map getDebugCounters() { private static native long tscFrequency0(); private static native void mallocArenaMax0(int max); + + private static native String getStatus0(); } diff --git a/ddprof-test/src/test/java/com/datadoghq/profiler/AbstractProcessProfilerTest.java b/ddprof-test/src/test/java/com/datadoghq/profiler/AbstractProcessProfilerTest.java new file mode 100644 index 000000000..c34606dec --- /dev/null +++ b/ddprof-test/src/test/java/com/datadoghq/profiler/AbstractProcessProfilerTest.java @@ -0,0 +1,94 @@ +package com.datadoghq.profiler; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; + +import static org.junit.jupiter.api.Assertions.*; + + +public abstract class AbstractProcessProfilerTest { + protected final boolean launch(String target, List jvmArgs, String commands, Function onStdoutLine, Function onStderrLine) throws Exception { + String javaHome = System.getenv("JAVA_TEST_HOME"); + if (javaHome == null) { + javaHome = System.getenv("JAVA_HOME"); + } + if (javaHome == null) { + javaHome = System.getProperty("java.home"); + } + assertNotNull(javaHome); + + List args = new ArrayList<>(); + args.add(javaHome + "/bin/java"); + args.addAll(jvmArgs); + args.add("-cp"); + args.add(System.getProperty("java.class.path")); + args.add(ExternalLauncher.class.getName()); + args.add(target); + if (commands != null && !commands.isEmpty()) { + args.add(commands); + } + + ProcessBuilder pb = new ProcessBuilder(args); + Process p = pb.start(); + Thread stdoutReader = new Thread(() -> { + Function lineProcessor = onStdoutLine != null ? onStdoutLine : l -> true; + try (BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()))) { + String line; + while ((line = br.readLine()) != null) { + System.out.println("[out] " + line); + if (!lineProcessor.apply(line)) { + try { + p.getOutputStream().write(1); + p.getOutputStream().flush(); + } catch (IOException ignored) { + } + } else { + if (line.contains("[ready]")) { + p.getOutputStream().write(1); + p.getOutputStream().flush(); + } + } + } + } catch (IOException ignored) { + } catch (Exception e) { + throw new RuntimeException(e); + } + }, "stdout-reader"); + Thread stderrReader = new Thread(() -> { + Function lineProcessor = onStderrLine != null ? onStderrLine : l -> true; + try (BufferedReader br = new BufferedReader(new InputStreamReader(p.getErrorStream()))) { + String line; + while ((line = br.readLine()) != null) { + System.out.println("[err] " + line); + if (!lineProcessor.apply(line)) { + try { + p.getOutputStream().write(1); + p.getOutputStream().flush(); + } catch (IOException ignored) { + } + } + } + } catch (IOException ignored) { + } catch (Exception e) { + throw new RuntimeException(e); + } + }, "stderr-reader"); + + stdoutReader.setDaemon(true); + stderrReader.setDaemon(true); + + stdoutReader.start(); + stderrReader.start(); + + boolean val = p.waitFor(10, TimeUnit.SECONDS); + if (!val) { + p.destroyForcibly(); + } + return val; + } +} \ No newline at end of file diff --git a/ddprof-test/src/test/java/com/datadoghq/profiler/ExternalLauncher.java b/ddprof-test/src/test/java/com/datadoghq/profiler/ExternalLauncher.java index 0a9590aa0..17bbb3490 100644 --- a/ddprof-test/src/test/java/com/datadoghq/profiler/ExternalLauncher.java +++ b/ddprof-test/src/test/java/com/datadoghq/profiler/ExternalLauncher.java @@ -2,13 +2,27 @@ public class ExternalLauncher { public static void main(String[] args) throws Exception { - if (args.length != 1) { - throw new RuntimeException(); - } - if (args[0].equals("library")) { - JVMAccess.getInstance(); - } else if (args[0].equals("profiler")) { - JavaProfiler.getInstance(); + try { + if (args.length < 1) { + throw new RuntimeException(); + } + if (args[0].equals("library")) { + JVMAccess.getInstance(); + } else if (args[0].equals("profiler")) { + JavaProfiler instance = JavaProfiler.getInstance(); + if (args.length == 2) { + String commands = args[1]; + if (!commands.isEmpty()) { + instance.execute(commands); + } + } + } + } finally { + System.out.println("[ready]"); + System.out.flush(); + System.err.flush(); } + // wait for signal to exit + System.in.read(); } } diff --git a/ddprof-test/src/test/java/com/datadoghq/profiler/JVMAccessTest.java b/ddprof-test/src/test/java/com/datadoghq/profiler/JVMAccessTest.java index c0c5f1bf9..825d2990f 100644 --- a/ddprof-test/src/test/java/com/datadoghq/profiler/JVMAccessTest.java +++ b/ddprof-test/src/test/java/com/datadoghq/profiler/JVMAccessTest.java @@ -3,14 +3,15 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import java.io.File; -import java.nio.file.Files; +import java.util.Collections; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assumptions.assumeFalse; import static org.junit.jupiter.api.Assumptions.assumeTrue; -public class JVMAccessTest { +public class JVMAccessTest extends AbstractProcessProfilerTest { @BeforeAll static void setUp() { assumeFalse(Platform.isJ9() || Platform.isZing()); // J9 and Zing do not support vmstructs @@ -21,39 +22,22 @@ void sanityInitailizationTest() throws Exception { String config = System.getProperty("ddprof_test.config"); assumeTrue("debug".equals(config)); - String javaHome = System.getenv("JAVA_TEST_HOME"); - if (javaHome == null) { - javaHome = System.getenv("JAVA_HOME"); - } - if (javaHome == null) { - javaHome = System.getProperty("java.home"); - } - assertNotNull(javaHome); - - File outFile = Files.createTempFile("jvmaccess", ".out").toFile(); - outFile.deleteOnExit(); - File errFile = Files.createTempFile("jvmaccess", ".err").toFile(); - errFile.deleteOnExit(); - - ProcessBuilder pb = new ProcessBuilder(javaHome + "/bin/java", "-cp", System.getProperty("java.class.path"), ExternalLauncher.class.getName(), "library"); - pb.redirectOutput(outFile); - pb.redirectError(errFile); - Process p = pb.start(); - int val = p.waitFor(); - assertEquals(0, val); - - boolean initLibraryFound = false; - boolean initProfilerFound = false; - for (String line : Files.readAllLines(outFile.toPath())) { - initLibraryFound |= line.contains("[TEST::INFO] VM::initLibrary"); - if (line.contains("[TEST::INFO] VM::initProfilerBridge")) { - // profiler is not expected to get initialized here; first occurrence means the test failed - initProfilerFound = true; - break; - } - } - assertTrue(initLibraryFound, "initLibrary not found"); - assertFalse(initProfilerFound, "initProfilerBridge found"); + AtomicBoolean initLibraryFound = new AtomicBoolean(false); + AtomicBoolean initProfilerFound = new AtomicBoolean(false); + + boolean rslt = launch("library", Collections.emptyList(), null, + l -> { + initLibraryFound.set(initLibraryFound.get() | l.contains("[TEST::INFO] VM::initLibrary")); + initProfilerFound.set(initProfilerFound.get() | l.contains("[TEST::INFO] VM::initProfilerBridge")); + return true; + }, + null + ); + + assertTrue(rslt); + + assertTrue(initLibraryFound.get(), "initLibrary not found"); + assertFalse(initProfilerFound.get(), "initProfilerBridge found"); } @Test @@ -68,37 +52,20 @@ void jvmVersionTest() throws Exception { javaVersion = "8.0." + javaVersion.split("u")[1]; } - String javaHome = System.getenv("JAVA_TEST_HOME"); - if (javaHome == null) { - javaHome = System.getenv("JAVA_HOME"); - } - if (javaHome == null) { - javaHome = System.getProperty("java.home"); - } - assertNotNull(javaHome); - - File outFile = Files.createTempFile("jvmaccess", ".out").toFile(); - outFile.deleteOnExit(); - File errFile = Files.createTempFile("jvmaccess", ".err").toFile(); - errFile.deleteOnExit(); - - ProcessBuilder pb = new ProcessBuilder(javaHome + "/bin/java", "-cp", System.getProperty("java.class.path"), ExternalLauncher.class.getName(), "library"); - pb.redirectOutput(outFile); - pb.redirectError(errFile); - Process p = pb.start(); - int val = p.waitFor(); - assertEquals(0, val); - - String foundVersion = null; - for (String line : Files.readAllLines(outFile.toPath())) { - System.err.println(line); - if (line.contains("[TEST::INFO] jvm_version#")) { - foundVersion = line.split("#")[1]; - break; + AtomicReference foundVersion = new AtomicReference<>(null); + + boolean rslt = launch("library", Collections.emptyList(), null, l -> { + if (l.contains("[TEST::INFO] jvm_version#")) { + foundVersion.set(l.split("#")[1]); + return false; } - } - assertNotNull(foundVersion, "java version not found in logs"); - assertEquals(javaVersion, foundVersion, "invalid java version"); + return true; + }, null); + + assertTrue(rslt); + + assertNotNull(foundVersion.get(), "java version not found in logs"); + assertEquals(javaVersion, foundVersion.get(), "invalid java version"); } @Test diff --git a/ddprof-test/src/test/java/com/datadoghq/profiler/JavaProfilerTest.java b/ddprof-test/src/test/java/com/datadoghq/profiler/JavaProfilerTest.java index e3cc32237..16ac73a0b 100644 --- a/ddprof-test/src/test/java/com/datadoghq/profiler/JavaProfilerTest.java +++ b/ddprof-test/src/test/java/com/datadoghq/profiler/JavaProfilerTest.java @@ -2,55 +2,104 @@ import org.junit.jupiter.api.Test; -import java.io.File; import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assumptions.assumeFalse; import static org.junit.jupiter.api.Assumptions.assumeTrue; -public class JavaProfilerTest { +public class JavaProfilerTest extends AbstractProcessProfilerTest { @Test void sanityInitailizationTest() throws Exception { String config = System.getProperty("ddprof_test.config"); - assumeTrue(config != null && "debug".equals(config)); + assumeTrue("debug".equals(config)); - String javaHome = System.getenv("JAVA_TEST_HOME"); - if (javaHome == null) { - javaHome = System.getenv("JAVA_HOME"); - } - if (javaHome == null) { - javaHome = System.getProperty("java.home"); - } - assertNotNull(javaHome); - - File outFile = Files.createTempFile("jvmaccess", ".out").toFile(); - outFile.deleteOnExit(); - File errFile = Files.createTempFile("jvmaccess", ".err").toFile(); - errFile.deleteOnExit(); - - ProcessBuilder pb = new ProcessBuilder(javaHome + "/bin/java", "-cp", System.getProperty("java.class.path"), ExternalLauncher.class.getName(), "profiler"); - pb.redirectOutput(outFile); - pb.redirectError(errFile); - Process p = pb.start(); - int val = p.waitFor(); - - boolean initLibraryFound = false; - boolean initProfilerFound = false; - for (String line : Files.readAllLines(outFile.toPath())) { - initLibraryFound |= line.contains("[TEST::INFO] VM::initLibrary"); - initProfilerFound |= line.contains("[TEST::INFO] VM::initProfilerBridge"); - System.out.println("[out] " + line); - } + AtomicInteger initFlag = new AtomicInteger(0); + boolean val = launch("profiler", Collections.emptyList(), "", l -> { + if (l.contains("[TEST::INFO] VM::initLibrary")) { + initFlag.set(initFlag.get() | 1); + } else if (l.contains("[TEST::INFO] VM::initProfilerBridge")) { + initFlag.set(initFlag.get() | 2); + } + // found both expected sections; can terminate the test now + return initFlag.get() != 3; + }, null); + + assertTrue(val); + } + + @Test + void testJ9DefaultSanity() throws Exception { + String config = System.getProperty("ddprof_test.config"); + assumeTrue("debug".equals(config)); + assumeFalse(Platform.isMac()); // crashy on mac + assumeTrue(Platform.isJ9()); - System.out.println(); + Path jfr = Files.createTempFile("j9", ".jfr"); + jfr.toFile().deleteOnExit(); - for (String line : Files.readAllLines(errFile.toPath())) { - System.out.println("[err] " + line); + String sampler = "jvmti"; + if (Platform.isJavaVersion(8) && Platform.isJavaVersionAtLeast(8, 0, 432)) { + sampler = "asgct"; + } else if (Platform.isJavaVersion(11) && Platform.isJavaVersionAtLeast(11, 0, 25)) { + sampler = "asgct"; + } else if (Platform.isJavaVersion(17) && Platform.isJavaVersionAtLeast(17, 0, 13)) { + sampler = "asgct"; } - assertEquals(0, val); + AtomicReference usedSampler = new AtomicReference<>(""); + AtomicBoolean hasWall = new AtomicBoolean(false); + boolean val = launch("profiler", Collections.emptyList(), "start,cpu,file=" + jfr, l -> { + if (l.contains("J9[cpu]")) { + usedSampler.set(l.split("=")[1]); + return false; + } else if (l.contains("J9[wall]")) { + hasWall.set(true); + return false; + } + return true; + }, null); + assertTrue(val); + assertEquals(sampler, usedSampler.get()); + assertFalse(hasWall.get()); + } + + @Test + void testJ9ForceJvmtiSanity() throws Exception { + String config = System.getProperty("ddprof_test.config"); + assumeTrue("debug".equals(config)); + assumeFalse(Platform.isMac()); // crashy on mac + assumeTrue(Platform.isJ9()); + + Path jfr = Files.createTempFile("j9", ".jfr"); + jfr.toFile().deleteOnExit(); + + String sampler = "jvmti"; - assertTrue(initLibraryFound, "initLibrary not found"); - assertTrue(initProfilerFound, "initProfilerBridge found"); + AtomicReference usedSampler = new AtomicReference<>(""); + AtomicBoolean hasWall = new AtomicBoolean(false); + List args = new ArrayList<>(); + args.add("-XX:+KeepJNIIDs"); + args.add("-Ddd.profiling.ddprof.j9.sampler=jvmti"); + boolean val = launch("profiler", args, "start,cpu,file=" + jfr, l -> { + if (l.contains("J9[cpu]")) { + usedSampler.set(l.split("=")[1]); + return false; + } else if (l.contains("J9[wall]")) { + hasWall.set(true); + return false; + } + return true; + }, null); + assertTrue(val); + assertEquals(sampler, usedSampler.get()); + assertFalse(hasWall.get()); } } From 7ba5aa511ed8e62886c04c1e7fe22b3694ce837e Mon Sep 17 00:00:00 2001 From: Jaroslav Bachorik Date: Tue, 18 Feb 2025 14:44:29 +0100 Subject: [PATCH 2/4] Add missing include in profiler.cpp --- ddprof-lib/src/main/cpp/profiler.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/ddprof-lib/src/main/cpp/profiler.cpp b/ddprof-lib/src/main/cpp/profiler.cpp index 4d60f3d3e..b35448c9c 100644 --- a/ddprof-lib/src/main/cpp/profiler.cpp +++ b/ddprof-lib/src/main/cpp/profiler.cpp @@ -7,6 +7,7 @@ #include "profiler.h" #include "asyncSampleMutex.h" #include "context.h" +#include "common.h" #include "counters.h" #include "ctimer.h" #include "dwarf.h" From 8c91f534986efeac376de8306dff78dc0e775729 Mon Sep 17 00:00:00 2001 From: Jaroslav Bachorik Date: Tue, 18 Feb 2025 15:06:35 +0100 Subject: [PATCH 3/4] Test only dwarf cstack on J9 --- .../test/java/com/datadoghq/profiler/junit/CStackInjector.java | 2 +- .../java/com/datadoghq/profiler/wallclock/SmokeWallTest.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/ddprof-test/src/test/java/com/datadoghq/profiler/junit/CStackInjector.java b/ddprof-test/src/test/java/com/datadoghq/profiler/junit/CStackInjector.java index 5aba5f56b..088eddee3 100644 --- a/ddprof-test/src/test/java/com/datadoghq/profiler/junit/CStackInjector.java +++ b/ddprof-test/src/test/java/com/datadoghq/profiler/junit/CStackInjector.java @@ -51,7 +51,7 @@ public Stream provideTestTemplateInvocationContex return Stream.of(new ParameterizedTestContext("no", retryCount)); } else { return Stream.of(valueSource.strings()). - filter(param -> (!Platform.isJ9() || !param.startsWith("vm"))). + filter(param -> (!Platform.isJ9() || "dwarf".equals(param))). map(param -> new ParameterizedTestContext(param, retryCount)); } } diff --git a/ddprof-test/src/test/java/com/datadoghq/profiler/wallclock/SmokeWallTest.java b/ddprof-test/src/test/java/com/datadoghq/profiler/wallclock/SmokeWallTest.java index eb0078b85..55caf858c 100644 --- a/ddprof-test/src/test/java/com/datadoghq/profiler/wallclock/SmokeWallTest.java +++ b/ddprof-test/src/test/java/com/datadoghq/profiler/wallclock/SmokeWallTest.java @@ -16,6 +16,7 @@ import java.util.concurrent.ExecutionException; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assumptions.assumeFalse; public class SmokeWallTest extends CStackAwareAbstractProfilerTest { private ProfiledCode profiledCode; From 89436cb451d6ac53ac0b780aedba6f0b4f9d6d51 Mon Sep 17 00:00:00 2001 From: Jaroslav Bachorik Date: Tue, 18 Feb 2025 17:20:51 +0100 Subject: [PATCH 4/4] Collect J9 crash logs --- .github/scripts/prepare_reports.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/scripts/prepare_reports.sh b/.github/scripts/prepare_reports.sh index fcbfc5a88..a58a4e0b2 100755 --- a/.github/scripts/prepare_reports.sh +++ b/.github/scripts/prepare_reports.sh @@ -1,8 +1,9 @@ -#!/usr/bin/env bash + #!/usr/bin/env bash set -e mkdir -p reports cp /tmp/hs_err* reports/ || true +cp ddprof-test/javacore*.txt reports/ || true cp ddprof-test/build/hs_err* reports/ || true cp -r ddprof-lib/build/tmp reports/native_build || true cp -r ddprof-test/build/reports/tests reports/tests || true