阿里云认证网站建设题库,网站开发一般用哪种语言,友链交换网站,网站建设 超薄网络以前#xff0c;我曾说过#xff0c; 您总是想保留一些调试器断点作为例外 。 此帮助可防止代码在不引起注意的情况下腐烂掉-有时掩盖了另一个问题。 如果您认真对待这一点#xff0c;则最好将此概念扩展到自动化测试中。 但是想出一个全面的解决方案并不简单。 您可以仅从… 以前我曾说过 您总是想保留一些调试器断点作为例外 。 此帮助可防止代码在不引起注意的情况下腐烂掉-有时掩盖了另一个问题。 如果您认真对待这一点则最好将此概念扩展到自动化测试中。 但是想出一个全面的解决方案并不简单。 您可以仅从try / catch开始但不会捕获其他线程上的异常。 您还可以使用AOP进行操作 但是根据框架的不同您不能保证完全捕获所有内容这确实意味着您正在使用稍微不同的代码进行测试这将使您有些担忧。 几天前我遇到了有关如何编写自己的调试器的博客文章我想知道java进程是否有可能自行调试。 事实证明可以的这是我作为这个小小的思想实验的一部分提出的代码。 该类的第一部分仅包含一些相当hacky的代码用于根据启动参数来猜测连接回同一VM所需的端口。 可能可以使用Attach机制启动调试器。 但是我没有看到一种明显的方法来使其工作。 然后只有几个工厂方法带有要查找的异常列表。 package com.kingsfleet.debug;import com.sun.jdi.Bootstrap;
import com.sun.jdi.ReferenceType;
import com.sun.jdi.VirtualMachine;
import com.sun.jdi.connect.AttachingConnector;
import com.sun.jdi.connect.Connector;
import com.sun.jdi.connect.IllegalConnectorArgumentsException;
import com.sun.jdi.event.ClassPrepareEvent;
import com.sun.jdi.event.Event;
import com.sun.jdi.event.EventQueue;
import com.sun.jdi.event.EventSet;
import com.sun.jdi.event.ExceptionEvent;
import com.sun.jdi.event.VMDeathEvent;
import com.sun.jdi.event.VMDisconnectEvent;
import com.sun.jdi.request.ClassPrepareRequest;
import com.sun.jdi.request.EventRequest;
import com.sun.jdi.request.ExceptionRequest;import java.io.IOException;import java.lang.management.ManagementFactory;
import java.lang.management.RuntimeMXBean;import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;public class ExceptionDebugger implements AutoCloseable {public static int getDebuggerPort() {// Try to work out what port we need to connect toRuntimeMXBean runtime ManagementFactory.getRuntimeMXBean();ListString inputArguments runtime.getInputArguments();int port -1;boolean isjdwp false;for (String next : inputArguments) {if (next.startsWith(-agentlib:jdwp)) {isjdwp true;String parameterString next.substring(-agentlib:jdwp.length());String[] parameters parameterString.split(,);for (String parameter : parameters) {if (parameter.startsWith(address)) {int portDelimeter parameter.lastIndexOf(:);if (portDelimeter ! -1) {port Integer.parseInt(parameter.substring(portDelimeter 1));} else {port Integer.parseInt(parameter.split()[1]);}}}}}return port;}public static ExceptionDebugger connect(final String... exceptions) throws InterruptedException {return connect(getDebuggerPort(),exceptions);}public static ExceptionDebugger connect(final int port, final String... exceptions) throws InterruptedException {ExceptionDebugger ed new ExceptionDebugger(port, exceptions);return ed;} 构造函数创建一个简单的守护程序线程以启动与虚拟机的连接。 这是一个单独的线程这一点非常重要否则显然当我们遇到断点时VM会停止运行。 确保该线程中的代码不会引发异常是一个好主意-目前我只是希望做到最好。 最后代码仅维护了一个禁止的异常列表如果您有更多的时间应该可以在发生异常的地方存储堆栈跟踪。 // // Instance variablesprivate final CountDownLatch startupLatch new CountDownLatch(1);private final CountDownLatch shutdownLatch new CountDownLatch(1);private final SetString set Collections.synchronizedSet(new HashSetString());private final int port;private final String exceptions[];private Thread debugger;private volatile boolean shutdown false;//// Object construction and methods//private ExceptionDebugger(final int port, final String... exceptions) throws InterruptedException {this.port port;this.exceptions exceptions;debugger new Thread(new Runnable() {Overridepublic void run() {try {connect();} catch (Exception ex) {ex.printStackTrace();}}}, Self debugging);debugger.setDaemon(true); // Dont hold the VM opendebugger.start();// Make sure the debugger has connectedif (!startupLatch.await(1, TimeUnit.MINUTES)) {throw new IllegalStateException(Didnt connect before timeout);}}Overridepublic void close() throws InterruptedException {shutdown true;// Somewhere in JDI the interrupt was being eaten, hence the volatile flag debugger.interrupt();shutdownLatch.await();}/*** return A list of exceptions that were thrown*/public SetString getExceptionsViolated() {return new HashSetString(set);}/*** Clear the list of exceptions violated*/public void clearExceptionsViolated() {set.clear();} 主要的connect方法是一个相当简单的代码块可确保连接并配置任何初始断点。 //// Implementation details//private void connect() throws java.io.IOException {try {// Create a virtual machine connectionVirtualMachine attach connectToVM();try{// Add prepare and any already loaded exception breakpointscreateInitialBreakpoints(attach);// We can now allow the rest of the work to go on as we have created the breakpoints// we requiredstartupLatch.countDown();// Process the eventsprocessEvents(attach);}finally {// Disconnect the debuggerattach.dispose();// Give the debugger time to really disconnect// before we might reconnect, couldnt find another// way to do thistry {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {Thread.currentThread().interrupt();}}} finally {// Notify watchers that we have shutdownshutdownLatch.countDown();}} 重新连接到自我只是找到合适的连接器的过程在本例中是Socket尽管我猜想如果您稍稍修改一下代码便可以在某些平台上使用共享内存传输。 private VirtualMachine connectToVM() throws java.io.IOException {ListAttachingConnector attachingConnectors Bootstrap.virtualMachineManager().attachingConnectors();AttachingConnector ac null;found:for (AttachingConnector next : attachingConnectors) {if (next.name().contains(SocketAttach)) {ac next;break;}}MapString, Connector.Argument arguments ac.defaultArguments();arguments.get(hostname).setValue(localhost);arguments.get(port).setValue(Integer.toString(port));arguments.get(timeout).setValue(4000);try {return ac.attach(arguments);} catch (IllegalConnectorArgumentsException e) {throw new IOException(Problem connecting to debugger,e);}} 连接调试器时您不知道是否已加载您感兴趣的异常因此您需要为准备类的点和已经加载的点注册断点。 请注意设置的断点仅用于断开一个线程的策略–否则出于显而易见的原因如果调试器线程也进入睡眠状态则当前VM将停止运行。 private void createInitialBreakpoints(VirtualMachine attach) {// Our first exception is for class loadingfor (String exception : exceptions) {ClassPrepareRequest cpr attach.eventRequestManager().createClassPrepareRequest();cpr.addClassFilter(exception);cpr.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);cpr.setEnabled(true);}// Then we can check each in turn to see if it have already been loaded as we might// be late to the game, remember classes can be loaded more than once//for (String exception : exceptions) {ListReferenceType types attach.classesByName(exception);for (ReferenceType type : types) {createExceptionRequest(attach, type);}}}private static void createExceptionRequest(VirtualMachine attach, ReferenceType refType) {ExceptionRequest er attach.eventRequestManager().createExceptionRequest(refType, true, true);er.setSuspendPolicy(EventRequest.SUSPEND_EVENT_THREAD);er.setEnabled(true);} 事件处理循环轮询包含一个或多个事件实例的EventSet实例。 尽管并非所有这些事件都属于断点请求所以您必须注意不要总是在事件集上调用简历。 这是因为您可能连续有两个事件集而代码甚至在阅读第二个事件集之前就调用了resume。 随着代码的赶上这会导致错过断点。 出于某种原因 JDI似乎正在吃掉中断的标志因此该布尔属性使用以前的close方法停止循环。 private void processEvents(VirtualMachine attach) {// Listen for eventsEventQueue eq attach.eventQueue();eventLoop: while (!Thread.interrupted() !shutdown) {// Poll for event sets, with a short timeout so that we can// be interrupted if requiredEventSet eventSet null;try {eventSet eq.remove(500);}catch (InterruptedException ex) {Thread.currentThread().interrupt();continue eventLoop; }// Just loop again if we have no eventsif (eventSet null) {continue eventLoop;}//boolean resume false;for (Event event : eventSet) {EventRequest request event.request();if (request ! null) {int eventPolicy request.suspendPolicy();resume | eventPolicy ! EventRequest.SUSPEND_NONE;}if (event instanceof VMDeathEvent || event instanceof VMDisconnectEvent) {// This should never happen as the VM will exit before this is called} else if (event instanceof ClassPrepareEvent) {// When an instance of the exception class is loaded attach an exception breakpointClassPrepareEvent cpe (ClassPrepareEvent) event;ReferenceType refType cpe.referenceType();createExceptionRequest(attach, refType);} else if (event instanceof ExceptionEvent) {String name ((ExceptionRequest)event.request()).exception().name();set.add(name);}}// Dangerous to call resume always because not all event suspend the VM// and events happen asynchornously.if (resume)eventSet.resume();}}} 因此剩下的只是一个简单的测试示例因为这是JDK 7而ExceptionDebugger是AutoCloseable我们可以使用try-with-resources构造进行此操作如下所示。 显然如果要进行自动化测试请使用您选择的测试框架固定装置。 public class Target {public static void main(String[] args) throws InterruptedException {try (ExceptionDebugger ex ExceptionDebugger.connect(NoClassDefFoundError.class.getName())) {doSomeWorkThatQuietlyThrowsAnException();System.out.println(ex.getExceptionsViolated());}System.exit(0);}private static void doSomeWorkThatQuietlyThrowsAnException() {// Check to see that break point gets firedtry {Thread t new Thread(new Runnable() {public void run() {try{throw new NoClassDefFoundError();}catch (Throwable ex) {}}});t.start();t.join();} catch (Throwable th) {// Eat this and dont tell anybody}}
} 因此如果使用以下VM参数运行此类请注意suspend n否则代码将不会开始运行您会发现它可以重新连接到自身并开始运行。 -agentlib:jdwptransportdt_socket,addresslocalhost:5656,servery,suspendn 这将为您提供以下输出请注意来自VM的额外调试行 Listening for transport dt_socket at address: 5656java.lang.NoClassDefFoundError
Listening for transport dt_socket at address: 5656 每个人都想读一下这是否对人们有用并有助于消除任何明显的错误。 参考在Gerard Davison的博客博客中从JCG合作伙伴 Gerard Davison 编写了一个自动调试器以在测试执行期间捕获异常 。 翻译自: https://www.javacodegeeks.com/2013/10/write-an-auto-debugger-to-catch-exceptions-during-test-execution.html