Issue: Enable Kineto Profiler Support in JavaCPP PyTorch Bindings
HI @saudet
Problem Description
The PyTorch JavaCPP bindings do not expose the Kineto profiler functionality, making it impossible to generate Chrome trace JSON files from Java for performance profiling.
Verification
// Attempting to use Kineto profiler fails
ExperimentalConfig ecfg = new ExperimentalConfig();
BytePointer traceId = new BytePointer();
ProfilerConfig config = new ProfilerConfig(
ProfilerState.KINETO,
true, true, false, false, false,
ecfg, traceId
);
ActivityTypeSet activities = new ActivityTypeSet();
activities.insert(ActivityType.CPU);
activities.insert(ActivityType.CUDA);
prepareProfiler(config, activities);
// Result: profilerEnabled() returns false, profilerType() returns NONE
Same issue with NVTX, ITT, and CPU profiler modes - all fail with "NONE".
Root Cause
The PyTorch distribution bundled with JavaCPP (pytorch-*-linux-x86_64-gpu.jar) was compiled without the USE_KINETO=1 build flag. As a result:
torch::profiler namespace functions are stubbed out
prepareProfiler() always fails silently
- No JSON trace export possible
Impact
- No performance profiling for PyTorch models running from Java
- Cannot identify GPU kernel bottlenecks
- Cannot export to Chrome tracing UI (
chrome://tracing)
- No integration with PyTorch Profiler ecosystem
Solution: Add Kineto at Build Time
When building the PyTorch native libraries, include Kineto support:
# Base PyTorch build command (current)
python setup.py build
# With Kineto support (REQUIRED for profiler)
USE_KINETO=1 USE_CUDA=1 USE_NVTX=1 python setup.py build
Then repackage the JAR files:
# Package with Kineto
pip wheel . --no-deps --verbose -w wheelhouse/
Alternative: Minimal C++ JNI Wrapper (Workaround)
Since JavaCPP doesn't include Kineto symbols, here's a workaround using CUDA driver API directly:
C++ JNI Implementation (works standalone)
// TorchProfilerJNI.cpp
// Compile: g++ -shared -fPIC -I$CUDA/include -I$JAVA/include \
// -o libtorch_profiler_jni.so TorchProfilerJNI.cpp -lcuda -ldl
#include <jni.h>
#include <cuda.h>
#include <dlfcn.h>
#include <stdio.h>
#include <pthread.h>
static CUcontext g_context = nullptr;
static bool g_cudaInit = false;
static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
static long g_peakMemory = 0;
static long g_currentMemory = 0;
static bool initCUDA() {
if (g_cudaInit) return true;
CUresult err = cuInit(0);
if (err != CUDA_SUCCESS) return false;
CUdevice device;
err = cuDeviceGet(&device, 0);
if (err != CUDA_SUCCESS) return false;
err = cuCtxCreate(&g_context, nullptr, 0, device);
if (err != CUDA_SUCCESS) return false;
g_cudaInit = true;
return true;
}
extern "C" {
JNIEXPORT jboolean JNICALL
Java_profiler_KinetoInit_nativeCudaAvailable(JNIEnv* env, jclass cls) {
return initCUDA() ? JNI_TRUE : JNI_FALSE;
}
JNIEXPORT jlong JNICALL
Java_profiler_KinetoInit_nativeGetUsedMemory(JNIEnv* env, jclass cls) {
if (!initCUDA()) return 0;
size_t free = 0, total = 0;
cuMemGetInfo(&free, &total);
return (jlong)(total - free);
}
JNIEXPORT jlong JNICALL
Java_profiler_KinetoInit_nativeAllocMemory(JNIEnv* env, jclass cls, jlong bytes) {
if (!initCUDA() || bytes <= 0) return 0;
CUdeviceptr ptr;
CUresult err = cuMemAlloc(&ptr, bytes);
if (err != CUDA_SUCCESS) return 0;
pthread_mutex_lock(&g_lock);
g_currentMemory += bytes;
if (g_currentMemory > g_peakMemory) g_peakMemory = g_currentMemory;
pthread_mutex_unlock(&g_lock);
return (jlong)ptr;
}
JNIEXPORT jboolean JNICALL
Java_profiler_KinetoInit_nativeFreeMemory(JNIEnv* env, jclass cls, jlong addr, jlong bytes) {
if (!initCUDA() || addr == 0) return JNI_FALSE;
CUresult err = cuMemFree((CUdeviceptr)addr);
if (err != CUDA_SUCCESS) return JNI_FALSE;
pthread_mutex_lock(&g_lock);
g_currentMemory -= bytes;
pthread_mutex_unlock(&g_lock);
return JNI_TRUE;
}
JNIEXPORT jlong JNICALL
Java_profiler_KinetoInit_nativeFindSymbol(JNIEnv* env, jclass cls, jstring lib, jstring symbol) {
const char* libName = lib ? env->GetStringUTFChars(lib, nullptr) : nullptr;
const char* symName = symbol ? env->GetStringUTFChars(symbol, nullptr) : nullptr;
void* handle = RTLD_DEFAULT;
if (libName) {
handle = dlopen(libName, RTLD_NOLOAD);
if (!handle) handle = dlopen(libName, RTLD_LAZY | RTLD_GLOBAL);
}
void* addr = handle ? dlsym(handle, symName) : nullptr;
if (lib) env->ReleaseStringUTFChars(lib, libName);
if (symbol) env->ReleaseStringUTFChars(symbol, symName);
return (jlong)(intptr_t)addr;
}
JNIEXPORT jboolean JNICALL
Java_profiler_KinetoInit_nativeInitKineto(JNIEnv* env, jclass cls) {
const char* syms[] = {
"_ZN5torch18global_kineto_initEv",
"_ZN6kineto16initKinetoOrThrowEv"
};
for (const char* sym : syms) {
void* fn = dlsym(RTLD_DEFAULT, sym);
if (fn) {
typedef void(*fn_t)();
((fn_t)fn)();
return JNI_TRUE;
}
}
return JNI_FALSE;
}
} // extern "C"
Java Interface
package profiler;
import java.io.*;
/**
* Kineto + CUDA ?? Profiler JNI ??
* ???? CUDA driver API ? PyTorch profiler C++ ??
*/
public class KinetoInit {
static {
try {
System.load("/home/muller/IdeaProjects/cuda-triton-test/src/main/resources/libtorch_profiler_jni.so");
System.out.println("? JNI Profiler ???? (CUDA + PyTorch)");
} catch (Throwable e) {
System.err.println("?? JNI ?????: " + e.getMessage());
}
}
// ==================== CUDA ???? (native) ====================
public static native boolean nativeCudaAvailable();
public static native long nativeGetTotalMemory();
public static native long nativeGetFreeMemory();
public static native long nativeGetUsedMemory();
public static native long nativeGetPeakMemory();
public static native long nativeAllocMemory(long bytes);
public static native boolean nativeFreeMemory(long addr, long bytes);
public static native void nativeResetPeakMemory();
public static native int nativeGetRecordCount();
public static native void nativeGetRecords(long[] arr);
public static native String nativeGetDeviceName();
public static native int[] nativeGetComputeCapability();
// ==================== PyTorch Profiler ???? (native) ====================
public static native long nativeFindSymbol(String lib, String symbol);
public static native boolean nativeInitKineto();
public static native String nativeGetProfilerState();
public static native void nativeLogOperation(String name, long startUs, long endUs);
// ==================== ???? ====================
public static String generateKinetoReport() {
StringBuilder sb = new StringBuilder();
sb.append("=== Kineto/CUDA Profiler ?? ===\n\n");
// CUDA ??
boolean cudaAvail = nativeCudaAvailable();
sb.append("CUDA ??: ").append(cudaAvail).append("\n");
if (cudaAvail) {
try {
long total = nativeGetTotalMemory();
long used = nativeGetUsedMemory();
long peak = nativeGetPeakMemory();
String name = nativeGetDeviceName();
int[] cc = nativeGetComputeCapability();
sb.append("GPU ??: ").append(name).append("\n");
if (cc != null && cc.length >= 2) {
sb.append("????: ").append(cc[0]).append(".").append(cc[1]).append("\n");
}
sb.append("????: ").append(formatBytes(total)).append("\n");
sb.append("????: ").append(formatBytes(used)).append("\n");
sb.append("????: ").append(formatBytes(peak)).append("\n");
} catch (Exception e) {
sb.append("??????: ").append(e.getMessage()).append("\n");
}
}
// PyTorch Kineto ????
sb.append("\n--- PyTorch Kineto ?? ---\n");
String[] libs = {"libtorch_cpu.so", "libtorch_cuda.so", "libtorch.so"};
String[] syms = {
"_ZN5torch18global_kineto_initEv",
"_ZN6kineto16initKinetoOrThrowEv",
"libkineto_init",
"_ZN5torch8profiler17kineto_init_impl20KinetoLibsEnvironmentB5cxx11E"
};
boolean kinetoFound = false;
for (String lib : libs) {
for (String sym : syms) {
long addr = nativeFindSymbol(lib, sym);
if (addr != 0) {
sb.append("? ??: ").append(sym).append(" @ 0x").append(Long.toHexString(addr)).append("\n");
kinetoFound = true;
break;
}
}
}
if (!kinetoFound) {
sb.append("? ??? Kineto ?????\n");
sb.append(" (?? PyTorch ????? USE_KINETO=1)\n");
}
// Profiler ??
String state = nativeGetProfilerState();
sb.append("\n--- Profiler ?? ---\n");
sb.append(state).append("\n");
return sb.toString();
}
public static String[] getMemoryReport() {
String[] report = new String[5];
report[0] = "CUDA: " + nativeCudaAvailable();
report[1] = "??: " + formatBytes(nativeGetTotalMemory());
report[2] = "??: " + formatBytes(nativeGetUsedMemory());
report[3] = "??: " + formatBytes(nativeGetPeakMemory());
report[4] = "??: " + nativeGetDeviceName();
return report;
}
public static void allocAndTrack(String label, long bytes) {
long addr = nativeAllocMemory(bytes);
System.out.printf("[Alloc] %-20s | %12s | @ 0x%x%n",
label, formatBytes(bytes), addr);
}
public static void freeAndTrack(long addr, long bytes) {
boolean ok = nativeFreeMemory(addr, bytes);
System.out.printf("[Free] addr=0x%x | %12s | %s%n",
addr, formatBytes(bytes), ok ? "OK" : "FAIL");
}
private static String formatBytes(long bytes) {
if (bytes < 0) return "N/A";
if (bytes >= 1024L * 1024 * 1024) {
return String.format("%.2f GB", bytes / 1024.0 / 1024 / 1024);
} else if (bytes >= 1024 * 1024) {
return String.format("%.2f MB", bytes / 1024.0 / 1024);
} else if (bytes >= 1024) {
return String.format("%.2f KB", bytes / 1024.0);
}
return bytes + " B";
}
public static void main(String[] args) {
System.out.println(generateKinetoReport());
}
}
the example
package profiler;
import java.io.*;
/**
* Kineto + CUDA ?? Profiler JNI ??
* ???? CUDA driver API ? PyTorch profiler C++ ??
*/
public class KinetoInit {
static {
try {
System.load("/home/muller/IdeaProjects/cuda-triton-test/src/main/resources/libtorch_profiler_jni.so");
System.out.println("? JNI Profiler ???? (CUDA + PyTorch)");
} catch (Throwable e) {
System.err.println("?? JNI ?????: " + e.getMessage());
}
}
// ==================== CUDA ???? (native) ====================
public static native boolean nativeCudaAvailable();
public static native long nativeGetTotalMemory();
public static native long nativeGetFreeMemory();
public static native long nativeGetUsedMemory();
public static native long nativeGetPeakMemory();
public static native long nativeAllocMemory(long bytes);
public static native boolean nativeFreeMemory(long addr, long bytes);
public static native void nativeResetPeakMemory();
public static native int nativeGetRecordCount();
public static native void nativeGetRecords(long[] arr);
public static native String nativeGetDeviceName();
public static native int[] nativeGetComputeCapability();
// ==================== PyTorch Profiler ???? (native) ====================
public static native long nativeFindSymbol(String lib, String symbol);
public static native boolean nativeInitKineto();
public static native String nativeGetProfilerState();
public static native void nativeLogOperation(String name, long startUs, long endUs);
// ==================== ???? ====================
public static String generateKinetoReport() {
StringBuilder sb = new StringBuilder();
sb.append("=== Kineto/CUDA Profiler ?? ===\n\n");
// CUDA ??
boolean cudaAvail = nativeCudaAvailable();
sb.append("CUDA ??: ").append(cudaAvail).append("\n");
if (cudaAvail) {
try {
long total = nativeGetTotalMemory();
long used = nativeGetUsedMemory();
long peak = nativeGetPeakMemory();
String name = nativeGetDeviceName();
int[] cc = nativeGetComputeCapability();
sb.append("GPU ??: ").append(name).append("\n");
if (cc != null && cc.length >= 2) {
sb.append("????: ").append(cc[0]).append(".").append(cc[1]).append("\n");
}
sb.append("????: ").append(formatBytes(total)).append("\n");
sb.append("????: ").append(formatBytes(used)).append("\n");
sb.append("????: ").append(formatBytes(peak)).append("\n");
} catch (Exception e) {
sb.append("??????: ").append(e.getMessage()).append("\n");
}
}
// PyTorch Kineto ????
sb.append("\n--- PyTorch Kineto ?? ---\n");
String[] libs = {"libtorch_cpu.so", "libtorch_cuda.so", "libtorch.so"};
String[] syms = {
"_ZN5torch18global_kineto_initEv",
"_ZN6kineto16initKinetoOrThrowEv",
"libkineto_init",
"_ZN5torch8profiler17kineto_init_impl20KinetoLibsEnvironmentB5cxx11E"
};
boolean kinetoFound = false;
for (String lib : libs) {
for (String sym : syms) {
long addr = nativeFindSymbol(lib, sym);
if (addr != 0) {
sb.append("? ??: ").append(sym).append(" @ 0x").append(Long.toHexString(addr)).append("\n");
kinetoFound = true;
break;
}
}
}
if (!kinetoFound) {
sb.append("? ??? Kineto ?????\n");
sb.append(" (?? PyTorch ????? USE_KINETO=1)\n");
}
// Profiler ??
String state = nativeGetProfilerState();
sb.append("\n--- Profiler ?? ---\n");
sb.append(state).append("\n");
return sb.toString();
}
public static String[] getMemoryReport() {
String[] report = new String[5];
report[0] = "CUDA: " + nativeCudaAvailable();
report[1] = "??: " + formatBytes(nativeGetTotalMemory());
report[2] = "??: " + formatBytes(nativeGetUsedMemory());
report[3] = "??: " + formatBytes(nativeGetPeakMemory());
report[4] = "??: " + nativeGetDeviceName();
return report;
}
public static void allocAndTrack(String label, long bytes) {
long addr = nativeAllocMemory(bytes);
System.out.printf("[Alloc] %-20s | %12s | @ 0x%x%n",
label, formatBytes(bytes), addr);
}
public static void freeAndTrack(long addr, long bytes) {
boolean ok = nativeFreeMemory(addr, bytes);
System.out.printf("[Free] addr=0x%x | %12s | %s%n",
addr, formatBytes(bytes), ok ? "OK" : "FAIL");
}
private static String formatBytes(long bytes) {
if (bytes < 0) return "N/A";
if (bytes >= 1024L * 1024 * 1024) {
return String.format("%.2f GB", bytes / 1024.0 / 1024 / 1024);
} else if (bytes >= 1024 * 1024) {
return String.format("%.2f MB", bytes / 1024.0 / 1024);
} else if (bytes >= 1024) {
return String.format("%.2f KB", bytes / 1024.0);
}
return bytes + " B";
}
public static void main(String[] args) {
System.out.println(generateKinetoReport());
}
}
the report
PyTorch/CUDA Profiler Report
Timestamp: Sat May 30 13:40:06 CST 2026
--- 1. CUDA Basic Info ---
CUDA Available: true
GPU: NVIDIA GeForce RTX 4060 Laptop GPU
CUDA Version: 8.9
Total Memory: 7.62 GB
Used Memory: 114.00 MB
Free Memory: 0 B
--- 2. PyTorch Kineto Profiler ---
Enable PyTorch Kineto Profiler
--- 3. CUDA Memory Stats (Native) ---
Current Used Memory: 114.00 MB
Memory Operations:
alloc(64MB) | 64.00 MB | 767 µs
alloc(32MB) | 32.00 MB | 535 µs
free(64MB) | -64.00 MB | 584 µs
alloc(16MB) | 16.00 MB | 596 µs
Peak Memory: 114.00 MB
Reserved Memory: 96.00 MB
Detailed CUDA Memory Records
--- 4. Kineto Config ---
Enabled: false, Mode: NONE
Kineto Profiler Disabled
--- NVTX Config ---
Enabled: false, Mode: NONE
--- ITT Config ---
Enabled: false, Mode: NONE
--- CPU Profiling ---
--- 5. RecordFunction ---
--- 6. CUDA Kernel Stats (Java Fallback) ---
CUDA Kernel Execution:
randn(2048x2048) | 252477 µs
mm | 151499 µs
add+relu | 72948 µs
mm(b,b) | 970 µs
Detailed CUDA Kernel Records
--- 7. Profiler Summary ---
Profiler Status:
KINETO: Disabled
NVTX: Disabled
ITT: Disabled
CPU: Disabled
Overall Profiler Information
Final Used Memory: 96.00 MB
how out generate so
// TorchProfilerJNI.cpp
// C++ JNI 实现:直接调用 PyTorch/CUDA 原生 Profiler API
// 编译: g++ -shared -fPIC -O2 -I/usr/local/cuda/include -I$JAVA_HOME/include -I$JAVA_HOME/include/linux \
// -o libtorch_profiler_jni.so TorchProfilerJNI.cpp -L/usr/local/cuda/lib64 -lcuda -ldl
#include <jni.h>
#include <dlfcn.h>
#include <cuda.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <sys/time.h>
#include <pthread.h>
// ==================== 工具宏 ====================
#define CUDA_DRIVER_CALL(call) \
do { \
CUresult err = call; \
if (err != CUDA_SUCCESS) { \
const char* errstr; \
cuGetErrorString(err, &errstr); \
fprintf(stderr, "[CUDA] %s:%d: %s\n", __FILE__, __LINE__, errstr); \
} \
} while(0)
// ==================== 全局状态 ====================
static CUcontext g_cudaContext = nullptr;
static bool g_cudaInit = false;
static pthread_mutex_t g_memoryLogMutex = PTHREAD_MUTEX_INITIALIZER;
// 内存操作记录
struct MemOpRecord {
char name[128];
long bytes;
long addr;
long timestamp_us;
int type; // 0=alloc, 1=free
};
static MemOpRecord g_memOps[1024];
static int g_memOpCount = 0;
static long g_currentMemory = 0;
static long g_peakMemory = 0;
// 获取当前时间(微秒)
static long getTimeUs() {
struct timeval tv;
gettimeofday(&tv, nullptr);
return (long)tv.tv_sec * 1000000L + tv.tv_usec;
}
// ==================== CUDA 初始化 ====================
static bool initCUDA() {
if (g_cudaInit) return true;
CUresult err = cuInit(0);
if (err != CUDA_SUCCESS) {
fprintf(stderr, "[CUDA] cuInit failed: %d\n", err);
return false;
}
int deviceId = 0;
CUdevice device;
err = cuDeviceGet(&device, deviceId);
if (err != CUDA_SUCCESS) {
fprintf(stderr, "[CUDA] cuDeviceGet failed: %d\n", err);
return false;
}
err = cuCtxCreate(&g_cudaContext, nullptr, 0, device);
if (err != CUDA_SUCCESS) {
fprintf(stderr, "[CUDA] cuCtxCreate failed: %d\n", err);
return false;
}
g_cudaInit = true;
fprintf(stdout, "[JNI CUDA] 初始化成功,设备 %d,context %p\n", deviceId, (void*)g_cudaContext);
return true;
}
// ==================== JNI: CUDA 内存查询 ====================
extern "C" {
// 检查 CUDA 可用性
JNIEXPORT jboolean JNICALL
Java_profiler_KinetoInit_nativeCudaAvailable(JNIEnv* env, jclass cls) {
if (!initCUDA()) return JNI_FALSE;
return JNI_TRUE;
}
// 获取 GPU 显存总量 (bytes)
JNIEXPORT jlong JNICALL
Java_profiler_KinetoInit_nativeGetTotalMemory(JNIEnv* env, jclass cls) {
if (!initCUDA()) return 0;
CUdevice device;
CUresult err = cuDeviceGet(&device, 0);
if (err != CUDA_SUCCESS) return 0;
size_t total = 0;
err = cuDeviceTotalMem(&total, device);
if (err != CUDA_SUCCESS) return 0;
return (jlong)total;
}
// 获取 GPU 显存可用量 (bytes)
JNIEXPORT jlong JNICALL
Java_profiler_KinetoInit_nativeGetFreeMemory(JNIEnv* env, jclass cls) {
if (!initCUDA()) return 0;
size_t free = 0, total = 0;
CUresult err = cuMemGetInfo(&free, &total);
if (err != CUDA_SUCCESS) return 0;
return (jlong)free;
}
// 获取 GPU 显存已用量 (bytes)
JNIEXPORT jlong JNICALL
Java_profiler_KinetoInit_nativeGetUsedMemory(JNIEnv* env, jclass cls) {
if (!initCUDA()) return 0;
size_t free = 0, total = 0;
CUresult err = cuMemGetInfo(&free, &total);
if (err != CUDA_SUCCESS) return 0;
return (jlong)(total - free);
}
// 获取当前峰值显存
JNIEXPORT jlong JNICALL
Java_profiler_KinetoInit_nativeGetPeakMemory(JNIEnv* env, jclass cls) {
pthread_mutex_lock(&g_memoryLogMutex);
long peak = g_peakMemory;
pthread_mutex_unlock(&g_memoryLogMutex);
return peak;
}
// 分配 CUDA 显存并追踪
JNIEXPORT jlong JNICALL
Java_profiler_KinetoInit_nativeAllocMemory(JNIEnv* env, jclass cls, jlong bytes) {
if (!initCUDA() || bytes <= 0) return 0;
CUdeviceptr ptr;
CUresult err = cuMemAlloc(&ptr, (size_t)bytes);
if (err != CUDA_SUCCESS) {
fprintf(stderr, "[CUDA] cuMemAlloc failed for %ld bytes: %d\n", (long)bytes, err);
return 0;
}
pthread_mutex_lock(&g_memoryLogMutex);
g_currentMemory += bytes;
if (g_currentMemory > g_peakMemory) {
g_peakMemory = g_currentMemory;
}
if (g_memOpCount < 1024) {
MemOpRecord* rec = &g_memOps[g_memOpCount++];
strcpy(rec->name, "alloc");
rec->bytes = bytes;
rec->addr = (long)ptr;
rec->timestamp_us = getTimeUs();
rec->type = 0;
}
pthread_mutex_unlock(&g_memoryLogMutex);
fprintf(stdout, "[JNI CUDA] 分配 %ld bytes @ 0x%lx, 当前: %ld bytes\n",
(long)bytes, (long)ptr, g_currentMemory);
return (jlong)ptr;
}
// 释放 CUDA 显存并追踪
JNIEXPORT jboolean JNICALL
Java_profiler_KinetoInit_nativeFreeMemory(JNIEnv* env, jclass cls, jlong addr, jlong bytes) {
if (!initCUDA() || addr == 0) return JNI_FALSE;
CUdeviceptr ptr = (CUdeviceptr)addr;
CUresult err = cuMemFree(ptr);
if (err != CUDA_SUCCESS) {
fprintf(stderr, "[CUDA] cuMemFree failed for 0x%lx: %d\n", (long)addr, err);
return JNI_FALSE;
}
pthread_mutex_lock(&g_memoryLogMutex);
g_currentMemory -= bytes;
if (g_memOpCount < 1024) {
MemOpRecord* rec = &g_memOps[g_memOpCount++];
strcpy(rec->name, "free");
rec->bytes = bytes;
rec->addr = (long)addr;
rec->timestamp_us = getTimeUs();
rec->type = 1;
}
pthread_mutex_unlock(&g_memoryLogMutex);
fprintf(stdout, "[JNI CUDA] 释放 0x%lx (%ld bytes), 当前: %ld bytes\n",
(long)addr, (long)bytes, g_currentMemory);
return JNI_TRUE;
}
// 重置峰值追踪
JNIEXPORT void JNICALL
Java_profiler_KinetoInit_nativeResetPeakMemory(JNIEnv* env, jclass cls) {
pthread_mutex_lock(&g_memoryLogMutex);
g_peakMemory = 0;
pthread_mutex_unlock(&g_memoryLogMutex);
}
// 获取操作记录数量
JNIEXPORT jint JNICALL
Java_profiler_KinetoInit_nativeGetRecordCount(JNIEnv* env, jclass cls) {
pthread_mutex_lock(&g_memoryLogMutex);
int count = g_memOpCount;
pthread_mutex_unlock(&g_memoryLogMutex);
return count;
}
// 获取操作记录(填充到 Java 的 long[] 数组)
// 格式: [addr, bytes, timestamp_us, type, addr, bytes, ...]
JNIEXPORT void JNICALL
Java_profiler_KinetoInit_nativeGetRecords(JNIEnv* env, jclass cls, jlongArray arr) {
pthread_mutex_lock(&g_memoryLogMutex);
jsize len = env->GetArrayLength(arr);
jlong* buf = env->GetLongArrayElements(arr, nullptr);
int count = g_memOpCount < (int)(len / 4) ? g_memOpCount : (int)(len / 4);
for (int i = 0; i < count; i++) {
MemOpRecord* rec = &g_memOps[i];
buf[i*4 + 0] = rec->addr;
buf[i*4 + 1] = rec->bytes;
buf[i*4 + 2] = rec->timestamp_us;
buf[i*4 + 3] = rec->type;
}
env->ReleaseLongArrayElements(arr, buf, 0);
pthread_mutex_unlock(&g_memoryLogMutex);
}
// 获取 GPU 设备名称
JNIEXPORT jstring JNICALL
Java_profiler_KinetoInit_nativeGetDeviceName(JNIEnv* env, jclass cls) {
if (!initCUDA()) return env->NewStringUTF("Unknown");
CUdevice device;
CUresult err = cuDeviceGet(&device, 0);
if (err != CUDA_SUCCESS) return env->NewStringUTF("Unknown");
char name[256];
err = cuDeviceGetName(name, sizeof(name), device);
if (err != CUDA_SUCCESS) return env->NewStringUTF("Unknown");
return env->NewStringUTF(name);
}
// ==================== JNI: PyTorch Profiler 符号查找 ====================
// 查找符号地址
JNIEXPORT jlong JNICALL
Java_profiler_KinetoInit_nativeFindSymbol(JNIEnv* env, jclass cls, jstring lib, jstring symbol) {
const char* libName = nullptr;
const char* symName = nullptr;
if (lib != nullptr) {
libName = env->GetStringUTFChars(lib, nullptr);
}
if (symbol != nullptr) {
symName = env->GetStringUTFChars(symbol, nullptr);
}
void* handle = RTLD_DEFAULT;
if (libName != nullptr && strlen(libName) > 0) {
handle = dlopen(libName, RTLD_NOLOAD);
if (!handle) {
handle = dlopen(libName, RTLD_LAZY | RTLD_GLOBAL);
}
if (!handle) {
if (lib != nullptr) env->ReleaseStringUTFChars(lib, libName);
if (symbol != nullptr) env->ReleaseStringUTFChars(symbol, symName);
return 0;
}
}
void* addr = dlsym(handle, symName);
if (lib != nullptr) env->ReleaseStringUTFChars(lib, libName);
if (symbol != nullptr) env->ReleaseStringUTFChars(symbol, symName);
return (jlong)(intptr_t)addr;
}
// 尝试初始化 PyTorch Kineto
JNIEXPORT jboolean JNICALL
Java_profiler_KinetoInit_nativeInitKineto(JNIEnv* env, jclass cls) {
const char* libs[] = {"libtorch_cpu.so", "libtorch_cuda.so", "libtorch.so"};
const char* syms[] = {
"_ZN5torch18global_kineto_initEv",
"_ZN6kineto16initKinetoOrThrowEv",
"libkineto_init",
"_ZN5torch8profiler17kineto_init_impl20KinetoLibsEnvironmentB5cxx11E"
};
for (int li = 0; li < 3; li++) {
void* handle = dlopen(libs[li], RTLD_NOLOAD);
if (!handle) {
handle = dlopen(libs[li], RTLD_LAZY | RTLD_GLOBAL);
}
if (!handle) continue;
for (int si = 0; si < 4; si++) {
void* fn = dlsym(handle, syms[si]);
if (fn) {
fprintf(stdout, "[JNI PyTorch] 找到符号 %s @ %p\n", syms[si], fn);
// 尝试调用(如果参数为 void)
typedef void(*fn_t)();
((fn_t)fn)();
return JNI_TRUE;
}
}
}
fprintf(stderr, "[JNI PyTorch] 未找到 Kineto 初始化符号\n");
return JNI_FALSE;
}
// 获取 PyTorch profiler 状态
JNIEXPORT jstring JNICALL
Java_profiler_KinetoInit_nativeGetProfilerState(JNIEnv* env, jclass cls) {
char buf[512];
snprintf(buf, sizeof(buf),
"CUDA init: %s | Peak memory: %ld bytes | Current ops: %d",
g_cudaInit ? "yes" : "no",
g_peakMemory,
g_memOpCount);
return env->NewStringUTF(buf);
}
// 记录一次操作耗时
JNIEXPORT void JNICALL
Java_profiler_KinetoInit_nativeLogOperation(JNIEnv* env, jclass cls, jstring name, jlong startUs, jlong endUs) {
const char* opName = env->GetStringUTFChars(name, nullptr);
fprintf(stdout, "[OP] %s | %ld µs\n", opName, (long)(endUs - startUs));
env->ReleaseStringUTFChars(name, opName);
}
// 获取 CUDA 计算能力
JNIEXPORT jintArray JNICALL
Java_profiler_KinetoInit_nativeGetComputeCapability(JNIEnv* env, jclass cls) {
if (!initCUDA()) return nullptr;
CUdevice device;
if (cuDeviceGet(&device, 0) != CUDA_SUCCESS) return nullptr;
int major = 0, minor = 0;
cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, device);
cuDeviceGetAttribute(&minor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, device);
jintArray result = env->NewIntArray(2);
jint vals[2] = {major, minor};
env->SetIntArrayRegion(result, 0, 2, vals);
return result;
}
} // extern "C"
Request
Please rebuild the PyTorch JavaCPP distributions with:
USE_KINETO=1 - Required for profiler API
USE_NVTX=1 - For NVIDIA Tools Extension
- Consider adding
USE_CUPTI=1 - For hardware counters
Alternatively, provide pre-built "profiler-enabled" distributions as optional artifacts.
References
Issue: Enable Kineto Profiler Support in JavaCPP PyTorch Bindings
HI @saudet
Problem Description
The PyTorch JavaCPP bindings do not expose the Kineto profiler functionality, making it impossible to generate Chrome trace JSON files from Java for performance profiling.
Verification
Same issue with NVTX, ITT, and CPU profiler modes - all fail with "NONE".
Root Cause
The PyTorch distribution bundled with JavaCPP (
pytorch-*-linux-x86_64-gpu.jar) was compiled without theUSE_KINETO=1build flag. As a result:torch::profilernamespace functions are stubbed outprepareProfiler()always fails silentlyImpact
chrome://tracing)Solution: Add Kineto at Build Time
When building the PyTorch native libraries, include Kineto support:
Then repackage the JAR files:
Alternative: Minimal C++ JNI Wrapper (Workaround)
Since JavaCPP doesn't include Kineto symbols, here's a workaround using CUDA driver API directly:
C++ JNI Implementation (works standalone)
Java Interface
the example
the report
how out generate so
Request
Please rebuild the PyTorch JavaCPP distributions with:
USE_KINETO=1- Required for profiler APIUSE_NVTX=1- For NVIDIA Tools ExtensionUSE_CUPTI=1- For hardware countersAlternatively, provide pre-built "profiler-enabled" distributions as optional artifacts.
References