如何中止在com.sun.jdi.ObjectReference上运行的长时间invokeMethod?

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

How to abort long running invokeMethod on com.sun.jdi.ObjectReference?

问题

我有自己的JDI调试器,它在某些对象上调用toString方法:

com.sun.jdi.ObjectReferenceobject object = ...
ThreadReference threadRef = frameProxy.threadProxy().getThreadReference();
Value value = object.invokeMethod(threadRef, toStringMethod,
                    Collections.EMPTY_LIST, ObjectReference.INVOKE_SINGLE_THREADED);

问题是,即使在toString()方法内没有设置断点,invokeMethod永远不会终止,因此我的调试器挂起。例如,当我在Double对象上调用时会发生这种情况。

如何在一定时间后终止invokeMethod的执行?

更新: 我尝试实现了自己的Double对象,并在toString()方法的开头和结尾放置了一些System.out.println()语句,似乎该方法执行得很好,但由于某种原因,调试器没有收到结果。也许这是JDI中的一个bug,因为有很多类似的bug,但我寻找解决方案,我只是寻找一种在invokeMethod()执行时间过长时中止执行的方法。

更新2: 我尝试了ThierryB建议的方法,但我只能在管理线程中调用frameProxy.threadProxy().stop(object);。而由于invokeMethod()的阻塞,管理线程被阻塞,因此不会执行我的命令。我尝试了这样的方式:

boolean[] isFinished = new boolean[2];
isFinished[0] = false;

DebuggerManagerThreadImpl managerThread = debugProcess.getManagerThread();
new Thread(() - > {
    try {
        Thread.sleep(2000);
        if (!isFinished[0]) {
            System.out.println("Invoked");
            managerThread.invokeCommand(new DebuggerCommand() {
                @Override
                public void action() {
                    try {
                        frameProxy.threadProxy().stop(object);
                    } catch (InvalidTypeException e) {
                        e.printStackTrace();
                    }
                    int threadStatus = frameProxy.threadProxy().status();
                    switch (threadStatus) {
                        case ThreadReference.THREAD_STATUS_RUNNING:
                            System.out.println("The thread is running.");
                            break;
                        case ThreadReference.THREAD_STATUS_ZOMBIE:
                            System.out.println("The thread has been completed.");
                            break;
                        case ThreadReference.THREAD_STATUS_WAIT:
                            System.out.println("The thread is waiting.");
                            break;
                        default:
                            System.out.println("The thread is not running / not waiting / not completed : but what is it doing right now ? (a little programmer joke ;) )");
                    }
                }

                @Override
                public void commandCancelled() {

                }
            });
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}).start();

Value value = object.invokeMethod(threadRef, toStringMethod,
    Collections.EMPTY_LIST, ObjectReference.INVOKE_SINGLE_THREADED);

isFinished[0] = true;

frameProxy.threadProxy().stop(object);永远不会执行,因为DebuggerCommandaction方法未被调用(线程被阻塞)。

另外,当我的调试器挂起并且我强制停止进程时,以下是堆栈跟踪:

com.sun.jdi.VMDisconnectedException
	at com.jetbrains.jdi.TargetVM.waitForReply(TargetVM.java:317)
	at com.jetbrains.jdi.VirtualMachineImpl.waitForTargetReply(VirtualMachineImpl.java:1170)
	at com.jetbrains.jdi.PacketStream.waitForReply(PacketStream.java:86)
	at com.jetbrains.jdi.JDWP$ObjectReference$InvokeMethod.waitForReply(JDWP.java:4840)
	at com.jetbrains.jdi.ObjectReferenceImpl.invokeMethod(ObjectReferenceImpl.java:413)

更新3: 在调用方法时使用哪个线程?目前我正在使用frameProxy.threadProxy().getThreadReference();,这在大多数情况下都很好,但是是否最好为对象上的方法调用创建一个单独的线程(除了我的JDI调试器外,我还在应用程序中有一个仪器代理,所以我可以专门为此用例创建一个线程(也许这将防止死锁?))。

更新4: 目前我正在使用suspend policySUSPEND_ALL,是否更好使用SUSPEND_EVENT_THREAD

英文:

I have my own JDI Debugger which calls the toString method on some objects:

com.sun.jdi.ObjectReferenceobject object = ...
ThreadReference threadRef = frameProxy.threadProxy().getThreadReference();
Value value = object.invokeMethod(threadRef, toStringMethod,
                    Collections.EMPTY_LIST, ObjectReference.INVOKE_SINGLE_THREADED);

The problem is that even if no breakpoints are set inside the toString() method, the invokeMethod never terminates so my debugger hangs. For example this happens when I call this on a Double object.

How can I kill the execution of invokeMethod after some duration of time?

Update: I tried implementing my own Double object and put some System.out.println() statements at the start and end of the toString() method and it seemed that the method is executed just fine but for some reason the debugger doesn't receive the result. Maybe this is a bug in JDI because there are many such bugs, but I am not looking for a solution for this, I am just looking for a way to abort the execution of invokeMethod() if it takes too much time.

Update2: I tried what ThierryB was suggesting but I can only invoke frameProxy.threadProxy().stop(object); in the manager thread. And the manager thread is blocked because of invokeMethod() so it won't execute my command. I tried something like this:

boolean[] isFinished = new boolean[2];
isFinished[0] = false;

DebuggerManagerThreadImpl managerThread = debugProcess.getManagerThread();
new Thread(() - > {
    try {
        Thread.sleep(2000);
        if (!isFinished[0]) {
            System.out.println("Invoked");
            managerThread.invokeCommand(new DebuggerCommand() {
                @Override
                public void action() {
                    try {
                        frameProxy.threadProxy().stop(object);
                    } catch (InvalidTypeException e) {
                        e.printStackTrace();
                    }
                    int threadStatus = frameProxy.threadProxy().status();
                    switch (threadStatus) {
                        case ThreadReference.THREAD_STATUS_RUNNING:
                            System.out.println("The thread is running.");
                            break;
                        case ThreadReference.THREAD_STATUS_ZOMBIE:
                            System.out.println("The thread has been completed.");
                            break;
                        case ThreadReference.THREAD_STATUS_WAIT:
                            System.out.println("The thread is waiting.");
                            break;
                        default:
                            System.out.println("The thread is not running / not waiting / not completed : but what is it doing right now ? (a little programmer joke ;) )");
                    }
                }

                @Override
                public void commandCancelled() {

                }
            });
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}).start();

Value value = object.invokeMethod(threadRef, toStringMethod,
    Collections.EMPTY_LIST, ObjectReference.INVOKE_SINGLE_THREADED);

isFinished[0] = true;

but frameProxy.threadProxy().stop(object); is never executed because the DebuggerCommand's action method is not called(thread is blocked).

Also this is a Stack trace when my Debugger hangs and I forcefully stop the process:

com.sun.jdi.VMDisconnectedException
at com.jetbrains.jdi.TargetVM.waitForReply(TargetVM.java:317)
at com.jetbrains.jdi.VirtualMachineImpl.waitForTargetReply(VirtualMachineImpl.java:1170)
at com.jetbrains.jdi.PacketStream.waitForReply(PacketStream.java:86)
at com.jetbrains.jdi.JDWP$ObjectReference$InvokeMethod.waitForReply(JDWP.java:4840)
at com.jetbrains.jdi.ObjectReferenceImpl.invokeMethod(ObjectReferenceImpl.java:413)

UPDATE 3: which thread to use to invoke the Method? Currently I am using frameProxy.threadProxy().getThreadReference(); which works fine most of the time, but is it better to create a separate thread just for invoking methods on objects(along my JDI debugger I also have an instrumentation agent inside the application so I can create a separate thread just for this use case (maybe this will prevent deadlocks?)).

UPDATE 4: Currently I am using SUSPEND_ALL as suspend policy, would it be better to use SUSPEND_EVENT_THREAD instead?

答案1

得分: 3

你可以使用接口 ThreadReference 的方法 void stop(ObjectReference throwable)。Javadoc API 中说明了“使用异步异常停止此线程”。

try {
    com.sun.jdi.ObjectReferenceobject object = ...
    ThreadReference threadRef = frameProxy.threadProxy().getThreadReference();
    frameProxy.threadProxy().stop(object);
    int threadStatus = frameProxy.threadProxy().status();
    switch(threadStatus) {
         case ThreadReference.THREAD_STATUS_RUNNING :
              log.info("线程正在运行。");
              break;
         case ThreadReference.THREAD_STATUS_ZOMBIE :
              log.info("线程已完成。");
              break;
         case ThreadReference.THREAD_STATUS_WAIT :
              log.info("线程正在等待。");
              break;
         default :
              log.info("线程未运行/未等待/未完成:但它现在在做什么?(一个小小的程序员笑话 ;) )");
    }
} catch (ClassNotLoadedException cnle) {
    // 记录异常或显示消息...
} catch (IncompatibleThreadStateException itse) {
    // 记录异常或显示消息...
} catch (InvalidTypeException ite) {
    // 记录异常或显示消息...
}

在使用 status 方法检查线程状态时,你可以找到以下值:

  • THREAD_STATUS_UNKNOWN
  • THREAD_STATUS_ZOMBIE
  • THREAD_STATUS_RUNNING
  • THREAD_STATUS_SLEEPING
  • THREAD_STATUS_MONITOR
  • THREAD_STATUS_WAIT
  • THREAD_STATUS_NOT_STARTED

希望你能根据我提供的信息找到实现你需要的方法。来自这里的示例或者Alvin Alexander网站上发布的测试套件也可能会有帮助。

英文:

You can use the interface ThreadReference with the method void stop(ObjectReference throwable). The javadoc api tells "Stops this thread with an asynchronous exception.".

try {
com.sun.jdi.ObjectReferenceobject object = ...
ThreadReference threadRef = frameProxy.threadProxy().getThreadReference();
frameProxy.threadProxy().stop(object);
int threadStatus = frameProxy.threadProxy().status();
switch(threadStatus) {
case ThreadReference.THREAD_STATUS_RUNNING :
log.info("The thread is running.");
break;
case ThreadReference.THREAD_STATUS_ZOMBIE :
log.info("The thread has been completed.");
break;
case ThreadReference.THREAD_STATUS_WAIT :
log.info("The thread is waiting.");
break;
default :
log.info("The thread is not running / not waiting / not completed : but what is it doing right now ? (a little programmer joke ;) )");
}
} catch (ClassNotLoadedException cnle) {
// log exception, or display a message...
} catch (IncompatibleThreadStateException itse) {
// log exception, or display a message...
} catch (InvalidTypeException ite) {
// log exception, or display a message...
}

When you are checking your thread status with the status method, you can find that values :

  • THREAD_STATUS_UNKNOWN
  • THREAD_STATUS_ZOMBIE
  • THREAD_STATUS_RUNNING
  • THREAD_STATUS_SLEEPING
  • THREAD_STATUS_MONITOR
  • THREAD_STATUS_WAIT
  • THREAD_STATUS_NOT_STARTED

Hope you will find a way to do what you need to with the stuff I have provided here.
Examples from here or the following suite test published on the website of Alvin Alexander could be helpfull too.

huangapple
  • 本文由 发表于 2020年8月18日 05:13:36
  • 转载请务必保留本文链接:https://go.coder-hub.com/63458738.html
匿名

发表评论

匿名网友

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

确定