diff --git a/.gitignore b/.gitignore index e61731c..c275c3c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ -# Ignore Gradle project-specific cache directory -.gradle - -# Ignore Gradle build output directory -build -out +# Ignore Gradle project-specific cache directory +.gradle + +# Ignore Gradle build output directory +build +out tracy-jni/build \ No newline at end of file diff --git a/README.md b/README.md index 39d9e5b..0fd11ff 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,41 @@ -## TracyJavaBindings – Java bindings for the Tracy Profiler -### Prerequisites -- After cloning the repository, ensure that you have checked-out submodules with `git submodule update --init`. -### Building -#### JNI Bindings -- You can build the JNI bindings with `gradlew jar` -#### Tracy profiler GUI -> _All commands need to be run in the `tracy-jni/tracy` subdirectory._ -- Create a new `build` folder under `tracy-jni/tracy/profiler`. -- Configure the profiler using `cmake -B profiler/build -S profiler -DCMAKE_BUILD_TYPE=Release` (this only needs to be done once). -- Build the profiler using `cmake --build profiler/build --parallel --config Release`. -- The built `tracy-profiler` executable can be found in the `profiler/build/Release` directory. - -### Integration -- At runtime, the `java.library.path` system property should contain a path to the native JNI libraries found in `tracy-jni/build/lib/main/release`. -### Usage Example -```java -import io.github.benjaminamos.tracy.Tracy; - -public class TracyTest { - public static void main(String[] args) { - // Start profiling - Tracy.startupProfiler(); - - // Allocate and begin zone - long handle = Tracy.allocSourceLocation(0, "Test.java", "test()", "Test!", 0); - Tracy.ZoneContext zoneContext = Tracy.zoneBegin(handle, 1); - - // Do work... - - // End zone - Tracy.zoneEnd(zoneContext); - - // Begin new frame - Tracy.markFrame(); - - // Stop profiling - Tracy.shutdownProfiler(); - } -} +## TracyJavaBindings – Java bindings for the Tracy Profiler +### Prerequisites +- After cloning the repository, ensure that you have checked-out submodules with `git submodule update --init`. +### Building +#### JNI Bindings +- You can build the JNI bindings with `gradlew jar` +#### Tracy profiler GUI +> _All commands need to be run in the `tracy-jni/tracy` subdirectory._ +- Create a new `build` folder under `tracy-jni/tracy/profiler`. +- Configure the profiler using `cmake -B profiler/build -S profiler -DCMAKE_BUILD_TYPE=Release` (this only needs to be done once). +- Build the profiler using `cmake --build profiler/build --parallel --config Release`. +- The built `tracy-profiler` executable can be found in the `profiler/build/Release` directory. + +### Integration +- At runtime, the `java.library.path` system property should contain a path to the native JNI libraries found in `tracy-jni/build/lib/main/release`. +### Usage Example +```java +import io.github.benjaminamos.tracy.Tracy; + +public class TracyTest { + public static void main(String[] args) { + // Start profiling + Tracy.startupProfiler(); + + // Allocate and begin zone + long handle = Tracy.allocSourceLocation(0, "Test.java", "test()", "Test!", 0); + Tracy.ZoneContext zoneContext = Tracy.zoneBegin(handle, 1); + + // Do work... + + // End zone + Tracy.zoneEnd(zoneContext); + + // Begin new frame + Tracy.markFrame(); + + // Stop profiling + Tracy.shutdownProfiler(); + } +} ``` \ No newline at end of file diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 8e081da..0000000 --- a/build.gradle +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2024 The Terasology Foundation -// SPDX-License-Identifier: Apache-2.0 - -plugins { - id 'java-library' -} - -group 'io.github.benjaminamos.TracyJavaBindings' -version '1.0-SNAPSHOT' - -repositories { - mavenCentral() -} - -dependencies { - testImplementation 'org.junit.jupiter:junit-jupiter:5.8.1' - testRuntimeOnly("org.junit.platform:junit-platform-launcher") -} - -sourceSets { - main -} - -tasks.withType(Jar).configureEach { - def sharedLibraryTasks = project('tracy-jni').tasks.withType(LinkSharedLibrary) - dependsOn sharedLibraryTasks - def sharedLibraryOutputs = sharedLibraryTasks.find { task -> - task.name.toLowerCase().contains("release") - }.outputs.files.asFileTree.files - from(sharedLibraryOutputs) { - include("*.dll") - into("windows") - } - from(sharedLibraryOutputs) { - include("*.so") - into("linux") - } - from(sharedLibraryOutputs) { - include("*.dylib") - into("macosx") - } - from (new File(project('tracy-jni').projectDir, "tracy")) { - include("LICENSE") - rename("LICENSE", "TRACY_LICENSE") - } -} - -tasks.named("test", Test) { - useJUnitPlatform() - - maxHeapSize = "1G" - - testLogging { - events("passed") - } - - systemProperty "java.library.path", project(":tracy-jni").getLayout().getBuildDirectory().dir("lib/main/debug").get().asFile.absolutePath -} diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..37e0e44 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,68 @@ +// Copyright 2024 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +// the following is possible to build for differing java versions. default is java-17. +// gradle clean build -PjavacRelease=21 +val javacRelease = (project.findProperty("javacRelease") ?: "17") as String + +plugins { + id("java-library") +} + +group "io.github.benjaminamos.TracyJavaBindings" +version "1.0-SNAPSHOT" + +repositories { + mavenCentral() +} + +dependencies { + testImplementation("org.junit.jupiter:junit-jupiter:5.8.1") + testRuntimeOnly("org.junit.platform:junit-platform-launcher") +} + +sourceSets { + main +} + +tasks.compileJava { + options.release.set(Integer.parseInt(javacRelease)) +} + +tasks.withType().configureEach { + val sharedLibraryTasks = project(":tracy-jni").tasks.withType() + dependsOn(sharedLibraryTasks) + val sharedLibraryOutputs = sharedLibraryTasks.find { task -> + task.name.toLowerCase().contains("release") + }?.outputs?.files?.asFileTree?.files + sharedLibraryOutputs?.let { + from(it) { + include("*.dll") + into("windows") + } + from(it) { + include("*.so") + into("linux") + } + from(it) { + include("*.dylib") + into("macosx") + } + } + from(File(project(":tracy-jni").projectDir, "tracy")) { + include("LICENSE") + rename("LICENSE", "TRACY_LICENSE") + } +} + +tasks.named("test") { + useJUnitPlatform() + + maxHeapSize = "1G" + + testLogging { + events("passed") + } + + systemProperty("java.library.path", project(":tracy-jni").layout.buildDirectory.dir("lib/main/debug").get().asFile.absolutePath) +} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b82aa23..df97d72 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/settings.gradle b/settings.gradle.kts similarity index 65% rename from settings.gradle rename to settings.gradle.kts index 35d1b9f..0bf1d70 100644 --- a/settings.gradle +++ b/settings.gradle.kts @@ -1,3 +1,3 @@ -rootProject.name = "TracyJavaBindings" - -include 'tracy-jni' \ No newline at end of file +rootProject.name = "TracyJavaBindings" + +include("tracy-jni") \ No newline at end of file diff --git a/src/main/java/io/github/benjaminamos/tracy/Tracy.java b/src/main/java/io/github/benjaminamos/tracy/Tracy.java index 20955c8..f41b648 100644 --- a/src/main/java/io/github/benjaminamos/tracy/Tracy.java +++ b/src/main/java/io/github/benjaminamos/tracy/Tracy.java @@ -1,46 +1,46 @@ -// Copyright 2024 The Terasology Foundation -// SPDX-License-Identifier: Apache-2.0 - -package io.github.benjaminamos.tracy; - -import java.io.File; - -public final class Tracy { - private Tracy() { - } - - static { - String libraryPath = System.getProperty("org.terasology.librarypath"); - if (libraryPath == null) { - System.loadLibrary("tracy-jni-" + System.getProperty("os.arch")); - } else { - File libraryDirectory = new File(libraryPath); - if (libraryDirectory.exists() && libraryDirectory.isDirectory()) { - String architecture = System.getProperty("os.arch"); - for (File file : libraryDirectory.listFiles()) { - if (file.getName().startsWith("tracy-jni-" + architecture) || file.getName().startsWith("libtracy-jni-" + architecture)) { - System.load(file.getPath()); - } - } - } - } - } - - public static native void startupProfiler(); - public static native void shutdownProfiler(); - public static native boolean isConnected(); - public static native void markFrame(); - public static native long allocSourceLocation(int line, String source, String function, String name, int colour); - public static native ZoneContext zoneBegin(long sourceLocation, int active); - public static native void zoneEnd(ZoneContext zoneContext); - - public static final class ZoneContext { - public final int id; - public final int active; - - public ZoneContext(int id, int active) { - this.id = id; - this.active = active; - } - } -} +// Copyright 2024 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package io.github.benjaminamos.tracy; + +import java.io.File; + +public final class Tracy { + private Tracy() { + } + + static { + String libraryPath = System.getProperty("org.terasology.librarypath"); + if (libraryPath == null) { + System.loadLibrary("tracy-jni-" + System.getProperty("os.arch")); + } else { + File libraryDirectory = new File(libraryPath); + if (libraryDirectory.exists() && libraryDirectory.isDirectory()) { + String architecture = System.getProperty("os.arch"); + for (File file : libraryDirectory.listFiles()) { + if (file.getName().startsWith("tracy-jni-" + architecture) || file.getName().startsWith("libtracy-jni-" + architecture)) { + System.load(file.getPath()); + } + } + } + } + } + + public static native void startupProfiler(); + public static native void shutdownProfiler(); + public static native boolean isConnected(); + public static native void markFrame(); + public static native long allocSourceLocation(int line, String source, String function, String name, int colour); + public static native ZoneContext zoneBegin(long sourceLocation, int active); + public static native void zoneEnd(ZoneContext zoneContext); + + public static final class ZoneContext { + public final int id; + public final int active; + + public ZoneContext(int id, int active) { + this.id = id; + this.active = active; + } + } +} diff --git a/src/test/java/io/github/benjaminamos/tracy/TracyTest.java b/src/test/java/io/github/benjaminamos/tracy/TracyTest.java index 32336df..f4a019e 100644 --- a/src/test/java/io/github/benjaminamos/tracy/TracyTest.java +++ b/src/test/java/io/github/benjaminamos/tracy/TracyTest.java @@ -1,40 +1,40 @@ -// Copyright 2024 The Terasology Foundation -// SPDX-License-Identifier: Apache-2.0 - -package io.github.benjaminamos.tracy; - -import org.junit.jupiter.api.Test; - -public class TracyTest { - @Test - public void testStartup() { - Tracy.startupProfiler(); - - System.out.println("Waiting for profiler..."); - if (!Tracy.isConnected()) { - try { - this.wait(5000); - } catch (Throwable ignore) { - } - - if (!Tracy.isConnected()) { - System.out.println("Profiler connect timeout exceeded."); - return; - } - } - - long handle = Tracy.allocSourceLocation(0, "Test.java", "test()", "Test!", 0); - Tracy.ZoneContext zoneContext = Tracy.zoneBegin(handle, 1); - - try { - this.wait(5000); - } catch (Throwable ignore) { - } - - Tracy.zoneEnd(zoneContext); - - Tracy.markFrame(); - - Tracy.shutdownProfiler(); - } -} +// Copyright 2024 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +package io.github.benjaminamos.tracy; + +import org.junit.jupiter.api.Test; + +public class TracyTest { + @Test + public void testStartup() { + Tracy.startupProfiler(); + + System.out.println("Waiting for profiler..."); + if (!Tracy.isConnected()) { + try { + this.wait(5000); + } catch (Throwable ignore) { + } + + if (!Tracy.isConnected()) { + System.out.println("Profiler connect timeout exceeded."); + return; + } + } + + long handle = Tracy.allocSourceLocation(0, "Test.java", "test()", "Test!", 0); + Tracy.ZoneContext zoneContext = Tracy.zoneBegin(handle, 1); + + try { + this.wait(5000); + } catch (Throwable ignore) { + } + + Tracy.zoneEnd(zoneContext); + + Tracy.markFrame(); + + Tracy.shutdownProfiler(); + } +} diff --git a/tracy-jni/build.gradle.kts b/tracy-jni/build.gradle.kts index d36b208..0f6aeb8 100644 --- a/tracy-jni/build.gradle.kts +++ b/tracy-jni/build.gradle.kts @@ -1,23 +1,23 @@ -import org.gradle.internal.jvm.Jvm - -// Copyright 2024 The Terasology Foundation -// SPDX-License-Identifier: Apache-2.0 - -plugins { - `cpp-library` -} - -version = "1.0.0" - -library { - baseName = "tracy-jni-" + System.getProperty("os.arch") - source.from(file("src")) - privateHeaders.from( - file("src"), file("tracy/public"), - file("${Jvm.current().javaHome}/include"), - file("${Jvm.current().javaHome}/include/win32"), - file("${Jvm.current().javaHome}/include/darwin"), - file("${Jvm.current().javaHome}/include/linux") - ) - publicHeaders.from(file("include")) -} +import org.gradle.internal.jvm.Jvm + +// Copyright 2024 The Terasology Foundation +// SPDX-License-Identifier: Apache-2.0 + +plugins { + `cpp-library` +} + +version = "1.0.0" + +library { + baseName = "tracy-jni-" + System.getProperty("os.arch") + source.from(file("src")) + privateHeaders.from( + file("src"), file("tracy/public"), + file("${Jvm.current().javaHome}/include"), + file("${Jvm.current().javaHome}/include/win32"), + file("${Jvm.current().javaHome}/include/darwin"), + file("${Jvm.current().javaHome}/include/linux") + ) + publicHeaders.from(file("include")) +} diff --git a/tracy-jni/src/tracy_jni.cpp b/tracy-jni/src/tracy_jni.cpp index ffb0e54..4e00b42 100644 --- a/tracy-jni/src/tracy_jni.cpp +++ b/tracy-jni/src/tracy_jni.cpp @@ -1,66 +1,66 @@ -#define TRACY_DELAYED_INIT -#define TRACY_MANUAL_LIFETIME -#define TRACY_NO_CRASH_HANDLER -#define TRACY_NO_CONTEXT_SWITCH -#define TRACY_NO_SAMPLING -#define TRACY_NO_FRAME_IMAGE -#define TRACY_ENABLE -#include "TracyClient.cpp" -#include "tracy/TracyC.h" - -#include "jni.h" - -extern "C" -{ - JNIEXPORT void JNICALL Java_io_github_benjaminamos_tracy_Tracy_startupProfiler(JNIEnv *env, jobject obj) - { - ___tracy_startup_profiler(); - } - - JNIEXPORT void JNICALL Java_io_github_benjaminamos_tracy_Tracy_shutdownProfiler(JNIEnv *env, jobject obj) - { - ___tracy_shutdown_profiler(); - } - - JNIEXPORT jboolean JNICALL Java_io_github_benjaminamos_tracy_Tracy_isConnected(JNIEnv *env, jobject obj) - { - return ___tracy_connected(); - } - - JNIEXPORT void JNICALL Java_io_github_benjaminamos_tracy_Tracy_markFrame(JNIEnv *env, jobject obj) - { - ___tracy_emit_frame_mark(NULL); - } - - JNIEXPORT jlong JNICALL Java_io_github_benjaminamos_tracy_Tracy_allocSourceLocation(JNIEnv *env, jobject obj, jint line, jstring source, jstring function, jstring name, jint colour) - { - const char* sourceUTF8 = env->GetStringUTFChars(source, NULL); - const char* functionUTF8 = env->GetStringUTFChars(function, NULL); - const char* nameUTF8 = env->GetStringUTFChars(name, NULL); - - uint64_t handle = ___tracy_alloc_srcloc_name(line, sourceUTF8, env->GetStringUTFLength(source), functionUTF8, env->GetStringUTFLength(function), nameUTF8, env->GetStringUTFLength(name), colour); - - env->ReleaseStringUTFChars(source, sourceUTF8); - env->ReleaseStringUTFChars(function, functionUTF8); - env->ReleaseStringUTFChars(name, nameUTF8); - return handle; - } - - JNIEXPORT jobject JNICALL Java_io_github_benjaminamos_tracy_Tracy_zoneBegin(JNIEnv *env, jobject obj, jlong sourceLocation, int active) - { - TracyCZoneCtx zoneContext = ___tracy_emit_zone_begin_alloc(sourceLocation, active); - - jclass zoneContextClass = env->FindClass("io/github/benjaminamos/tracy/Tracy$ZoneContext"); - return env->NewObject(zoneContextClass, env->GetMethodID(zoneContextClass, "", "(II)V"), zoneContext.id, zoneContext.active); - } - - JNIEXPORT void JNICALL Java_io_github_benjaminamos_tracy_Tracy_zoneEnd(JNIEnv *env, jobject obj, jobject zoneContextObject) - { - jclass zoneContextClass = env->FindClass("io/github/benjaminamos/tracy/Tracy$ZoneContext"); - - TracyCZoneCtx zoneContext; - zoneContext.id = env->GetIntField(zoneContextObject, env->GetFieldID(zoneContextClass, "id", "I")); - zoneContext.active = env->GetIntField(zoneContextObject, env->GetFieldID(zoneContextClass, "active", "I")); - ___tracy_emit_zone_end(zoneContext); - } +#define TRACY_DELAYED_INIT +#define TRACY_MANUAL_LIFETIME +#define TRACY_NO_CRASH_HANDLER +#define TRACY_NO_CONTEXT_SWITCH +#define TRACY_NO_SAMPLING +#define TRACY_NO_FRAME_IMAGE +#define TRACY_ENABLE +#include "TracyClient.cpp" +#include "tracy/TracyC.h" + +#include "jni.h" + +extern "C" +{ + JNIEXPORT void JNICALL Java_io_github_benjaminamos_tracy_Tracy_startupProfiler(JNIEnv *env, jobject obj) + { + ___tracy_startup_profiler(); + } + + JNIEXPORT void JNICALL Java_io_github_benjaminamos_tracy_Tracy_shutdownProfiler(JNIEnv *env, jobject obj) + { + ___tracy_shutdown_profiler(); + } + + JNIEXPORT jboolean JNICALL Java_io_github_benjaminamos_tracy_Tracy_isConnected(JNIEnv *env, jobject obj) + { + return ___tracy_connected(); + } + + JNIEXPORT void JNICALL Java_io_github_benjaminamos_tracy_Tracy_markFrame(JNIEnv *env, jobject obj) + { + ___tracy_emit_frame_mark(NULL); + } + + JNIEXPORT jlong JNICALL Java_io_github_benjaminamos_tracy_Tracy_allocSourceLocation(JNIEnv *env, jobject obj, jint line, jstring source, jstring function, jstring name, jint colour) + { + const char* sourceUTF8 = env->GetStringUTFChars(source, NULL); + const char* functionUTF8 = env->GetStringUTFChars(function, NULL); + const char* nameUTF8 = env->GetStringUTFChars(name, NULL); + + uint64_t handle = ___tracy_alloc_srcloc_name(line, sourceUTF8, env->GetStringUTFLength(source), functionUTF8, env->GetStringUTFLength(function), nameUTF8, env->GetStringUTFLength(name), colour); + + env->ReleaseStringUTFChars(source, sourceUTF8); + env->ReleaseStringUTFChars(function, functionUTF8); + env->ReleaseStringUTFChars(name, nameUTF8); + return handle; + } + + JNIEXPORT jobject JNICALL Java_io_github_benjaminamos_tracy_Tracy_zoneBegin(JNIEnv *env, jobject obj, jlong sourceLocation, int active) + { + TracyCZoneCtx zoneContext = ___tracy_emit_zone_begin_alloc(sourceLocation, active); + + jclass zoneContextClass = env->FindClass("io/github/benjaminamos/tracy/Tracy$ZoneContext"); + return env->NewObject(zoneContextClass, env->GetMethodID(zoneContextClass, "", "(II)V"), zoneContext.id, zoneContext.active); + } + + JNIEXPORT void JNICALL Java_io_github_benjaminamos_tracy_Tracy_zoneEnd(JNIEnv *env, jobject obj, jobject zoneContextObject) + { + jclass zoneContextClass = env->FindClass("io/github/benjaminamos/tracy/Tracy$ZoneContext"); + + TracyCZoneCtx zoneContext; + zoneContext.id = env->GetIntField(zoneContextObject, env->GetFieldID(zoneContextClass, "id", "I")); + zoneContext.active = env->GetIntField(zoneContextObject, env->GetFieldID(zoneContextClass, "active", "I")); + ___tracy_emit_zone_end(zoneContext); + } } \ No newline at end of file