diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 9cf90cc9a..8b5cbfdfb 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -38,11 +38,11 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -55,4 +55,4 @@ jobs: ./gradlew -x test build - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/continuous.yml b/.github/workflows/continuous.yml index 3602adcba..8b177a1cf 100644 --- a/.github/workflows/continuous.yml +++ b/.github/workflows/continuous.yml @@ -19,20 +19,21 @@ jobs: runs-on: ubuntu-latest steps: - name: Set up Java - uses: actions/setup-java@v1 + uses: actions/setup-java@v4 with: java-version: 11 + distribution: temurin - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Setup Gradle - uses: gradle/gradle-build-action@v2 + uses: gradle/actions/setup-gradle@v4 - name: Build run: ./gradlew build - name: Upload build data if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: build retention-days: 1 @@ -40,7 +41,7 @@ jobs: btrace-dist/build - name: Archive test reports if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: test-reports path: | @@ -52,20 +53,21 @@ jobs: timeout-minutes: 10 strategy: matrix: - java: [ 8, 11, 17, 20 ] + java: [ 8, 11, 17, 21 ] steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Setup Gradle - uses: gradle/gradle-build-action@v2 + uses: gradle/actions/setup-gradle@v4 - name: Set up Java ${{ matrix.java }} - uses: actions/setup-java@v1 + uses: actions/setup-java@v4 with: java-version: ${{ matrix.java }} + distribution: temurin - name: Download build data - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: build path: btrace-dist/build @@ -74,15 +76,15 @@ jobs: ./gradlew -Pintegration :integration-tests:test - name: Integration test reports if: always() - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: - name: integration-test-reports + name: integration-test-reports-${{ matrix.java }} path: | integration-tests/build/reports/**/* - name: Archive binary artifacts - uses: actions/upload-artifact@v3 + if: success() && matrix.java == '11' + uses: actions/upload-artifact@v4 with: - if: success() name: btrace-dist path: | btrace-dist/build/distributions/**/btrace-*-bin*.tar.gz @@ -93,21 +95,22 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Setup Gradle - uses: gradle/gradle-build-action@v2 + uses: gradle/actions/setup-gradle@v4 - name: Set up Java - uses: actions/setup-java@v1 + uses: actions/setup-java@v4 with: java-version: 11 + distribution: temurin - name: Download build data - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: build - name: Deploy Maven - run: JAVA_HOME="$JAVA_11_HOME" ./gradlew -x test :btrace-dist:publish + run: ./gradlew -x test :btrace-dist:publish env: GPG_SIGNING_KEY: ${{ secrets.GPG_SIGNING_KEY }} GPG_SIGNING_PWD: ${{ secrets.GPG_SIGNING_PWD }} @@ -119,6 +122,6 @@ jobs: needs: publish steps: - name: Cleanup temporary artifacts - uses: geekyeggo/delete-artifact@v2 + uses: geekyeggo/delete-artifact@v5 with: name: build diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bf0a5be40..eed1b2655 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Branch name id: branch_name run: | @@ -23,14 +23,15 @@ jobs: echo ::set-output name=SOURCE_BRANCH::${GITHUB_REF#refs/heads/} echo ::set-output name=SOURCE_TAG::${GITHUB_REF#refs/tags/} - name: Set up Java - uses: actions/setup-java@v1 + uses: actions/setup-java@v4 with: java-version: 11 + distribution: temurin - name: Build artifacts run: ./gradlew :btrace-dist:build - name: Create Release id: create_release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -39,4 +40,20 @@ jobs: files: | btrace-dist/build/distributions/btrace-${{ steps.branch_name.outputs.SOURCE_TAG }}-bin.tar.gz btrace-dist/build/distributions/btrace-${{ steps.branch_name.outputs.SOURCE_TAG }}-bin.zip - btrace-dist/build/distributions/btrace-${{ steps.branch_name.outputs.SOURCE_TAG }}-sdkman-bin.zip \ No newline at end of file + btrace-dist/build/distributions/btrace-${{ steps.branch_name.outputs.SOURCE_TAG }}-sdkman-bin.zip + - name: Deploy Maven + id: deploy_maven + env: + GPG_SIGNING_KEY: ${{ secrets.GPG_SIGNING_KEY }} + GPG_SIGNING_PWD: ${{ secrets.GPG_SIGNING_PWD }} + BTRACE_SONATYPE_USER: ${{ secrets.BTRACE_SONATYPE_USER }} + BTRACE_SONATYPE_PWD: ${{ secrets.BTRACE_SONATYPE_PWD }} + run: | + ./gradlew :btrace-dist:publishAllPublicationsToMavenRepository + - name: Update SDKMan! + id: update_sdkman + env: + SKDMAN_API_KEY: ${{ secrets.SDKMAN_KEY }} + SDKMAN_API_TOKEN: ${{ secrets.SDKMAN_TOKEN }} + run: | + ./gradlew :btrace-dist:sdkMinorRelease diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 213b6cb1f..4a53aae70 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/stale@v7 + - uses: actions/stale@v9 with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-issue-message: 'Stale issue message' diff --git a/.gitignore b/.gitignore index bdbe80366..27358af05 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,9 @@ *.war *.ear +# Un-ignore specific files +!btrace-instr/src/test/resources/packed/test-pack.jar + # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* diff --git a/README.md b/README.md index 970bf0b5c..530c5b224 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ A safe, dynamic tracing tool for the Java platform ## Version -2.2.3 +2.2.6 ## Quick Summary BTrace is a safe, dynamic tracing tool for the Java platform. diff --git a/btrace-agent/src/main/java/org/openjdk/btrace/agent/Main.java b/btrace-agent/src/main/java/org/openjdk/btrace/agent/Main.java index 9c0b4a02b..322e8c721 100644 --- a/btrace-agent/src/main/java/org/openjdk/btrace/agent/Main.java +++ b/btrace-agent/src/main/java/org/openjdk/btrace/agent/Main.java @@ -169,6 +169,8 @@ private static synchronized void main(String args, Instrumentation inst) { BTraceRuntime.leave(); } }); + // set the fall-back instrumentation object to BTraceRuntime + BTraceRuntime.instrumentation = inst; // force back-registration of BTraceRuntimeImpl in BTraceRuntime BTraceRuntimes.getDefault(); // init BTraceRuntime @@ -398,8 +400,8 @@ private static void parseArgs() { String libs = argMap.get(LIBS); String config = argMap.get(CONFIG); - loadDefaultArguments(config); processClasspaths(libs); + loadDefaultArguments(config); p = argMap.get(DEBUG); settings.setDebug(p != null && !"false".equals(p)); diff --git a/btrace-client/build.gradle b/btrace-client/build.gradle index 76871f1c3..7457e77ac 100644 --- a/btrace-client/build.gradle +++ b/btrace-client/build.gradle @@ -1,3 +1,7 @@ +buildscript { scriptHandler -> + apply from: rootProject.file('buildSrc/shared.gradle'), to: scriptHandler +} + plugins { alias(libs.plugins.versioning) } diff --git a/btrace-client/src/main/java/org/openjdk/btrace/client/Main.java b/btrace-client/src/main/java/org/openjdk/btrace/client/Main.java index 34f32ab54..380e94e34 100644 --- a/btrace-client/src/main/java/org/openjdk/btrace/client/Main.java +++ b/btrace-client/src/main/java/org/openjdk/btrace/client/Main.java @@ -29,6 +29,10 @@ import java.io.File; import java.io.IOException; import java.io.PrintWriter; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Properties; + import org.openjdk.btrace.core.DebugSupport; import org.openjdk.btrace.core.Messages; import org.openjdk.btrace.core.comm.Command; @@ -83,10 +87,22 @@ public final class Main { if (log.isDebugEnabled()) log.debug("dumpDir is {}", DUMP_DIR); } PROBE_DESC_PATH = System.getProperty("com.sun.btrace.probeDescPath", "."); - con = System.console(); + String javaVersion = getJavaVersion(); + // In Java 22 the console will write standard output to stderr :shrug: + con = javaVersion == null || !javaVersion.startsWith("22") ? System.console() : null; out = getOutWriter(con); } + private static String getJavaVersion() { + Properties props = new Properties(); + try { + props.load(Files.newInputStream(Paths.get(System.getenv("JAVA_HOME"), "release"))); + return props.getProperty("JAVA_VERSION").replace("\"", ""); + } catch (IOException ignored) { + return null; + } + } + @SuppressWarnings("DefaultCharset") private static PrintWriter getOutWriter(Console con) { return (con != null) ? con.writer() : new PrintWriter(System.out); diff --git a/btrace-compiler/src/main/java/org/openjdk/btrace/compiler/Postprocessor.java b/btrace-compiler/src/main/java/org/openjdk/btrace/compiler/Postprocessor.java index 111d7a36d..a8979e03c 100644 --- a/btrace-compiler/src/main/java/org/openjdk/btrace/compiler/Postprocessor.java +++ b/btrace-compiler/src/main/java/org/openjdk/btrace/compiler/Postprocessor.java @@ -271,7 +271,7 @@ public void visitVarInsn(int opcode, int var) { case Opcodes.DLOAD: { simulatedStack.push(Boolean.FALSE); - // long and double occoupy 2 stack slots; fall through + // long and double occupy 2 stack slots; fall through } case Opcodes.ILOAD: case Opcodes.FLOAD: @@ -283,7 +283,7 @@ public void visitVarInsn(int opcode, int var) { case Opcodes.DSTORE: { simulatedStack.poll(); - // long and double occoupy 2 stack slots; fall through + // long and double occupy 2 stack slots; fall through } case Opcodes.ASTORE: case Opcodes.ISTORE: diff --git a/btrace-compiler/src/main/java/org/openjdk/btrace/compiler/Verifier.java b/btrace-compiler/src/main/java/org/openjdk/btrace/compiler/Verifier.java index 89c6e8724..0bd2c0510 100644 --- a/btrace-compiler/src/main/java/org/openjdk/btrace/compiler/Verifier.java +++ b/btrace-compiler/src/main/java/org/openjdk/btrace/compiler/Verifier.java @@ -65,7 +65,7 @@ * @author A. Sundararajan */ @SupportedAnnotationTypes("*") -@SupportedSourceVersion(SourceVersion.RELEASE_7) +@SupportedSourceVersion(SourceVersion.RELEASE_8) public class Verifier extends AbstractProcessor implements TaskListener { private final List classNames = new ArrayList<>(); private final List compUnits = new ArrayList<>(); diff --git a/btrace-compiler/src/test/java/org/openjdk/btrace/compiler/JfrEventsTest.java b/btrace-compiler/src/test/java/org/openjdk/btrace/compiler/JfrEventsTest.java index 7c8500466..123e2277c 100644 --- a/btrace-compiler/src/test/java/org/openjdk/btrace/compiler/JfrEventsTest.java +++ b/btrace-compiler/src/test/java/org/openjdk/btrace/compiler/JfrEventsTest.java @@ -2,6 +2,7 @@ import java.io.File; import java.io.PrintWriter; +import java.io.StringWriter; import java.net.URL; import java.util.Map; import org.junit.jupiter.api.Test; @@ -11,6 +12,8 @@ import org.openjdk.btrace.instr.BTraceProbe; import org.openjdk.btrace.instr.BTraceProbeFactory; +import static org.junit.jupiter.api.Assertions.fail; + public class JfrEventsTest { @Test public void testCompile() throws Exception { @@ -26,9 +29,18 @@ public void testCompile() throws Exception { BTraceProbeFactory factory = new BTraceProbeFactory(SharedSettings.GLOBAL); for (byte[] bytes : data.values()) { BTraceProbe probe = factory.createProbe(bytes); - byte[] code = probe.getDataHolderBytecode(); - CheckClassAdapter.verify(new ClassReader(code), true, new PrintWriter(System.err)); + verifyCode(probe.getFullBytecode()); + verifyCode(probe.getDataHolderBytecode()); + } + } + + private void verifyCode(byte[] code) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + CheckClassAdapter.verify(new ClassReader(code), true, pw); + if (sw.toString().contains("AnalyzerException")) { + System.err.println(sw); + fail(); } - System.out.println(data); } } diff --git a/btrace-compiler/src/test/java/org/openjdk/btrace/compiler/TypeErasureTest.java b/btrace-compiler/src/test/java/org/openjdk/btrace/compiler/TypeErasureTest.java new file mode 100644 index 000000000..3951345bc --- /dev/null +++ b/btrace-compiler/src/test/java/org/openjdk/btrace/compiler/TypeErasureTest.java @@ -0,0 +1,47 @@ +package org.openjdk.btrace.compiler; + +import org.junit.jupiter.api.Test; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.util.CheckClassAdapter; +import org.openjdk.btrace.core.SharedSettings; +import org.openjdk.btrace.instr.BTraceProbe; +import org.openjdk.btrace.instr.BTraceProbeFactory; + +import java.io.File; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.net.URL; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.fail; + +public class TypeErasureTest { + @Test + void testTypeErasure() throws Exception { + URL input = TypeErasureTest.class.getResource("/HistoProbe.java"); + File inputFile = new File(input.toURI()); + Map data = + new Compiler(true) + .compile( + inputFile, + new PrintWriter(System.err), + null, + System.getProperty("java.class.path")); + BTraceProbeFactory factory = new BTraceProbeFactory(SharedSettings.GLOBAL); + for (byte[] bytes : data.values()) { + BTraceProbe probe = factory.createProbe(bytes); + verifyCode(probe.getFullBytecode()); + verifyCode(probe.getDataHolderBytecode()); + } + } + + private void verifyCode(byte[] code) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + CheckClassAdapter.verify(new ClassReader(code), true, pw); + if (sw.toString().contains("AnalyzerException")) { + System.err.println(sw); + fail(); + } + } +} diff --git a/btrace-compiler/src/test/resources/HistoProbe.java b/btrace-compiler/src/test/resources/HistoProbe.java new file mode 100644 index 000000000..8d5df886e --- /dev/null +++ b/btrace-compiler/src/test/resources/HistoProbe.java @@ -0,0 +1,27 @@ +package test; + +import static org.openjdk.btrace.core.BTraceUtils.*; +import org.openjdk.btrace.core.annotations.*; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; + +@BTrace public class HistoProbe { + private static Map histo = newHashMap(); + @OnMethod(clazz = "javax.swing.JComponent", method = "") + public static void onMethod(@Self Object obj) { + String cn = name(classOf(obj)); + AtomicInteger ai = get((Map)histo, cn); + if (ai == null) { + ai = newAtomicInteger(1); + put(histo, cn, ai); + } else { + incrementAndGet(ai); // WORKS if commented out + } + } + + @OnTimer(1000) + public static void print() { + printNumberMap("Component Histogram", histo); + } +} \ No newline at end of file diff --git a/btrace-core/build.gradle b/btrace-core/build.gradle index afb509eff..08aa6c467 100644 --- a/btrace-core/build.gradle +++ b/btrace-core/build.gradle @@ -1,5 +1,9 @@ import org.apache.tools.ant.filters.* +buildscript { scriptHandler -> + apply from: rootProject.file('buildSrc/shared.gradle'), to: scriptHandler +} + plugins { alias(libs.plugins.versioning) } diff --git a/btrace-core/src/main/java/org/openjdk/btrace/core/BTraceRuntime.java b/btrace-core/src/main/java/org/openjdk/btrace/core/BTraceRuntime.java index 52c574c96..d7894d7cb 100644 --- a/btrace-core/src/main/java/org/openjdk/btrace/core/BTraceRuntime.java +++ b/btrace-core/src/main/java/org/openjdk/btrace/core/BTraceRuntime.java @@ -30,6 +30,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; +import java.lang.instrument.Instrumentation; import java.lang.management.GarbageCollectorMXBean; import java.lang.management.LockInfo; import java.lang.management.MemoryMXBean; @@ -97,6 +98,8 @@ public final class BTraceRuntime { private static volatile BTraceRuntimeAccessor rtAccessor = () -> null; private static final String INDENT = " "; + public static volatile Instrumentation instrumentation = null; + static { LINE_SEPARATOR = System.getProperty("line.separator"); } @@ -1048,6 +1051,10 @@ public static JfrEvent.Factory createEventFactory(JfrEvent.Template template) { return getRt().createEventFactory(template); } + public static boolean isBootstrapClass(String className) { + return getRt().isBootstrapClass(className); + } + // BTrace aggregation support static Aggregation newAggregation(AggregationFunction type) { return new Aggregation(type); @@ -1282,6 +1289,8 @@ public interface Impl { JfrEvent.Factory createEventFactory(JfrEvent.Template template); int version(); + + boolean isBootstrapClass(String className); } public interface BTraceRuntimeAccessor { diff --git a/btrace-dist/build.gradle b/btrace-dist/build.gradle index 9f282985e..c7e7cc1d6 100644 --- a/btrace-dist/build.gradle +++ b/btrace-dist/build.gradle @@ -18,6 +18,76 @@ def distBase = "${project.buildDir}/resources/main" def distTarget = "${distBase}/v${project.version}" def libsDir = new File("${distTarget}/libs") +def includes = [:] + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(8)) + } +} + +includes['agent'] = { + if (it.directory) { + return true + } + if (it.path.endsWith('.jar')) { + return true + } + return it.path.startsWith('org/jctools/') || + it.path.startsWith('org/openjdk/btrace/agent/') || + it.path.startsWith('org/openjdk/btrace/instr/') || + // include the messages resource bundle and class in the agent jar (bootstrap can't load resources) + it.path == 'org/openjdk/btrace/core/Messages.class' || + it.path == 'org/openjdk/btrace/core/messages.properties' +} + +includes['boot'] = { + if (it.directory) { + return true + } + if (it.path.endsWith('.jar')) { + return true + } + if (it.path.startsWith('org/openjdk/btrace/core/')) { + if (it.path == 'org/openjdk/btrace/core/Messages.class' || it.path == 'org/openjdk/btrace/core/messages.properties') { + // messages resource bundle&class are hoisted to agent and client jars + return false + } + return true + } + if (it.path.startsWith('org/objectweb/asm/')) { + if (it.path.startsWith('org/objectweb/asm/commons/') || + it.path.startsWith('org/objectweb/asm/util/') || + it.path.startsWith('org/objectweb/asm/xml/')) { + return false + } + return true + } + return it.path.startsWith("org/slf4j/") || it.path.startsWith('org/openjdk/btrace/runtime/') || + it.path.startsWith('org/jctools/') || + it.path.startsWith('org/openjdk/btrace/services/') || + it.path.startsWith('org/openjdk/btrace/statsd/') +} + +includes['client'] = { + if (it.directory) { + return true + } + if (it.path.endsWith('.jar')) { + return true + } + if (it.path.startsWith('org/openjdk/btrace')) { + return !it.path.startsWith('org/openjdk/btrace/agent') + } + if (it.path.startsWith('META-INF/services')) { + return !it.path.contains('com.sun.') && !it.path.contains('javax.annotation.') + } + return it.path.startsWith("org/slf4j") || + it.path.startsWith('org/objectweb/asm') || + it.path.startsWith('org/jctools') +} + + configurations { artifact.extendsFrom implementation } @@ -62,22 +132,10 @@ task agentJar(type: ShadowJar) { ) } - include { - if (it.directory) { - return true - } - if (it.path.endsWith('.jar')) { - return true - } - return it.path.startsWith('org/openjdk/btrace/agent/') || - it.path.startsWith('org/openjdk/btrace/instr/') || - // include the messages resource bundle and class in the agent jar (bootstrap can't load resources) - it.path == 'org/openjdk/btrace/core/Messages.class' || - it.path == 'org/openjdk/btrace/core/messages.properties' - } + include includes['agent'] configurations = [project.configurations.artifact] - relocate 'org.jctools', 'org.openjdk.btrace.libs.org.jctools' + relocate 'org.jctools', 'org.openjdk.btrace.libs.agent.org.jctools' relocate 'org.objectweb.asm', 'org.openjdk.btrace.libs.org.objectweb.asm' relocate 'org.slf4j', 'org.openjdk.btrace.libs.org.slf4j' } @@ -90,41 +148,10 @@ task bootJar(type: ShadowJar) { destinationDirectory = libsDir - include { - if (it.directory) { - return true - } - if (it.path.endsWith('.jar')) { - return true - } - if (it.path.startsWith('org/openjdk/btrace/core/')) { - if (it.path == 'org/openjdk/btrace/core/Messages.class' || it.path == 'org/openjdk/btrace/core/messages.properties') { - // messages resource bundle&class are hoisted to agent and client jars - return false - } - return true - } - if (it.path.startsWith('org/jctools/')) { - if (it.path.startsWith('org/jctools/map/')) { - return false - } - return true - } - if (it.path.startsWith('org/objectweb/asm/')) { - if (it.path.startsWith('org/objectweb/asm/commons/') || - it.path.startsWith('org/objectweb/asm/util/') || - it.path.startsWith('org/objectweb/asm/xml/')) { - return false - } - return true - } - return it.path.startsWith("org/slf4j/") || it.path.startsWith('org/openjdk/btrace/runtime/') || - it.path.startsWith('org/openjdk/btrace/services/') || - it.path.startsWith('org/openjdk/btrace/statsd/') - } + include includes['boot'] configurations = [project.configurations.artifact] - relocate 'org.jctools', 'org.openjdk.btrace.libs.org.jctools' + relocate 'org.jctools', 'org.openjdk.btrace.libs.boot.org.jctools' relocate 'org.objectweb.asm', 'org.openjdk.btrace.libs.org.objectweb.asm' relocate 'org.slf4j', 'org.openjdk.btrace.libs.org.slf4j' } @@ -136,35 +163,12 @@ task clientJar(type: ShadowJar) { archiveClassifier.set('') destinationDirectory = libsDir - exclude 'afu/**' - exclude 'javax/**' - exclude 'com/**' - exclude 'sun/**' - exclude 'org/relaxng/**' - exclude 'org/checkerframework/**' - exclude 'org/codehaus/**' - - exclude { - it.path.startsWith('org/jctools/maps/') && - !it.path.startsWith("org/jctools/maps/NonBlockingIdentityHashMap") && - !it.path.startsWith("org/jctools/maps/NonBlockingHashMap") - } - exclude { - // 'org/jctools/util/**' - it.path.startsWith('org/jctools/util/') && - !it.path.startsWith("org/jctools/util/UnsafeAccess") - } - exclude 'META-INF/services/com.sun.*' - exclude 'META-INF/services/javax.annotation.*' - - exclude 'org/objectweb/asm/xml/**' - - exclude 'org/openjdk/btrace/agent/**' + include includes['client'] configurations = [project.configurations.artifact] - relocate 'org.jctools', 'org.openjdk.btrace.libs.org.jctools' - relocate 'org.objectweb.asm', 'org.openjdk.btrace.libs.org.objectweb.asm' - relocate 'org.slf4j', 'org.openjdk.btrace.libs.org.slf4j' + relocate 'org.jctools', 'org.openjdk.btrace.libs.client.org.jctools' + relocate 'org.objectweb.asm', 'org.openjdk.btrace.libs.client.org.objectweb.asm' + relocate 'org.slf4j', 'org.openjdk.btrace.libs.client.org.slf4j' } task fixPermissions(type: Exec) { @@ -264,28 +268,84 @@ test { } ['agent', 'boot', 'client'].each { name -> - tasks.create(name: "${name}SourcesJar", type: Jar) { + // Define a temporary directory for the sources + def tempDir = new File(buildDir, "tmp/${name}Sources") + + // first, we will copy the classes and make sure there are no empty directories + tasks.create(name: "${name}CopySources", type: Copy) { + doFirst { + tempDir.deleteDir() // Ensure the directory is clean + } + + into tempDir + + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + gradle.projectsEvaluated { + rootProject.childProjects.each { projectName, siblingProject -> + // Skip the current project to avoid duplication + if (siblingProject != project) { + from(siblingProject.sourceSets.main.allSource) { + include includes["${name}"] + exclude 'META-INF/**' + } + } + } + } + + doLast { + // Recursively delete empty directories + def deleteEmptyDirs + deleteEmptyDirs = { dir -> + dir.eachDir { subDir -> + deleteEmptyDirs(subDir) + if (!subDir.listFiles().any()) { + subDir.deleteDir() + } + } + } + deleteEmptyDirs(tempDir) + } + } + + // we will use the output of the previous task to crate the JAR + tasks.create(name: "${name}SourcesJar", type: Jar, dependsOn: "${name}CopySources") { group 'Documentation' description "Build the btrace-${name} sources jar." archiveAppendix = "${name}" archiveClassifier = "sources" - from sourceSets.main.allSource - exclude excludes["${name}"] - } + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + from tempDir + } tasks.create(name: "${name}Javadoc", type: Javadoc) { group 'Documentation' description "Generates Javadoc API documentation for the btrace-${name}." title = "btrace-${name}" - source = sourceSets.main.allJava destinationDir = file("${project.docsDir}/${name}/javadoc") classpath = files(compileJava.destinationDirectory) + configurations.artifact.asFileTree - exclude excludes["${name}"] options.addStringOption('Xdoclint:all,-missing', '-quiet') failOnError false + + project.parent.subprojects.each { subproject -> + // Skip the current project to avoid duplication + if (subproject != project) { + // Add source from sibling projects based on filter + source subproject.sourceSets.main.java.matching { + include includes["${name}"] + } + } + } + + // Include classpath of all sibling projects + project.parent.subprojects.each { subproject -> + // Skip the current project to avoid duplication + if (subproject != project) { + classpath += subproject.sourceSets.main.compileClasspath + } + } } @@ -300,8 +360,8 @@ test { } sdkman { - consumerKey = project.hasProperty("sdkman.key") ? project.property("sdkman.key") : System.getenv('SDKMAN_API_KEY') - consumerToken = project.hasProperty("sdkman.token") ? project.property("sdkman.token") : System.getenv('SDKMAN_API_TOKEN') + consumerKey = project.hasProperty("sdkman.key") ? project.property("sdkman.key") : System.getenv('SDKMAN_KEY') + consumerToken = project.hasProperty("sdkman.token") ? project.property("sdkman.token") : System.getenv('SDKMAN_TOKEN') candidate = "btrace" version = "${project.version}" url = "https://github.com/btraceio/btrace/releases/download/v${project.version}/btrace-v${project.version}-sdkman-bin.zip" diff --git a/btrace-instr/build.gradle b/btrace-instr/build.gradle index 6ad492bcf..f27997413 100644 --- a/btrace-instr/build.gradle +++ b/btrace-instr/build.gradle @@ -65,6 +65,6 @@ test { if (props.getProperty("JAVA_VERSION")?.contains("1.8")) { jvmArgs "-Dproject.version=${project.version}" } else { - jvmArgs '-XX:+IgnoreUnrecognizedVMOptions', '--add-opens', 'java.base/jdk.internal.reflect=ALL-UNNAMED', '--add-exports', 'java.base/jdk.internal.reflect=ALL-UNNAMED', "-Dproject.version=${project.version}" + jvmArgs '-XX:+IgnoreUnrecognizedVMOptions', '--add-opens', 'java.base/jdk.internal.reflect=ALL-UNNAMED', '--add-opens', 'java.base/java.lang=ALL-UNNAMED', '--add-exports', 'java.base/jdk.internal.reflect=ALL-UNNAMED', "-Dproject.version=${project.version}" } } \ No newline at end of file diff --git a/btrace-instr/src/main/java/org/openjdk/btrace/instr/BTraceProbeFactory.java b/btrace-instr/src/main/java/org/openjdk/btrace/instr/BTraceProbeFactory.java index eac4e7dcb..db2286046 100644 --- a/btrace-instr/src/main/java/org/openjdk/btrace/instr/BTraceProbeFactory.java +++ b/btrace-instr/src/main/java/org/openjdk/btrace/instr/BTraceProbeFactory.java @@ -49,6 +49,7 @@ import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import org.openjdk.btrace.core.ArgsMap; @@ -83,16 +84,46 @@ private static void applyArgs(BTraceProbe bp, ArgsMap argsMap) { * and BTrace probe pack are supported. * * @param filePath the file path - * @return {@literal true} if a BTrace probe can be recornstructed from data in the given file + * @return {@literal true} if a BTrace probe can be reconstructed from data in the given file */ public static boolean canLoad(String filePath) { + return canLoad(filePath, null); + } + + public static boolean canLoad(String filePath, ClassLoader cl) { if (filePath == null) { return false; } - try (DataInputStream dis = new DataInputStream(Files.newInputStream(Paths.get(filePath)))) { - int magic = dis.readInt(); - return magic == CLASS_MAGIC || magic == BTraceProbePersisted.MAGIC; + Path path = Paths.get(filePath); + InputStream is = null; + try { + if (!Files.exists(path)) { + // try to load from the classpath + if (cl == null) { + cl = ClassLoader.getSystemClassLoader(); + } + is = cl.getResourceAsStream("META-INF/btrace/" + filePath); + } else { + is = Files.newInputStream(path); + } + if (is != null) { + try { + try (DataInputStream dis = new DataInputStream(is)) { + int magic = dis.readInt(); + return magic == CLASS_MAGIC || magic == BTraceProbePersisted.MAGIC; + } + } catch (IOException ignored) { + is = null; + } + } } catch (IOException ignored) { + } finally { + if (is != null) { + try { + is.close(); + } catch (IOException ignored) { + } + } } return false; } diff --git a/btrace-instr/src/main/java/org/openjdk/btrace/instr/BTraceProbeSupport.java b/btrace-instr/src/main/java/org/openjdk/btrace/instr/BTraceProbeSupport.java index b4a436369..6e5190e64 100644 --- a/btrace-instr/src/main/java/org/openjdk/btrace/instr/BTraceProbeSupport.java +++ b/btrace-instr/src/main/java/org/openjdk/btrace/instr/BTraceProbeSupport.java @@ -82,7 +82,7 @@ String translateOwner(String owner) { } boolean isTransforming() { - return onMethods.size() > 0; + return !onMethods.isEmpty(); } Collection getApplicableHandlers(BTraceClassReader cr) { diff --git a/btrace-instr/src/main/java/org/openjdk/btrace/instr/ClassInfo.java b/btrace-instr/src/main/java/org/openjdk/btrace/instr/ClassInfo.java index 3c7213b68..de3ad1eb1 100644 --- a/btrace-instr/src/main/java/org/openjdk/btrace/instr/ClassInfo.java +++ b/btrace-instr/src/main/java/org/openjdk/btrace/instr/ClassInfo.java @@ -24,16 +24,21 @@ */ package org.openjdk.btrace.instr; +import org.openjdk.btrace.core.BTraceRuntime; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.IOException; import java.io.InputStream; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.LinkedHashSet; import java.util.Objects; import java.util.Set; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * Arbitrary class info type allowing access to supertype information also for not-already-loaded @@ -45,7 +50,7 @@ public final class ClassInfo { private static final Logger log = LoggerFactory.getLogger(ClassInfo.class); private static final ClassLoader SYS_CL = ClassLoader.getSystemClassLoader(); - private static volatile Method BSTRP_CHECK_MTD; + private static volatile MethodHandle BSTRP_CHECK_MTD; private final String cLoaderId; private final ClassName classId; @@ -109,31 +114,9 @@ private static ClassLoader inferClassLoader(ClassLoader initiating, ClassName cl } } - private static boolean isBootstrap(String className) { - try { - Method m = getCheckBootstrap(); - if (m != null) { - return m.invoke(SYS_CL, className) != null; - } - } catch (Throwable t) { - log.warn("Unable to check for class {} being loaded by bootstrap classloader", className, t); - } - return false; - } - - private static Method getCheckBootstrap() { - if (BSTRP_CHECK_MTD != null) { - return BSTRP_CHECK_MTD; - } - Method m = null; - try { - m = ClassLoader.class.getDeclaredMethod("findBootstrapClassOrNull", String.class); - m.setAccessible(true); - } catch (Throwable t) { - log.warn("Can not resolve method 'findBootstrapClassOrNull'", t); - } - BSTRP_CHECK_MTD = m; - return BSTRP_CHECK_MTD; + // package private only for testing purposes + static boolean isBootstrap(String className) { + return BTraceRuntime.isBootstrapClass(className); } /** diff --git a/btrace-instr/src/main/java/org/openjdk/btrace/instr/HandlerRepositoryImpl.java b/btrace-instr/src/main/java/org/openjdk/btrace/instr/HandlerRepositoryImpl.java index d1ead3d80..8543ed8ce 100644 --- a/btrace-instr/src/main/java/org/openjdk/btrace/instr/HandlerRepositoryImpl.java +++ b/btrace-instr/src/main/java/org/openjdk/btrace/instr/HandlerRepositoryImpl.java @@ -1,11 +1,5 @@ package org.openjdk.btrace.instr; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.nio.file.StandardOpenOption; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.openjdk.btrace.core.DebugSupport; @@ -14,6 +8,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + public final class HandlerRepositoryImpl { private static final Logger log = LoggerFactory.getLogger(HandlerRepositoryImpl.class); @@ -45,7 +46,7 @@ public static byte[] getProbeHandler( BTraceProbe probe = probeMap.get(probeName); ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); - String handlerClassName = callerName.replace('.', '/'); + String handlerClassName = callerName.replace('.', '/') + "$" + probeName.replace('/', '_'); ClassVisitor visitor = new CopyingVisitor(handlerClassName, true, writer) { @Override diff --git a/btrace-instr/src/main/java/org/openjdk/btrace/instr/Instrumentor.java b/btrace-instr/src/main/java/org/openjdk/btrace/instr/Instrumentor.java index cfb13e82b..5bb713d33 100644 --- a/btrace-instr/src/main/java/org/openjdk/btrace/instr/Instrumentor.java +++ b/btrace-instr/src/main/java/org/openjdk/btrace/instr/Instrumentor.java @@ -41,6 +41,7 @@ import org.objectweb.asm.Handle; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.openjdk.btrace.core.BTraceRuntime; import org.openjdk.btrace.core.MethodID; @@ -114,7 +115,7 @@ public void visit( String[] interfaces) { className = name; this.superName = superName; - super.visit(version, access, name, signature, superName, interfaces); + super.visit(Math.max(Opcodes.V1_7, version), access, name, signature, superName, interfaces); } @Override @@ -966,8 +967,8 @@ && matches(targetFieldName, name)) { getMethodOrFieldName( om.isTargetMethodOrFieldFqn(), opcode, - targetClassName, - targetFieldName, + owner, + name, desc)), constArg(om.getClassNameParameter(), className.replace('/', '.')), constArg(om.getMethodParameter(), getName(om.isMethodFqn())), @@ -1015,8 +1016,8 @@ && matches(targetFieldName, name)) { getMethodOrFieldName( om.isTargetMethodOrFieldFqn(), opcode, - targetClassName, - targetFieldName, + owner, + name, desc)), localVarArg(om.getReturnParameter(), fldType, returnValIndex), constArg(om.getClassNameParameter(), className.replace('/', '.')), @@ -1085,8 +1086,8 @@ && matches(targetFieldName, name)) { getMethodOrFieldName( om.isTargetMethodOrFieldFqn(), opcode, - targetClassName, - targetFieldName, + owner, + name, desc)), constArg(om.getClassNameParameter(), className.replace('/', '.')), constArg(om.getMethodParameter(), getName(om.isMethodFqn())), @@ -1129,8 +1130,8 @@ && matches(targetFieldName, name)) { getMethodOrFieldName( om.isTargetMethodOrFieldFqn(), opcode, - targetClassName, - targetFieldName, + owner, + name, desc)), constArg(om.getClassNameParameter(), className.replace('/', '.')), constArg(om.getMethodParameter(), getName(om.isMethodFqn())), diff --git a/btrace-instr/src/main/java/org/openjdk/btrace/instr/MethodVerifier.java b/btrace-instr/src/main/java/org/openjdk/btrace/instr/MethodVerifier.java index f7cbdec63..b75d4b01f 100644 --- a/btrace-instr/src/main/java/org/openjdk/btrace/instr/MethodVerifier.java +++ b/btrace-instr/src/main/java/org/openjdk/btrace/instr/MethodVerifier.java @@ -261,6 +261,7 @@ public void visitTypeInsn(int opcode, String desc) { } Verifier.reportError("no.new.object", desc); } + super.visitTypeInsn(opcode, desc); } @Override diff --git a/btrace-instr/src/test/btrace/onmethod/FieldGetAfter.java b/btrace-instr/src/test/btrace/onmethod/FieldGetAfter.java index 595d8ad82..d8d7155d5 100644 --- a/btrace-instr/src/test/btrace/onmethod/FieldGetAfter.java +++ b/btrace-instr/src/test/btrace/onmethod/FieldGetAfter.java @@ -46,8 +46,8 @@ public class FieldGetAfter { location = @Location( value = Kind.FIELD_GET, - clazz = "resources.OnMethodTest", - field = "field", + clazz = "/^resources.OnMethodTest$/", + field = "/^field$/", where = Where.AFTER)) public static void args( @Self Object self, diff --git a/btrace-instr/src/test/btrace/onmethod/FieldGetAfterStatic.java b/btrace-instr/src/test/btrace/onmethod/FieldGetAfterStatic.java index dba3bc84f..c548c9a74 100644 --- a/btrace-instr/src/test/btrace/onmethod/FieldGetAfterStatic.java +++ b/btrace-instr/src/test/btrace/onmethod/FieldGetAfterStatic.java @@ -46,8 +46,8 @@ public class FieldGetAfterStatic { location = @Location( value = Kind.FIELD_GET, - clazz = "resources.OnMethodTest", - field = "sField", + clazz = "/^resources.OnMethodTest$/", + field = "/^sField$/", where = Where.AFTER)) public static void args( @Self Object self, diff --git a/btrace-instr/src/test/btrace/onmethod/FieldGetBefore.java b/btrace-instr/src/test/btrace/onmethod/FieldGetBefore.java index d311e92fa..37004dde4 100644 --- a/btrace-instr/src/test/btrace/onmethod/FieldGetBefore.java +++ b/btrace-instr/src/test/btrace/onmethod/FieldGetBefore.java @@ -41,7 +41,7 @@ public class FieldGetBefore { @OnMethod( clazz = "/.*\\.OnMethodTest/", method = "field", - location = @Location(value = Kind.FIELD_GET, clazz = "/.*\\.OnMethodTest/", field = "field")) + location = @Location(value = Kind.FIELD_GET, clazz = "/.*\\.OnMethodTest/", field = "/^field$/")) public static void args( @Self Object self, @TargetInstance Object inst, @TargetMethodOrField String fldName) { println("args"); diff --git a/btrace-instr/src/test/btrace/onmethod/FieldGetBeforeStatic.java b/btrace-instr/src/test/btrace/onmethod/FieldGetBeforeStatic.java index 536b53176..6bb14a345 100644 --- a/btrace-instr/src/test/btrace/onmethod/FieldGetBeforeStatic.java +++ b/btrace-instr/src/test/btrace/onmethod/FieldGetBeforeStatic.java @@ -41,7 +41,7 @@ public class FieldGetBeforeStatic { @OnMethod( clazz = "/.*\\.OnMethodTest/", method = "staticField", - location = @Location(value = Kind.FIELD_GET, clazz = "/.*\\.OnMethodTest/", field = "sField")) + location = @Location(value = Kind.FIELD_GET, clazz = "/.*\\.OnMethodTest/", field = "/^sField$/")) public static void args( @Self Object self, @TargetInstance Object inst, @TargetMethodOrField String fldName) { println("args"); diff --git a/btrace-instr/src/test/btrace/onmethod/FieldSetAfter.java b/btrace-instr/src/test/btrace/onmethod/FieldSetAfter.java index bb1ebcdfd..29cce722b 100644 --- a/btrace-instr/src/test/btrace/onmethod/FieldSetAfter.java +++ b/btrace-instr/src/test/btrace/onmethod/FieldSetAfter.java @@ -46,7 +46,7 @@ public class FieldSetAfter { @Location( value = Kind.FIELD_SET, clazz = "resources.OnMethodTest", - field = "field", + field = "/^field$/", where = Where.AFTER)) public static void args( @Self Object self, diff --git a/btrace-instr/src/test/btrace/onmethod/FieldSetAfterStatic.java b/btrace-instr/src/test/btrace/onmethod/FieldSetAfterStatic.java index f915fd9ff..ccb224f2a 100644 --- a/btrace-instr/src/test/btrace/onmethod/FieldSetAfterStatic.java +++ b/btrace-instr/src/test/btrace/onmethod/FieldSetAfterStatic.java @@ -46,7 +46,7 @@ public class FieldSetAfterStatic { @Location( value = Kind.FIELD_SET, clazz = "resources.OnMethodTest", - field = "sField", + field = "/^sField$/", where = Where.AFTER)) public static void args( @Self Object self, diff --git a/btrace-instr/src/test/btrace/onmethod/FieldSetBefore.java b/btrace-instr/src/test/btrace/onmethod/FieldSetBefore.java index bc71da99f..916b4583c 100644 --- a/btrace-instr/src/test/btrace/onmethod/FieldSetBefore.java +++ b/btrace-instr/src/test/btrace/onmethod/FieldSetBefore.java @@ -41,7 +41,7 @@ public class FieldSetBefore { @OnMethod( clazz = "/.*\\.OnMethodTest/", method = "field", - location = @Location(value = Kind.FIELD_SET, clazz = "/.*\\.OnMethodTest/", field = "field")) + location = @Location(value = Kind.FIELD_SET, clazz = "/.*\\.OnMethodTest/", field = "/^field$/")) public static void args( @Self Object self, @TargetInstance Object inst, diff --git a/btrace-instr/src/test/btrace/onmethod/FieldSetBeforeStatic.java b/btrace-instr/src/test/btrace/onmethod/FieldSetBeforeStatic.java index 31c666340..3fd80d4ae 100644 --- a/btrace-instr/src/test/btrace/onmethod/FieldSetBeforeStatic.java +++ b/btrace-instr/src/test/btrace/onmethod/FieldSetBeforeStatic.java @@ -41,7 +41,7 @@ public class FieldSetBeforeStatic { @OnMethod( clazz = "/.*\\.OnMethodTest/", method = "staticField", - location = @Location(value = Kind.FIELD_SET, clazz = "/.*\\.OnMethodTest/", field = "sField")) + location = @Location(value = Kind.FIELD_SET, clazz = "/.*\\.OnMethodTest/", field = "/^sField$/")) public static void args( @Self Object self, @TargetInstance Object inst, diff --git a/btrace-instr/src/test/btrace/onmethod/leveled/FieldGetAfter.java b/btrace-instr/src/test/btrace/onmethod/leveled/FieldGetAfter.java index a1bccb39d..e2e9b21aa 100644 --- a/btrace-instr/src/test/btrace/onmethod/leveled/FieldGetAfter.java +++ b/btrace-instr/src/test/btrace/onmethod/leveled/FieldGetAfter.java @@ -47,8 +47,8 @@ public class FieldGetAfter { location = @Location( value = Kind.FIELD_GET, - clazz = "resources.OnMethodTest", - field = "field", + clazz = "/^resources.OnMethodTest$/", + field = "/^field$/", where = Where.AFTER), enableAt = @Level(">=1")) public static void args( diff --git a/btrace-instr/src/test/btrace/onmethod/leveled/FieldGetAfterStatic.java b/btrace-instr/src/test/btrace/onmethod/leveled/FieldGetAfterStatic.java index 4f8e2c3cd..a6acc9db7 100644 --- a/btrace-instr/src/test/btrace/onmethod/leveled/FieldGetAfterStatic.java +++ b/btrace-instr/src/test/btrace/onmethod/leveled/FieldGetAfterStatic.java @@ -47,8 +47,8 @@ public class FieldGetAfterStatic { location = @Location( value = Kind.FIELD_GET, - clazz = "resources.OnMethodTest", - field = "sField", + clazz = "/^resources.OnMethodTest$/", + field = "/^sField$/", where = Where.AFTER), enableAt = @Level(">=1")) public static void args( diff --git a/btrace-instr/src/test/btrace/onmethod/leveled/FieldGetBefore.java b/btrace-instr/src/test/btrace/onmethod/leveled/FieldGetBefore.java index d9ae7febd..9ba58e654 100644 --- a/btrace-instr/src/test/btrace/onmethod/leveled/FieldGetBefore.java +++ b/btrace-instr/src/test/btrace/onmethod/leveled/FieldGetBefore.java @@ -42,7 +42,7 @@ public class FieldGetBefore { @OnMethod( clazz = "/.*\\.OnMethodTest/", method = "field", - location = @Location(value = Kind.FIELD_GET, clazz = "/.*\\.OnMethodTest/", field = "field"), + location = @Location(value = Kind.FIELD_GET, clazz = "/.*\\.OnMethodTest/", field = "/^field$/"), enableAt = @Level(">=1")) public static void args( @Self Object self, @TargetInstance Object inst, @TargetMethodOrField String fldName) { diff --git a/btrace-instr/src/test/btrace/onmethod/leveled/FieldGetBeforeStatic.java b/btrace-instr/src/test/btrace/onmethod/leveled/FieldGetBeforeStatic.java index 609dfee88..b98e7bd34 100644 --- a/btrace-instr/src/test/btrace/onmethod/leveled/FieldGetBeforeStatic.java +++ b/btrace-instr/src/test/btrace/onmethod/leveled/FieldGetBeforeStatic.java @@ -42,7 +42,7 @@ public class FieldGetBeforeStatic { @OnMethod( clazz = "/.*\\.OnMethodTest/", method = "staticField", - location = @Location(value = Kind.FIELD_GET, clazz = "/.*\\.OnMethodTest/", field = "sField"), + location = @Location(value = Kind.FIELD_GET, clazz = "/.*\\.OnMethodTest/", field = "/^sField$/"), enableAt = @Level(">=1")) public static void args( @Self Object self, @TargetInstance Object inst, @TargetMethodOrField String fldName) { diff --git a/btrace-instr/src/test/btrace/onmethod/leveled/FieldSetAfter.java b/btrace-instr/src/test/btrace/onmethod/leveled/FieldSetAfter.java index 35715a786..b0661d50e 100644 --- a/btrace-instr/src/test/btrace/onmethod/leveled/FieldSetAfter.java +++ b/btrace-instr/src/test/btrace/onmethod/leveled/FieldSetAfter.java @@ -47,7 +47,7 @@ public class FieldSetAfter { @Location( value = Kind.FIELD_SET, clazz = "resources.OnMethodTest", - field = "field", + field = "/^field$/", where = Where.AFTER), enableAt = @Level(">=1")) public static void args( diff --git a/btrace-instr/src/test/btrace/onmethod/leveled/FieldSetAfterStatic.java b/btrace-instr/src/test/btrace/onmethod/leveled/FieldSetAfterStatic.java index d59e16afb..324c2928e 100644 --- a/btrace-instr/src/test/btrace/onmethod/leveled/FieldSetAfterStatic.java +++ b/btrace-instr/src/test/btrace/onmethod/leveled/FieldSetAfterStatic.java @@ -47,7 +47,7 @@ public class FieldSetAfterStatic { @Location( value = Kind.FIELD_SET, clazz = "resources.OnMethodTest", - field = "sField", + field = "/^sField$/", where = Where.AFTER), enableAt = @Level(">=1")) public static void args( diff --git a/btrace-instr/src/test/btrace/onmethod/leveled/FieldSetBefore.java b/btrace-instr/src/test/btrace/onmethod/leveled/FieldSetBefore.java index 3dcc5d87c..9d99bc850 100644 --- a/btrace-instr/src/test/btrace/onmethod/leveled/FieldSetBefore.java +++ b/btrace-instr/src/test/btrace/onmethod/leveled/FieldSetBefore.java @@ -42,7 +42,7 @@ public class FieldSetBefore { @OnMethod( clazz = "/.*\\.OnMethodTest/", method = "field", - location = @Location(value = Kind.FIELD_SET, clazz = "/.*\\.OnMethodTest/", field = "field"), + location = @Location(value = Kind.FIELD_SET, clazz = "/.*\\.OnMethodTest/", field = "/^field$/"), enableAt = @Level(">=1")) public static void args( @Self Object self, diff --git a/btrace-instr/src/test/btrace/onmethod/leveled/FieldSetBeforeStatic.java b/btrace-instr/src/test/btrace/onmethod/leveled/FieldSetBeforeStatic.java index a67d00f82..736efb9ba 100644 --- a/btrace-instr/src/test/btrace/onmethod/leveled/FieldSetBeforeStatic.java +++ b/btrace-instr/src/test/btrace/onmethod/leveled/FieldSetBeforeStatic.java @@ -42,7 +42,7 @@ public class FieldSetBeforeStatic { @OnMethod( clazz = "/.*\\.OnMethodTest/", method = "staticField", - location = @Location(value = Kind.FIELD_SET, clazz = "/.*\\.OnMethodTest/", field = "sField"), + location = @Location(value = Kind.FIELD_SET, clazz = "/.*\\.OnMethodTest/", field = "/^sField$/"), enableAt = @Level(">=1")) public static void args( @Self Object self, diff --git a/btrace-instr/src/test/java/org/openjdk/btrace/instr/BTraceProbeFactoryTest.java b/btrace-instr/src/test/java/org/openjdk/btrace/instr/BTraceProbeFactoryTest.java index 846e0120c..36baaa9c7 100644 --- a/btrace-instr/src/test/java/org/openjdk/btrace/instr/BTraceProbeFactoryTest.java +++ b/btrace-instr/src/test/java/org/openjdk/btrace/instr/BTraceProbeFactoryTest.java @@ -4,6 +4,8 @@ import java.io.File; import java.net.URL; +import java.net.URLClassLoader; + import org.junit.jupiter.api.Test; class BTraceProbeFactoryTest { @@ -41,4 +43,14 @@ void refuseUnknown() throws Exception { assertFalse(BTraceProbeFactory.canLoad(new File(rsrc.toURI()).getPath())); } + + @Test + void canLoadFromPack() throws Exception { + URL jarUrl = BTraceProbeFactoryTest.class.getResource("/packed/test-pack.jar"); + assertNotNull(jarUrl); + + try (URLClassLoader cl = new URLClassLoader(new URL[] {jarUrl})) { + assertTrue(BTraceProbeFactory.canLoad("io/btrace/btrace_test/AllMethods.class", cl)); + } + } } diff --git a/btrace-instr/src/test/java/org/openjdk/btrace/instr/ClassInfoTest.java b/btrace-instr/src/test/java/org/openjdk/btrace/instr/ClassInfoTest.java new file mode 100644 index 000000000..7795576a1 --- /dev/null +++ b/btrace-instr/src/test/java/org/openjdk/btrace/instr/ClassInfoTest.java @@ -0,0 +1,25 @@ +package org.openjdk.btrace.instr; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.openjdk.btrace.core.BTraceRuntime; +import org.openjdk.btrace.runtime.BTraceRuntimeAccess; + +import java.lang.reflect.Method; + +import static org.junit.jupiter.api.Assertions.*; + +class ClassInfoTest { + @BeforeAll + static void setupAll() throws Throwable { + Method m = BTraceRuntimeAccess.class.getDeclaredMethod("registerRuntimeAccessor"); + m.setAccessible(true); + m.invoke(null); + } + + @Test + void isBootstrap() { + assertTrue(ClassInfo.isBootstrap(String.class.getName())); + assertFalse(ClassInfo.isBootstrap(ClassInfoTest.class.getName())); + } +} \ No newline at end of file diff --git a/btrace-instr/src/test/resources/packed/test-pack.jar b/btrace-instr/src/test/resources/packed/test-pack.jar new file mode 100644 index 000000000..3be0f9531 Binary files /dev/null and b/btrace-instr/src/test/resources/packed/test-pack.jar differ diff --git a/btrace-runtime/build.gradle b/btrace-runtime/build.gradle index 840443a08..2ee5fdf60 100644 --- a/btrace-runtime/build.gradle +++ b/btrace-runtime/build.gradle @@ -69,7 +69,7 @@ dependencies { implementation group: 'org.slf4j', name: 'slf4j-api', version: '1.7.36' // https://mvnrepository.com/artifact/org.jctools/jctools-core - implementation(group: 'org.jctools', name: 'jctools-core', version: '4.0.1') + implementation(group: 'org.jctools', name: 'jctools-core', version: '4.0.5') java9Implementation project(':btrace-core') java9Implementation project(':btrace-services-api') diff --git a/btrace-runtime/src/main/java/org/openjdk/btrace/runtime/BTraceRuntimeImpl_8.java b/btrace-runtime/src/main/java/org/openjdk/btrace/runtime/BTraceRuntimeImpl_8.java index a057a01fb..a964df59d 100644 --- a/btrace-runtime/src/main/java/org/openjdk/btrace/runtime/BTraceRuntimeImpl_8.java +++ b/btrace-runtime/src/main/java/org/openjdk/btrace/runtime/BTraceRuntimeImpl_8.java @@ -26,6 +26,8 @@ package org.openjdk.btrace.runtime; import java.lang.instrument.Instrumentation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.security.AccessController; @@ -85,6 +87,8 @@ public boolean isEnabled() { private final boolean hasJfr; + private final Method findBootstrapOrNullMtd; + public BTraceRuntimeImpl_8() { boolean jfr = false; try { @@ -94,6 +98,13 @@ public BTraceRuntimeImpl_8() { } hasJfr = jfr; eventFactories = hasJfr ? new java.util.concurrent.CopyOnWriteArraySet<>() : null; + + Method m = null; + try { + m = ClassLoader.class.getDeclaredMethod("findBootstrapClassOrNull", String.class); + m.setAccessible(true); + } catch (NoSuchMethodException ignored) {} + findBootstrapOrNullMtd = m; } public BTraceRuntimeImpl_8( @@ -107,6 +118,13 @@ public BTraceRuntimeImpl_8( } hasJfr = jfr; eventFactories = hasJfr ? new java.util.concurrent.CopyOnWriteArraySet<>() : null; + + Method m = null; + try { + m = ClassLoader.class.getDeclaredMethod("findBootstrapClassOrNull", String.class); + m.setAccessible(true); + } catch (NoSuchMethodException ignored) {} + findBootstrapOrNullMtd = m; } @Override @@ -192,6 +210,14 @@ public JfrEvent.Factory createEventFactory(JfrEvent.Template template) { return () -> JfrEvent.EMPTY; } + @Override + public boolean isBootstrapClass(String className) { + try { + return findBootstrapOrNullMtd != null && findBootstrapOrNullMtd.invoke(ClassLoader.getSystemClassLoader(), className) != null; + } catch (IllegalAccessException | InvocationTargetException ignored) {} + return false; + } + @Override protected void cleanupRuntime() { if (hasJfr) { diff --git a/btrace-runtime/src/main/java11/org/openjdk/btrace/runtime/BTraceRuntimeImpl_11.java b/btrace-runtime/src/main/java11/org/openjdk/btrace/runtime/BTraceRuntimeImpl_11.java index 881fd9cef..8b987a047 100644 --- a/btrace-runtime/src/main/java11/org/openjdk/btrace/runtime/BTraceRuntimeImpl_11.java +++ b/btrace-runtime/src/main/java11/org/openjdk/btrace/runtime/BTraceRuntimeImpl_11.java @@ -28,6 +28,7 @@ import java.lang.instrument.Instrumentation; import java.lang.invoke.MethodHandles; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.security.AccessController; @@ -41,6 +42,7 @@ import jdk.internal.reflect.Reflection; import jdk.jfr.EventType; import org.openjdk.btrace.core.ArgsMap; +import org.openjdk.btrace.core.BTraceRuntime; import org.openjdk.btrace.core.comm.CommandListener; import org.openjdk.btrace.core.jfr.JfrEvent; import org.openjdk.btrace.runtime.auxiliary.Auxiliary; @@ -85,28 +87,49 @@ public boolean isEnabled() { private final Set eventFactories = new java.util.concurrent.CopyOnWriteArraySet<>(); - public BTraceRuntimeImpl_11() {} + private final Method findBootstrapOrNullMtd; + + public BTraceRuntimeImpl_11() { + fixExports(BTraceRuntime.instrumentation); + + Method m = null; + try { + m = ClassLoader.class.getDeclaredMethod("findBootstrapClassOrNull", String.class); + m.setAccessible(true); + } catch (NoSuchMethodException ignored) {} + findBootstrapOrNullMtd = m; + } public BTraceRuntimeImpl_11( String className, ArgsMap args, CommandListener cmdListener, Instrumentation inst) { super(className, args, cmdListener, fixExports(inst)); + + Method m = null; + try { + m = ClassLoader.class.getDeclaredMethod("findBootstrapClassOrNull", String.class); + m.setAccessible(true); + } catch (NoSuchMethodException ignored) {} + findBootstrapOrNullMtd = m; } private static Instrumentation fixExports(Instrumentation instr) { Set myModules = Collections.singleton(BTraceRuntimeImpl_11.class.getModule()); if (instr != null) { + Module javaBaseMod = int.class.getModule(); + Module jfrMod = EventType.class.getModule(); instr.redefineModule( - String.class.getModule(), - Collections.emptySet(), - Map.of( - "jdk.internal.reflect", myModules, - "jdk.internal.perf", myModules, - "sun.security.action", myModules), - Map.of("java.lang", myModules), - Collections.emptySet(), - Collections.emptyMap()); + javaBaseMod, + Collections.emptySet(), + Map.of( + "java.lang", myModules, + "jdk.internal.reflect", myModules, + "jdk.internal.perf", myModules, + "sun.security.action", myModules), + Map.of("java.lang", myModules), + Collections.emptySet(), + Collections.emptyMap()); instr.redefineModule( - EventType.class.getModule(), + jfrMod, // jdk.jfr Collections.emptySet(), Map.of("jdk.jfr.internal", myModules), Map.of("jdk.jfr", myModules), @@ -221,6 +244,14 @@ public JfrEvent.Factory createEventFactory(JfrEvent.Template template) { return factory; } + @Override + public boolean isBootstrapClass(String className) { + try { + return findBootstrapOrNullMtd != null && findBootstrapOrNullMtd.invoke(ClassLoader.getSystemClassLoader(), className) != null; + } catch (IllegalAccessException | InvocationTargetException ignored) {} + return false; + } + @Override protected void cleanupRuntime() { for (JfrEventFactoryImpl factory : eventFactories) { diff --git a/btrace-runtime/src/main/java9/org/openjdk/btrace/runtime/BTraceRuntimeImpl_9.java b/btrace-runtime/src/main/java9/org/openjdk/btrace/runtime/BTraceRuntimeImpl_9.java index ca6eb8aca..97ded89b7 100644 --- a/btrace-runtime/src/main/java9/org/openjdk/btrace/runtime/BTraceRuntimeImpl_9.java +++ b/btrace-runtime/src/main/java9/org/openjdk/btrace/runtime/BTraceRuntimeImpl_9.java @@ -28,6 +28,7 @@ import java.lang.instrument.Instrumentation; import java.lang.invoke.MethodHandles; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.security.AccessController; @@ -40,6 +41,7 @@ import jdk.internal.reflect.CallerSensitive; import jdk.internal.reflect.Reflection; import org.openjdk.btrace.core.ArgsMap; +import org.openjdk.btrace.core.BTraceRuntime; import org.openjdk.btrace.core.comm.CommandListener; import org.openjdk.btrace.core.jfr.JfrEvent; import org.openjdk.btrace.runtime.auxiliary.Auxiliary; @@ -81,11 +83,29 @@ public boolean isEnabled() { private static Perf perf; - public BTraceRuntimeImpl_9() {} + private final Method findBootstrapOrNullMtd; + + public BTraceRuntimeImpl_9() { + fixExports(BTraceRuntime.instrumentation); + + Method m = null; + try { + m = ClassLoader.class.getDeclaredMethod("findBootstrapClassOrNull", String.class); + m.setAccessible(true); + } catch (NoSuchMethodException ignored) {} + findBootstrapOrNullMtd = m; + } public BTraceRuntimeImpl_9( String className, ArgsMap args, CommandListener cmdListener, Instrumentation inst) { super(className, args, cmdListener, fixExports(inst)); + + Method m = null; + try { + m = ClassLoader.class.getDeclaredMethod("findBootstrapClassOrNull", String.class); + m.setAccessible(true); + } catch (NoSuchMethodException ignored) {} + findBootstrapOrNullMtd = m; } private static Instrumentation fixExports(Instrumentation instr) { @@ -230,6 +250,14 @@ public JfrEvent.Factory createEventFactory(JfrEvent.Template template) { return () -> JfrEvent.EMPTY; } + @Override + public boolean isBootstrapClass(String className) { + try { + return findBootstrapOrNullMtd != null && findBootstrapOrNullMtd.invoke(null, className) != null; + } catch (IllegalAccessException | InvocationTargetException ignored) {} + return false; + } + private static Perf getPerf() { synchronized (BTraceRuntimeImpl_9.class) { if (perf == null) { diff --git a/btrace-ui/build.gradle b/btrace-ui/build.gradle index eca1a328c..545d76e5d 100644 --- a/btrace-ui/build.gradle +++ b/btrace-ui/build.gradle @@ -1,3 +1,7 @@ +buildscript { scriptHandler -> + apply from: rootProject.file('buildSrc/shared.gradle'), to: scriptHandler +} + plugins { alias(libs.plugins.versioning) } diff --git a/buildSrc/shared.gradle b/buildSrc/shared.gradle new file mode 100644 index 000000000..b9957b547 --- /dev/null +++ b/buildSrc/shared.gradle @@ -0,0 +1,10 @@ +configurations.all { + resolutionStrategy.eachDependency { details -> + if (details.requested.group == 'org.ajoberstar.grgit' && + details.requested.name == 'grgit-core' && + details.requested.version == '4.0.1') { + details.useVersion '4.1.1' + details.because '4.0.1 has been removed' + } + } +} diff --git a/common.gradle b/common.gradle index aac1183c4..aab348175 100644 --- a/common.gradle +++ b/common.gradle @@ -6,7 +6,7 @@ apply plugin: 'java' apply plugin: 'idea' project.group = 'org.openjdk.btrace' -project.version = '2.2.4' +project.version = '2.2.6' buildscript { repositories { diff --git a/docs/BTraceTutorial.md b/docs/BTraceTutorial.md index 9a18744b4..7b2ac783d 100644 --- a/docs/BTraceTutorial.md +++ b/docs/BTraceTutorial.md @@ -1,4 +1,4 @@ -# BTrace Tutorial (BTrace 2.2.2) +# BTrace Tutorial (BTrace 2.2.6) ## 1. Hello World diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 249e5832f..e6441136f 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 84a0b92f9..df97d72b8 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip +networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index a69d9cb6c..1aa94a426 100755 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,13 +80,11 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,22 +131,29 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -193,11 +198,15 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ diff --git a/gradlew.bat b/gradlew.bat index 53a6b238d..7101f8e46 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -26,6 +26,7 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -42,11 +43,11 @@ set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -56,11 +57,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail diff --git a/integration-tests/src/test/btrace/OnMethodTest.java b/integration-tests/src/test/btrace/OnMethodTest.java index a29827313..7dfd96ccb 100644 --- a/integration-tests/src/test/btrace/OnMethodTest.java +++ b/integration-tests/src/test/btrace/OnMethodTest.java @@ -68,6 +68,23 @@ public static void args(@Self Object self, int i, String s) { println("prop: " + property("btrace.test")); } + @OnMethod(clazz = "+resources.Main", method = "startWork") + public static void onSubtype() { + println("subtype"); + } + + @OnMethod(clazz = "resources.Main", method = "/^call.*/", + location = @Location(value = Kind.FIELD_GET, clazz = "resources.Main", field = "/^s?[fF]ield$/")) + public static void fieldGet(@TargetMethodOrField(fqn = true) String fldName) { + println("fieldGet: " + fldName); + } + + @OnMethod(clazz = "resources.Main", method = "/^call.*/", + location = @Location(value = Kind.FIELD_SET, clazz = "resources.Main", field = "/^s?[fF]ield$/")) + public static void fieldSet(@TargetMethodOrField(fqn = true) String fldName) { + println("fieldSet: " + fldName); + } + @OnTimer(500) public static void doRecurrent() { long x = 10; diff --git a/integration-tests/src/test/java/resources/Main.java b/integration-tests/src/test/java/resources/Main.java index b6b82d301..b300becd9 100644 --- a/integration-tests/src/test/java/resources/Main.java +++ b/integration-tests/src/test/java/resources/Main.java @@ -30,6 +30,8 @@ */ public class Main extends TestApp { private String id = "xxx"; + private String field; + private static String sField; public static void main(String[] args) throws Exception { Main i = new Main(); @@ -49,11 +51,13 @@ protected void startWork() { } private void callA() { + field = "AAA"; + sField = "BBB"; print("i=" + callB(1, "Hello World")); } private int callB(int i, String s) { - print("[" + i + "] = " + s); + print("[" + i + "] = " + s + ", field = " + field + ", sField = " + sField); return i + 1; } diff --git a/integration-tests/src/test/java/tests/BTraceFunctionalTests.java b/integration-tests/src/test/java/tests/BTraceFunctionalTests.java index 8bc36ee24..c1ac75757 100644 --- a/integration-tests/src/test/java/tests/BTraceFunctionalTests.java +++ b/integration-tests/src/test/java/tests/BTraceFunctionalTests.java @@ -50,8 +50,8 @@ */ public class BTraceFunctionalTests extends RuntimeTest { @BeforeAll - public static void classSetup() throws Exception { - setup(); + public static void setup() throws Exception { + classSetup(); } @BeforeEach @@ -153,7 +153,7 @@ public void testOnMethod() throws Exception { test( "resources.Main", "btrace/OnMethodTest.java", - 10, + 14, new ResultValidator() { @Override public void validate(String stdout, String stderr, int retcode, String jfrFile) { @@ -164,6 +164,10 @@ public void validate(String stdout, String stderr, int retcode, String jfrFile) assertTrue(stdout.contains("{xxx}")); assertTrue(stdout.contains("heap:init")); assertTrue(stdout.contains("prop: test")); + assertTrue(stdout.contains("fieldSet: field java.lang.String resources.Main#field")); + assertTrue(stdout.contains("fieldSet: static field java.lang.String resources.Main#sField")); + assertTrue(stdout.contains("fieldGet: field java.lang.String resources.Main#field")); + assertTrue(stdout.contains("fieldGet: static field java.lang.String resources.Main#sField")); } }); } diff --git a/integration-tests/src/test/java/tests/RuntimeTest.java b/integration-tests/src/test/java/tests/RuntimeTest.java index 114c2286e..a9c1bf262 100644 --- a/integration-tests/src/test/java/tests/RuntimeTest.java +++ b/integration-tests/src/test/java/tests/RuntimeTest.java @@ -29,7 +29,6 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; -import java.net.URL; import java.nio.charset.StandardCharsets; import java.nio.file.FileVisitResult; import java.nio.file.FileVisitor; @@ -69,11 +68,11 @@ public abstract class RuntimeTest { /** Track retransforming progress */ protected boolean trackRetransforms = false; /** Provide extra JVM args */ - protected List extraJvmArgs = Collections.emptyList(); + private static final List extraJvmArgs = new ArrayList<>(); protected boolean attachDebugger = false; - public static void setup() { + public static void classSetup() { String forceDebugVal = System.getProperty("btrace.test.debug"); if (forceDebugVal == null) { forceDebugVal = System.getenv("BTRACE_TEST_DEBUG"); @@ -97,8 +96,18 @@ public static void setup() { Assertions.assertNotNull(clientClassPath); String toolsjar = null; - - javaHome = System.getProperty("java.home").replace("/jre", ""); + // TEST_JAVA_HOME has the highest precedence + javaHome = System.getenv("TEST_JAVA_HOME"); + if (javaHome == null) { + javaHome = System.getenv("JAVA_HOME"); + } + if (javaHome == null) { + javaHome = System.getProperty("java.home"); + } + if (javaHome == null) { + throw new IllegalStateException("Missing TEST_JAVA_HOME or JAVA_HOME env variables"); + } + javaHome = javaHome.replace("/jre", ""); Path toolsJarPath = Paths.get(javaHome, "lib", "tools.jar"); if (Files.exists(toolsJarPath)) { @@ -151,6 +160,7 @@ public void test( } args.add("-XX:+AllowRedefinitionToAddDeleteMethods"); args.add("-XX:+IgnoreUnrecognizedVMOptions"); + args.add("-XX:+EnableDynamicAgentLoading"); // uncomment the following line to get extra JFR logs // args.add("-Xlog:jfr*=trace"); args.addAll(extraJvmArgs); @@ -389,16 +399,12 @@ public void runBTrace(String[] args, ProcessOutputProcessor outputProcessor) thr "-d", Paths.get(System.getProperty("java.io.tmpdir"), "btrace-test").toString())); argVals.addAll(Arrays.asList(args)); - String testJavaHome = System.getenv("TEST_JAVA_HOME"); - testJavaHome = testJavaHome != null ? testJavaHome : System.getenv("JAVA_HOME"); - if (testJavaHome == null) { - throw new IllegalStateException("Missing TEST_JAVA_HOME or JAVA_HOME env variables"); - } - if (Files.exists(Paths.get(testJavaHome, "jmods"))) { + if (Files.exists(Paths.get(javaHome, "jmods"))) { argVals.addAll( 1, Arrays.asList("--add-exports", "jdk.internal.jvmstat/sun.jvmstat.monitor=ALL-UNNAMED")); } + ProcessBuilder pb = new ProcessBuilder(argVals); pb.environment().remove("JAVA_TOOL_OPTIONS"); @@ -485,7 +491,7 @@ public Process runBTrace( "-d", Paths.get(System.getProperty("java.io.tmpdir"), "btrace-test").toString())); argVals.addAll(Arrays.asList(args)); - if (Files.exists(Paths.get(System.getenv("TEST_JAVA_HOME"), "jmods"))) { + if (Files.exists(Paths.get(javaHome, "jmods"))) { argVals.addAll( 1, Arrays.asList("--add-exports", "jdk.internal.jvmstat/sun.jvmstat.monitor=ALL-UNNAMED")); @@ -637,11 +643,7 @@ private Process attach( if (cmdArgs != null) { argVals.addAll(Arrays.asList(cmdArgs)); } - String testJavaHome = System.getenv("TEST_JAVA_HOME"); - if (testJavaHome == null) { - testJavaHome = System.getenv("JAVA_HOME"); - } - if (Files.exists(Paths.get(testJavaHome, "jmods"))) { + if (Files.exists(Paths.get(javaHome, "jmods"))) { argVals.addAll( 1, Arrays.asList("--add-exports", "jdk.internal.jvmstat/sun.jvmstat.monitor=ALL-UNNAMED")); diff --git a/settings.gradle b/settings.gradle index 346db88fb..8ac2e791d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -28,19 +28,19 @@ dependencyResolutionManagement { versionCatalogs { libs { // Families... - version('asm', '9.4') + version('asm', '9.7.1') version('jmh', '1.36') - version('jmhGradlePlugin', '0.7.1') + version('jmhGradlePlugin', '0.7.2') version('slf4j', '1.7.36') - version('junit', '5.10.0') + version('junit', '5.11.3') // *** Gradle plugins *** plugin ('spotless', 'com.diffplug.spotless').version('6.11.0') plugin ('versioning', 'net.nemerosa.versioning').version('2.15.1') - plugin ('ospackage', 'com.netflix.nebula.ospackage').version('11.4.0') + plugin ('ospackage', 'com.netflix.nebula.ospackage').version('11.10.0') plugin ('shadow', 'com.github.johnrengelman.shadow').version('7.1.2') plugin ('sdkman-vendors', 'io.sdkman.vendors').version('3.0.0') - plugin ('foojay-resolver', 'org.gradle.toolchains.foojay-resolver-convention').version('0.6.0') + plugin ('foojay-resolver', 'org.gradle.toolchains.foojay-resolver-convention').version('0.8.0') plugin ('jmh', 'me.champeau.jmh').versionRef('jmhGradlePlugin') @@ -49,7 +49,7 @@ dependencyResolutionManagement { // https://mvnrepository.com/artifact/com.google.auto.service library ('autoService', 'com.google.auto.service', 'auto-service').version('1.1.1') // https://mvnrepository.com/artifact/org.jctools - library ('jctools', 'org.jctools', 'jctools-core').version('4.0.1') + library ('jctools', 'org.jctools', 'jctools-core').version('4.0.5') // https://mvnrepository.com/artifact/org.slf4j