性能测试脚本的编写和调试_编写自动调试器以在测试执行期间捕获异常
性能測試腳本的編寫和調試
以前,我曾說過, 您總是想保留一些調試器斷點作為例外 。 這有助于防止代碼在不注意的情況下腐爛掉,有時掩蓋了另一個問題。
如果您認真對待這一點,那么最好將此想法擴展到自動化測試中。 但是想出一個全面的解決方案并不簡單。 您可以僅從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();}主要的連接方法是一個相當簡單的代碼塊,可確保連接并配置任何初始斷點。
//// 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 每個人都想讀一下這是否對人們有用并有助于消除任何明顯的錯誤。
翻譯自: https://www.javacodegeeks.com/2013/10/write-an-auto-debugger-to-catch-exceptions-during-test-execution.html
性能測試腳本的編寫和調試
總結
以上是生活随笔為你收集整理的性能测试脚本的编写和调试_编写自动调试器以在测试执行期间捕获异常的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑低格硬盘的方法(固态硬盘低格硬盘教程
- 下一篇: lol简单名字电脑(好听lol名字大全)