Skip to content

core issue # Issue: Enable Kineto Profiler Support in JavaCPP PyTorch Bindings,need javacpp compile kineto part so to java libtorch_profiler_jni.so #1772

Description

@mullerhai

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

  1. No performance profiling for PyTorch models running from Java
  2. Cannot identify GPU kernel bottlenecks
  3. Cannot export to Chrome tracing UI (chrome://tracing)
  4. 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:

  1. USE_KINETO=1 - Required for profiler API
  2. USE_NVTX=1 - For NVIDIA Tools Extension
  3. Consider adding USE_CUPTI=1 - For hardware counters

Alternatively, provide pre-built "profiler-enabled" distributions as optional artifacts.

References

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions