How to get the full stacktrace of a StackOverflowError without restarting the application

╄→гoц情女王★ 提交于 2021-01-28 11:14:31

问题


I currently hava a running Java application, which has a bug. I don't know how to fully reproduce it and it didn't happen for weeks until now. When it occurs one times, I can reproduce it over and over again easily until I restart the application. The bug causes a StackOverflowError because of a recursion and I don't know how this happens. The printed stacktrace of the StackOverflowError isn't helpful because it contains only the repeating part, but not the more insteresting initial part, because the JVM has a limit for stacktrace entries. The -XX:MaxJavaStackTraceDepth=... can be used to set this limit as explained here. The problem is that I think I have to restart my application in order to add this flag. But if I do so, I won't be able to reproduce the bug anymore. Is there any solution how I can get the full stacktrace or set this flag without restarting the application?


回答1:


I know at least two solutions.

  1. Create HotSpot Serviceability Agent tool to find the address of MaxJavaStackTraceDepth variable in memory, and then update the memory of the process using OS-specific mechanism.

  2. Attach a JVM TI agent that intercepts StackOverflowErrors and prints a stack trace right from the agent.

Here is the code for the first solution (as it is presumably shorter):

import sun.jvm.hotspot.debugger.Address;
import sun.jvm.hotspot.runtime.VM;
import sun.jvm.hotspot.tools.Tool;

import java.io.IOException;
import java.io.RandomAccessFile;

public class ChangeVMFlag extends Tool {
    private static String pid;

    @Override
    public void run() {
        Address addr = VM.getVM().getCommandLineFlag("MaxJavaStackTraceDepth").getAddress();
        long addrValue = VM.getVM().getDebugger().getAddressValue(addr);

        try (RandomAccessFile raf = new RandomAccessFile("/proc/" + pid + "/mem", "rw")) {
            raf.seek(addrValue);
            raf.writeInt(Integer.reverseBytes(1_000_000));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        pid = args[0];
        new ChangeVMFlag().execute(new String[]{pid});
    }
}

This tool changes the value of MaxJavaStackTraceDepth in the target process to 1 million.
Note: it uses Linux-specific /proc API to write into the target process' memory. Other OSes have different interfaces.

How to run

On JDK 8

java -cp .:$JAVA_HOME/lib/sa-jdi.jar ChangeVMFlag <pid>

On JDK 9+

java --add-modules=jdk.hotspot.agent \
     --add-exports jdk.hotspot.agent/sun.jvm.hotspot.tools=ALL-UNNAMED \
     --add-exports jdk.hotspot.agent/sun.jvm.hotspot.runtime=ALL-UNNAMED \
     --add-exports jdk.hotspot.agent/sun.jvm.hotspot.debugger=ALL-UNNAMED \
     ChangeVMFlag <pid>


来源:https://stackoverflow.com/questions/65239783/how-to-get-the-full-stacktrace-of-a-stackoverflowerror-without-restarting-the-ap

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!