如何调试通过JNI从Java中使用的DLL。

huangapple go评论66阅读模式
英文:

How to debug a DLL that is being used from Java through JNI

问题

你好,StackOverflow的亲爱的用户们。我知道这个问题可能会有点不清楚,但我真的很迷茫,没有人可以讨论这个问题,我只能描述一下情况。

我在一家公司工作,他们使用C++创建一个库,一个DLL文件,这个DLL通过Java使用JNI来访问。我想要调试这个DLL以解决内存泄漏的问题,我使用了Visual Studio Memory ProfilerDeleaker。但问题是,它只是一个DLL(不是可执行文件),它是从Java进程(工作进程)中使用的。当我尝试调试使用DLL的Java工作进程时,调试器会一直中断在Java.exe和JVM.DLL进程中,不让我调试应用程序,因此没有任何工具能够收集足够的内存分配和释放信息来建议任何内存泄漏。当我将Java进程附加到Visual Studio时,调试器每秒都会中断,如下面的截图所示。

如何调试通过JNI从Java中使用的DLL。

我在这里找到了一个类似的问题:https://stackoverflow.com/questions/359851/debug-a-dll-from-java-java-jni-dll-app-dll-app-dll 但没有帮助。

在这种情况下,应该如何调试内存泄漏?有没有可以在这里使用的工具?

英文:

Hello good people of StackOverflow. I know this question is going to be somewhat unclear, but I am really lost here and have no one with whom I can discuss this, I can only describe the scenario.

I am working for a company where they use C++ to create a library, a DLL file, this DLL is access through Java using JNI. I want to debug the DLL for memory leak issues for which I used Visual Studio Memory Profiler and Deleaker. But the problem is that it is just a DLL (not an executable) which is used from a Java process (worker process). When I do try debugging the Java worker process which is using the DLL the debugger keeps breaking in Java.exe and JVM.DLL process, and does not let me debug the application, so no tool is able to gather enough information about memory allocation and deallocation to suggest any memory leak. When I attach the Java process with Visual Studio, it debugger keeps breaking every second, as shown in the below screenshot.

如何调试通过JNI从Java中使用的DLL。

I found a similar question here https://stackoverflow.com/questions/359851/debug-a-dll-from-java-java-jni-dll-app-dll-app-dll but it did not help.

How should one debug the memory leak in such scenarios? Are there any tools that can be used here?

答案1

得分: 2

Here are the translated parts:

调试器在Java.exe和JVM.DLL进程中不断中断

调试器每秒中断一次

从您的屏幕截图中的堆栈跟踪来看,我会说断点发生在JIT编译的代码中,Visual Studio 无法解开它,因此您会得到这个断裂的堆栈跟踪。

JVM使用隐式异常分派,通常情况下会省略null检查,并执行内存访问。如果由此导致EXCEPTION_ACCESS_VIOLATION,它会在JVM的异常处理程序中转换为Java异常(另请参阅:https://shipilev.net/jvm/anatomy-quarks/25-implicit-null-checks/ 进行扩展描述)。在某些其他情况下,例如堆栈溢出和除零,也会执行相同操作。

但是,调试器也会首先捕获所有这些异常/信号。我个人总是继续(g)通过它们,直到我进入要调试的本机代码。但是出于分析目的,这可能过于繁琐。

我认为最直接的解决方案是在调试器中禁用对访问冲突的第一次机会异常处理。我认为正确的命令应该是 sxi av。并且对持续出现的其他异常执行相同操作。

您还可以通过使用-XX:+UnlockDiagnosticVMOptions -XX:-ImplicitNullChecks JVM标志来阻止JVM使用隐式null检查。但是我记得这不是完全防止调试器停止在JVM内部异常上的解决方案。

英文:

> the debugger keeps breaking in Java.exe and JVM.DLL process

> debugger keeps breaking every second

From the stack trace in your screen shot, I'd say the break is happening in JIT-compiled code, which Visual Studio can not unwind through, so you get this broken stack trace.

The JVM uses implicit exception dispatch, whereby often a null check is omitted, and a memory access is just done. If an EXCEPTION_ACCESS_VIOLATION occurs as a result, it is translated into a Java Exception in the JVMs exception handler. (See also: https://shipilev.net/jvm/anatomy-quarks/25-implicit-null-checks/ for an extended description). The same is done in some other cases such as stack overflow, and divide by zero.

But, the debugger will also catch all these exceptions/signals first. I personally always just continue (g) through them until I get to the native code I want to debug. But for profiling purposes, this might be too cumbersome.

I think the most straight forward solution is to just disable the first chance exception handling for access violations in the debugger. I believe the right command would be sxi av. And do the same for other exceptions that keep showing up.

You should also be able to prevent the JVM's use of implicit null checks by using the -XX:+UnlockDiagnosticVMOptions -XX:-ImplicitNullChecks JVM flags. But IIRC this is not a 100% solution to prevent the debugger from stopping on JVM internal exceptions.

答案2

得分: 0

你可以通过在Dll的代码中添加以下代码来实现:

__debugbreak();

这应该放在你怀疑存在内存泄漏的部分之前。你必须以调试模式编译你的Dll。

这个__debugbreak是一个内部函数,当程序执行到这一指令时,它会中断你的Java程序(实际上是Dll),然后会弹出一个对话框询问你是否要调试程序,你必须选择是,然后会被要求选择使用哪个调试器(Visual Studio)。

因此,你不需要将进程附加到调试器,相反,调试器(Visual Studio)将在达到__debugbreak时启动。

如果一切顺利,你将看到Dll的代码,并能够进行下一步调试(F10),并像通常一样进行调试。

英文:

You can do it by adding a

__debugbreak();

in the Dll's code, just before the part you suspect to have a memory leak. You must compile your Dll in debug mode.

This __debugbreak is an intrinsic that will cause your java program (in fact the Dll) to be interrupted when it reaches this instruction, you will see a dialogbox asking if you want to debug the program, you must say yes, then you will be asked to select which debugger to use (Visual Studio).

So, you won't attach the process to the debugger, instead the debugger (Visual Studio) will be launched when the __debugbreak is reached.

If everything goes well, you will se the Dll's code and you will be able to step to the next instruction (F10) and debug as usual.

huangapple
  • 本文由 发表于 2023年5月24日 18:33:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/76322573.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定