编写自动调试器以在测试执行期间捕获异常

以前,我曾说过, 您总是想保留一些调试器断点作为例外 。 此帮助可防止代码在不引起注意的情况下腐烂掉-有时掩盖了另一个问题。

如果您认真对待这一点,则最好将此概念扩展到自动化测试中。 但是想出一个全面的解决方案并不简单。 您可以仅从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();List<String> 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 Set<String> set = Collections.synchronizedSet(new HashSet<String>());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); // Don't hold the VM opendebugger.start();// Make sure the debugger has connectedif (!startupLatch.await(1, TimeUnit.MINUTES)) {throw new IllegalStateException("Didn't 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 Set<String> getExceptionsViolated() {return new HashSet<String>(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, couldn't 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 {List<AttachingConnector> attachingConnectors = Bootstrap.virtualMachineManager().attachingConnectors();AttachingConnector ac = null;found:for (AttachingConnector next : attachingConnectors) {if (next.name().contains("SocketAttach")) {ac = next;break;}}Map<String, 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) {List<ReferenceType> 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 don't tell anybody}}
}

因此,如果使用以下VM参数运行此类,请注意suspend = n,否则代码将不会开始运行,您会发现它可以重新连接到自身并开始运行。

-agentlib:jdwp=transport=dt_socket,address=localhost:5656,server=y,suspend=n

这将为您提供以下输出,请注意来自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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/366498.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

git显示服务器所有分支,git 查看所有远程分支以及同步

在多台电脑使用git管理开发分支的时候&#xff0c;会出现这样的情况。电脑A创建了分支1&#xff0c;并且push上了远程仓库。电脑B本地clone仓库默认只会clone下master分支&#xff0c;而其他电脑A推送的分支是不会默认同步下来的。那么如何同步呢&#xff1f;查看电脑B本地仓库…

Vue中浏览器的的前进和后退

项目开发的时候&#xff0c;有时候可能需要我们来对页面后退和前进&#xff0c;这个东西跟浏览器自带的前进后退功能很像,下面来大致讲一下在vue中浏览器的前进和后退 一、后退功能 vue中的后退有好多种方法可以使用&#xff0c;使用这些方法前要确认有之前的页面&#xff0c;否…

pandas:DataFrname(三)

pandas:从文件读取 读取文件&#xff1b;从文件名。url,文件对象中加载数据 read_csv 默认分隔符为逗号 read_table 默认分隔符为\t读取文件函数主要参数 sep 指定分隔符&#xff0c;可用正则表达式 headerNone 指定文件无列名 names 指定列名 index_col 指定某列作为索引 …

json-schema 可视化编辑器发布了

json-schema 的用途越来越广泛&#xff0c;除了定义数据结构外&#xff0c;我们还可以使用 json-schema 验证数据格式和生成随机数据&#xff0c;但是编写复杂数据结构的 json-schema 是非常痛苦的事情。假设一个 100 字段的数据结构&#xff0c;如果用 json-schema 定义&#…

tfw文件如何导入cad_如何将CAD的线稿导入PS并和底色分离

【新朋友】 点击标题下方的 CG伴学 迅速关注【老朋友】 点击右上角的按钮 分享 或者 收藏这是 【CG伴学 】制作的第249篇答疑教程观看往期视频教程请点击或者前往公众号自定义菜单【学习中心】我们专门制作了教程汇总目录&#xff0c;你可以根据标题关键词搜索获取我们在后台收…

HOW-TO:在Spring 4和Java 7中使用@PropertySource批注

今天&#xff0c;我将我当前正在从事的项目之一迁移到了Spring 4.0。 由于它是我用来学习和演示Spring功能的非常简单的Web应用程序&#xff0c;因此只需要更新项目的POM文件并更改Spring版本。 我将项目部署到Tomcat 7服务器&#xff0c;显然该应用程序未启动。 我在IntelliJ控…

Python全栈工程师(函数嵌套、变量作用域)

ParisGabriel 感谢 大家的支持 每天坚持 一天一篇 点个订阅吧 灰常感谢 当个死粉也阔以 Python人工智能从入门到精通 globals&#xff08;&#xff09;/locals&#xff08;&#xff09;函数&#xff1a;     globals&#xff08;&#xff09;返回当前全局作用域内变量…

可视化分析js的内存分配与回收

之前写了一篇文章浏览器是怎么看闭包的&#xff0c;发现有些读者对js内存分配与回收懵懵懂懂&#xff0c;理解文章的配图有些困难&#xff0c;我想主要是因为配图省略了一些细节。今天专门写一篇关于js内存分配回收的文章&#xff0c;帮助大家理解js代码的内存表示。原文备份在…

记录程序写入日志_终于有人把MySQL 三大日志讲清楚了

点击上方"蓝字"&#xff0c;关注了解更多日志是 mysql 数据库的重要组成部分&#xff0c;记录着数据库运行期间各种状态信息。mysql日志主要包括错误日志、查询日志、慢查询日志、事务日志、二进制日志几大类。作为开发&#xff0c;我们重点需要关注的是二进制日志( …

在ADF实体PK属性中使用MySQL自动增量PK列

大家好。 继续进行ADF MySQL解决方法系列&#xff0c;今天我们将看到要使用MySQL PK自动增量列和ADF实体PK属性来进行的工作。 如果使用的是Oracle数据库&#xff0c;则可以使用oracle.jbo.domain.DBSequence以及序列和触发器来立即进行操作。 为简单起见&#xff0c;我们将修…

HBuilder的快捷操作

1.通过&#xff01;感叹号单击tab 就可以快速生成&#xff1a; <!DOCTYPE html><html lang"zh"><head> <meta charset"UTF-8" /> <meta name"viewport" content"widthdevice-width, initial-scale1.0" /&g…

14.正则表达式、re模块、元字符

正则表达式 一、正则表达式所面向的问题 1、判断一个字符串是否匹配给定的格式 判断用户注册帐号是否满足格式 2、从一个字符串中按指定格式提取信息 抓取页面中的链接 二、判断用户提交的邮箱的格式是否正确 三、抓取页面中特定部分数据 er模块 1、findall方法&a…

WeScale 技术篇 —— mpvue 与微信小程序的火花

介绍项目介绍WeScale 定位为音乐训练小程序&#xff0c;初期规划了基础音阶的三个训练&#xff0c;以及他们的镜像模式。数字简谱字母简谱数字简谱对字母简谱后期看情况更新追加其他训练。产品展示扫描下方小程序码或在微信小程序中搜索 WeScale&#xff0c;即可使用。人员介绍…

使用Maven程序集创建漏洞评估工件

本文将讨论如何使用Maven程序集创建可提供给第三方漏洞评估站点&#xff08;例如Veracode &#xff09;进行审查的工件。 错误的静态分析与漏洞评估 在这一点上&#xff0c;每个人都知道findbug并虔诚地使用它&#xff0c;对吗&#xff1f; 对&#xff1f; Findbugs使用静态…

洛谷 P3835: 【模板】可持久化平衡树

题目传送门&#xff1a;洛谷P3835。 题意简述&#xff1a; 题面说的很清楚了。 题解&#xff1a; 考虑建立一棵每个节点都表示一个版本的树。 以初始版本 \(0\) 为根。对于第 \(i\) 个操作&#xff0c;从 \(v_i\) 向 \(i\) 连一条边&#xff0c;而边权则是 \(opt_i\) 和 \(x_i\…

自动论文生成器 python_Python生成器常见问题及解决方案

在Python中&#xff0c;生成器和函数很像&#xff0c;都是在运行的过程中才会去确定各种变量的值&#xff0c;所以在很多情况下&#xff0c;会导致各种各样的问题。def generator_test1():# 0...9 generatorx (i for i in range(10))# 5..9 generatorx_filter filter(lambda …

025 SSM简单搭建

参考了同事的文档&#xff0c;自己也写一篇文档。 同时&#xff0c;补充了一下&#xff0c;程序是如何运行的。 一&#xff1a;SSM框架 1.说明 SSM&#xff08;SpringSpringMVCMyBatis&#xff09;框架集由Spring、SpringMVC、MyBatis三个开源框架整合而成&#xff0c;常作为数…

让Vue也可以使用Redux

上周末看Vuex源码&#xff0c;突发灵感&#xff0c;为什么都是Vuex啊。 于是蛋疼一下午写了一个插件来帮助Vue可以使用Redux Gayhub Url Vue-with-Redux 这是一个用于帮助Vue使用Redux管理状态的插件。Redux是一个非常流行的状态管理工具。vue-with-redux为大家提供一个可以…

Apache Camel –从头开始开发应用程序(第2部分/第2部分)

这是本教程的第二部分&#xff0c;我们将使用Apache Camel创建发票处理应用程序。 如果您错过了它&#xff0c;一定要看一下第一部分 。 以前&#xff0c;我们已经定义了系统的功能要求&#xff0c;创建了网关&#xff0c;分离器&#xff0c;过滤器和基于内容的路由器组件。 让…

上升沿_PLC上升沿,下降沿的理解

有网友留言说&#xff1a;上升沿就是在信号从断开到接通的那一瞬间接通&#xff0c;下降沿就是在信号从接通到断开的那一瞬间接通。但是现在的问题它的实际用处是用在哪一些情况。我身边也有PLC可以做个什么实验来体验一下呢&#xff1f;虽然说LD X0 PLS M0与LDP X0 out Y0…