From b93179869189b85eca89069a601c9f52b34dea96 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Wed, 3 May 2017 22:55:06 +0200 Subject: [PATCH 1/7] Make Kompos tests more robus - add SOMns output to error - properly reject some of the promises - report error with stderr out when stream closes - added SOMns process kill - add shutdown hook to close sockets - wait until process exit with failing test Signed-off-by: Stefan Marr --- src/tools/debugger/FrontendConnector.java | 13 ++++---- src/tools/debugger/WebDebugger.java | 2 +- tools/kompos/tests/basic-protocol.ts | 7 +++-- tools/kompos/tests/csp.ts | 8 ++--- tools/kompos/tests/som-integration.ts | 2 +- tools/kompos/tests/test-setup.ts | 38 ++++++++++++++++++----- 6 files changed, 48 insertions(+), 22 deletions(-) diff --git a/src/tools/debugger/FrontendConnector.java b/src/tools/debugger/FrontendConnector.java index ce33b06b5..aa6632534 100644 --- a/src/tools/debugger/FrontendConnector.java +++ b/src/tools/debugger/FrontendConnector.java @@ -278,24 +278,25 @@ static void log(final String str) { } public void completeConnection(final WebSocket conn) { + Runtime.getRuntime().addShutdownHook(new Thread(() -> closeAllSockets())); + clientConnected.complete(conn); send(InitializationResponse.create(EntityType.values(), ActivityType.values(), BreakpointType.values(), SteppingType.values())); } - public void shutdown() { - int delaySec = 5; - contentServer.stop(delaySec); + private void closeAllSockets() { + final int delay = 0; + contentServer.stop(delay); sender.close(); if (binarySender != null) { binarySender.close(); } try { - int delayMsec = 1000; - receiver.stop(delayMsec); + receiver.stop(delay); if (binarySender != null) { - binaryHandler.stop(delayMsec); + binaryHandler.stop(delay); } } catch (InterruptedException e) { } } diff --git a/src/tools/debugger/WebDebugger.java b/src/tools/debugger/WebDebugger.java index c0fdf2666..851020473 100644 --- a/src/tools/debugger/WebDebugger.java +++ b/src/tools/debugger/WebDebugger.java @@ -155,7 +155,7 @@ public static void log(final String str) { @Override protected void onDispose(final Env env) { - connector.shutdown(); + /* NOOP: we close sockets with a VM shutdown hook */ } @Override diff --git a/tools/kompos/tests/basic-protocol.ts b/tools/kompos/tests/basic-protocol.ts index e0aa77c49..f75c263d7 100644 --- a/tools/kompos/tests/basic-protocol.ts +++ b/tools/kompos/tests/basic-protocol.ts @@ -60,7 +60,8 @@ describe("Basic Protocol", function() { conn = new TestConnection(); const ctrl = new ControllerWithInitialBreakpoints([], conn); let firstSourceCaptured = false; - sourceP = new Promise((resolve, _reject) => { + sourceP = new Promise((resolve, reject) => { + conn.fullyConnected.catch(reject); ctrl.onReceivedSource = (msg: SourceMessage) => { if (firstSourceCaptured) { return; }; firstSourceCaptured = true; @@ -255,7 +256,7 @@ describe("Basic Protocol", function() { before("Start SOMns and Connect", () => { conn = new TestConnection(); - ctrl = new HandleStoppedAndGetStackTrace([desc.breakpoint], conn); + ctrl = new HandleStoppedAndGetStackTrace([desc.breakpoint], conn, conn.fullyConnected); }); after(closeConnectionAfterSuite); @@ -276,7 +277,7 @@ describe("Basic Protocol", function() { const breakpoint = createSectionBreakpointData(PING_PONG_URI, 23, 14, 3, BT.MSG_SENDER, true); conn = new TestConnection(); - ctrl = new HandleStoppedAndGetStackTrace([breakpoint], conn, 4); + ctrl = new HandleStoppedAndGetStackTrace([breakpoint], conn, conn.fullyConnected, 4); }); after(closeConnectionAfterSuite); diff --git a/tools/kompos/tests/csp.ts b/tools/kompos/tests/csp.ts index 23776dce6..bfd420499 100644 --- a/tools/kompos/tests/csp.ts +++ b/tools/kompos/tests/csp.ts @@ -21,7 +21,7 @@ describe("Setting CSP Breakpoints", () => { const breakpoint = createSectionBreakpointData(CSP_URI, 13, 12, 4, BreakpointType.CHANNEL_BEFORE_RCV, true); conn = new TestConnection(["halt"], null, CSP_FILE); - ctrl = new HandleStoppedAndGetStackTrace([breakpoint], conn); + ctrl = new HandleStoppedAndGetStackTrace([breakpoint], conn, conn.fullyConnected); }); after(closeConnectionAfterSuite); @@ -39,7 +39,7 @@ describe("Setting CSP Breakpoints", () => { const breakpoint = createSectionBreakpointData(CSP_URI, 13, 12, 4, BreakpointType.CHANNEL_AFTER_SEND, true); conn = new TestConnection(["halt"], null, CSP_FILE); - ctrl = new HandleStoppedAndGetStackTrace([breakpoint], conn); + ctrl = new HandleStoppedAndGetStackTrace([breakpoint], conn, conn.fullyConnected); }); after(closeConnectionAfterSuite); @@ -56,7 +56,7 @@ describe("Setting CSP Breakpoints", () => { const breakpoint = createSectionBreakpointData(CSP_URI, 12, 13, 12, BreakpointType.CHANNEL_BEFORE_SEND, true); conn = new TestConnection(["halt"], null, CSP_FILE); - ctrl = new HandleStoppedAndGetStackTrace([breakpoint], conn); + ctrl = new HandleStoppedAndGetStackTrace([breakpoint], conn, conn.fullyConnected); }); after(closeConnectionAfterSuite); @@ -74,7 +74,7 @@ describe("Setting CSP Breakpoints", () => { const breakpoint = createSectionBreakpointData(CSP_URI, 12, 13, 12, BreakpointType.CHANNEL_AFTER_RCV, true); conn = new TestConnection(["halt"], null, CSP_FILE); - ctrl = new HandleStoppedAndGetStackTrace([breakpoint], conn); + ctrl = new HandleStoppedAndGetStackTrace([breakpoint], conn, conn.fullyConnected); }); after(closeConnectionAfterSuite); diff --git a/tools/kompos/tests/som-integration.ts b/tools/kompos/tests/som-integration.ts index 55c92bb6e..95f251a36 100644 --- a/tools/kompos/tests/som-integration.ts +++ b/tools/kompos/tests/som-integration.ts @@ -55,7 +55,7 @@ describe("Language Debugger Integration", function() { describe("execute `1 halt` and get suspended event", () => { before("Start SOMns and Connect", () => { conn = new TestConnection(["halt"]); - ctrl = new HandleStoppedAndGetStackTrace([], conn); + ctrl = new HandleStoppedAndGetStackTrace([], conn, conn.fullyConnected); }); after(closeConnectionAfterSuite); diff --git a/tools/kompos/tests/test-setup.ts b/tools/kompos/tests/test-setup.ts index f868ebc15..8c56a1eac 100644 --- a/tools/kompos/tests/test-setup.ts +++ b/tools/kompos/tests/test-setup.ts @@ -80,10 +80,25 @@ export class TestConnection extends VmConnection { const promise = new Promise((resolve, reject) => { this.connectionResolver = resolve; let connecting = false; + let errOut = ""; + + this.somProc.on("exit", (code, signal) => { + if (code !== 0) { + this.somProc.stderr.on("close", () => { + this.somProc.on("exit", (_code, _signal) => { + reject(new Error("Process exited with code: " + code + " Signal: " + signal + " StdErr: " + errOut)); + }); + }); + } + }); - if (PRINT_SOM_OUTPUT) { - this.somProc.stderr.on("data", (data) => { console.error(data.toString()); }); - } + this.somProc.stderr.on("data", data => { + const dataStr = data.toString(); + if (PRINT_SOM_OUTPUT) { + console.error(dataStr); + } + errOut += dataStr; + }); this.somProc.stdout.on("data", (data) => { const dataStr = data.toString(); @@ -95,7 +110,13 @@ export class TestConnection extends VmConnection { this.connect(); } if (dataStr.includes("Failed starting WebSocket and/or HTTP Server")) { - reject(new Error("SOMns failed to starting WebSocket and/or HTTP Server")); + this.somProc.stderr.on("close", () => { + this.somProc.on("exit", (_code, _signal) => { + reject(new Error("SOMns failed to starting WebSocket and/or HTTP Server. StdOut: " + dataStr + " StdErr: " + errOut)); + }); + }); + console.error("kill somProc because of failed connection"); + this.somProc.kill(); } }); }); @@ -113,6 +134,7 @@ export class TestConnection extends VmConnection { // wait until process is shut down, to make sure all ports are closed done(); }); + console.error("kill somProc via TestConnection.close()"); this.somProc.kill(); } } @@ -146,23 +168,25 @@ export class HandleStoppedAndGetStackTrace extends ControllerWithInitialBreakpoi public readonly stoppedActivities: Activity[]; constructor(initialBreakpoints: BreakpointData[], vmConnection: VmConnection, - numOps: number = 1) { + connectionP: Promise, numOps: number = 1) { super(initialBreakpoints, vmConnection); this.numOps = numOps; this.numStopped = 0; this.stoppedActivities = []; - this.stackP = new Promise((resolve, _reject) => { + this.stackP = new Promise((resolve, reject) => { this.resolveStackP = resolve; + connectionP.catch(reject); }); if (numOps > 1) { this.resolveStackPs = []; this.stackPs = []; for (let i = 1; i < numOps; i += 1) { - this.stackPs.push(new Promise((resolve, _reject) => { + this.stackPs.push(new Promise((resolve, reject) => { this.resolveStackPs.push(resolve); + connectionP.catch(reject); })); } } From a1a60c603f83855f58f4a07fad6193fb36838a6d Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Fri, 5 May 2017 12:04:04 +0200 Subject: [PATCH 2/7] [WIP] Use ephemeral ports if standard ports are not available Kompos tests seem to fail on travis some times with an address already bound error. To avoid it, try to use standard port, and if already in use, use an ephemeral port instead. The web socket ports can be communicated via JSON and easily queried by the frontend. The HTTP port needs to be parsed out of the STDOUT. Signed-off-by: Stefan Marr --- src/tools/debugger/FrontendConnector.java | 13 +++++++++---- src/tools/debugger/WebResourceHandler.java | 16 ++++++++++++++++ src/tools/debugger/WebSocketHandler.java | 5 +++++ tools/kompos/src/vm-connection.ts | 17 +++++++++++------ 4 files changed, 41 insertions(+), 10 deletions(-) diff --git a/src/tools/debugger/FrontendConnector.java b/src/tools/debugger/FrontendConnector.java index aa6632534..c54158ee5 100644 --- a/src/tools/debugger/FrontendConnector.java +++ b/src/tools/debugger/FrontendConnector.java @@ -99,14 +99,18 @@ public FrontendConnector(final Breakpoints breakpoints, try { log("[DEBUGGER] Initialize HTTP and WebSocket Server for Debugger"); receiver = initializeWebSocket(MESSAGE_PORT, clientConnected); + log("[DEBUGGER] Started WebSocket Server"); + // TODO: first try the given ports, then try the ephemeral ports binaryHandler = new BinaryWebSocketHandler(new InetSocketAddress(BINARY_PORT)); binaryHandler.start(); - contentServer = initializeHttpServer(DEBUGGER_PORT); + + contentServer = initializeHttpServer(DEBUGGER_PORT, + receiver.getPort(), binaryHandler.getPort()); log("[DEBUGGER] Started HTTP Server"); - log("[DEBUGGER] URL: http://localhost:" + DEBUGGER_PORT + "/index.html"); + log("[DEBUGGER] URL: http://localhost:" + contentServer.getAddress().getPort() + "/index.html"); } catch (IOException e) { log("Failed starting WebSocket and/or HTTP Server"); throw new RuntimeException(e); @@ -127,10 +131,11 @@ private WebSocketHandler initializeWebSocket(final int port, return server; } - private HttpServer initializeHttpServer(final int port) throws IOException { + private HttpServer initializeHttpServer(final int port, + final int debuggerPort, final int tracePort) throws IOException { InetSocketAddress address = new InetSocketAddress(port); HttpServer httpServer = HttpServer.create(address, 0); - httpServer.createContext("/", new WebResourceHandler()); + httpServer.createContext("/", new WebResourceHandler(debuggerPort, tracePort)); httpServer.setExecutor(null); httpServer.start(); return httpServer; diff --git a/src/tools/debugger/WebResourceHandler.java b/src/tools/debugger/WebResourceHandler.java index 399539199..dd3807bdc 100644 --- a/src/tools/debugger/WebResourceHandler.java +++ b/src/tools/debugger/WebResourceHandler.java @@ -5,6 +5,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.charset.Charset; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; @@ -12,6 +13,13 @@ import som.vm.NotYetImplementedException; class WebResourceHandler implements HttpHandler { + private final int debuggerPort; + private final int tracePort; + + WebResourceHandler(final int debuggerPort, final int tracePort) { + this.debuggerPort = debuggerPort; + this.tracePort = tracePort; + } @Override public void handle(final HttpExchange exchange) throws IOException { @@ -36,10 +44,18 @@ public void handle(final HttpExchange exchange) throws IOException { } exchange.sendResponseHeaders(200, f.length()); copy(f, exchange.getResponseBody()); + exchange.close(); return; } switch (requestedFile) { + case "/ports.json": + String jsonPorts = "{\"dbgPort\":" + debuggerPort + ",\"tracePort\":" + tracePort + "\"}"; + System.out.println(jsonPorts); + exchange.sendResponseHeaders(200, jsonPorts.length()); + exchange.getResponseBody().write(jsonPorts.getBytes(Charset.forName("UTF-8"))); + exchange.close(); + return; case "/favicon.ico": exchange.sendResponseHeaders(404, 0); exchange.close(); diff --git a/src/tools/debugger/WebSocketHandler.java b/src/tools/debugger/WebSocketHandler.java index 99bf50cf4..4efb69c58 100644 --- a/src/tools/debugger/WebSocketHandler.java +++ b/src/tools/debugger/WebSocketHandler.java @@ -25,6 +25,11 @@ public class WebSocketHandler extends WebSocketServer { this.gson = gson; } + @Override + public void onStart() { + + } + @Override public void onOpen(final WebSocket conn, final ClientHandshake handshake) { } diff --git a/tools/kompos/src/vm-connection.ts b/tools/kompos/src/vm-connection.ts index 8aca06077..4953ff175 100644 --- a/tools/kompos/src/vm-connection.ts +++ b/tools/kompos/src/vm-connection.ts @@ -6,8 +6,6 @@ import * as WebSocket from "ws"; import {Controller} from "./controller"; import {Activity, Message, Respond, BreakpointData} from "./messages"; -const DBG_PORT = 7977; -const TRACE_PORT = 7978; const LOCAL_WS_URL = "ws://localhost"; /** @@ -35,11 +33,11 @@ export class VmConnection { return this.socket !== null && this.socket.readyState === WebSocket.OPEN; } - private connectTraceDataSocket() { + private connectTraceDataSocket(tracePort: number) { if (!this.useTraceData) { return; } console.assert(this.traceDataSocket === null || this.traceDataSocket.readyState === WebSocket.CLOSED); - this.traceDataSocket = new WebSocket(LOCAL_WS_URL + ":" + TRACE_PORT); + this.traceDataSocket = new WebSocket(LOCAL_WS_URL + ":" + tracePort); ( this.traceDataSocket).binaryType = "arraybuffer"; // workaround, typescript doesn't recognize this property const controller = this.controller; @@ -55,8 +53,15 @@ export class VmConnection { } public connect() { + $.getJSON("ports.json", data => { + console.log(data); + this.connectWebSockets(data.dbgPort, data.tracePort); + }); + } + + private connectWebSockets(dbgPort: number, tracePort: number) { console.assert(this.socket === null || this.socket.readyState === WebSocket.CLOSED); - this.socket = new WebSocket(LOCAL_WS_URL + ":" + DBG_PORT); + this.socket = new WebSocket(LOCAL_WS_URL + ":" + dbgPort); const ctrl = this.controller; this.socket.onopen = () => { @@ -109,7 +114,7 @@ export class VmConnection { } }; - this.connectTraceDataSocket(); + this.connectTraceDataSocket(tracePort); } public disconnect() { From cbbd91b92457d23d4a0eebf2e3081b4a0282fb26 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Fri, 5 May 2017 12:04:17 +0200 Subject: [PATCH 3/7] Remove process kill logging Signed-off-by: Stefan Marr --- tools/kompos/tests/test-setup.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/tools/kompos/tests/test-setup.ts b/tools/kompos/tests/test-setup.ts index 8c56a1eac..618ab966f 100644 --- a/tools/kompos/tests/test-setup.ts +++ b/tools/kompos/tests/test-setup.ts @@ -115,7 +115,6 @@ export class TestConnection extends VmConnection { reject(new Error("SOMns failed to starting WebSocket and/or HTTP Server. StdOut: " + dataStr + " StdErr: " + errOut)); }); }); - console.error("kill somProc because of failed connection"); this.somProc.kill(); } }); @@ -134,7 +133,6 @@ export class TestConnection extends VmConnection { // wait until process is shut down, to make sure all ports are closed done(); }); - console.error("kill somProc via TestConnection.close()"); this.somProc.kill(); } } From f4bd294296f44326c3ed59d1f963eb8238fa4a59 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Sat, 6 May 2017 01:33:39 +0200 Subject: [PATCH 4/7] Update SOMns-deps to have latest WebSocket lib version Signed-off-by: Stefan Marr --- .classpath | 4 ++-- build.xml | 10 ++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.classpath b/.classpath index 4c11341e1..8485df73d 100644 --- a/.classpath +++ b/.classpath @@ -20,7 +20,7 @@ - - + + diff --git a/build.xml b/build.xml index f0084fa30..0733bcbd5 100644 --- a/build.xml +++ b/build.xml @@ -6,7 +6,7 @@ - + @@ -57,7 +57,7 @@ - + @@ -72,6 +72,12 @@ dest="${lib.dir}/somns-deps-dev.jar" /> + + + + Date: Sat, 6 May 2017 01:35:20 +0200 Subject: [PATCH 5/7] Change sockets to first try fixed port and then use ephemeral - in tests, parse stdout for ports - remove duplicate code of old BinaryWebSocketHandler Signed-off-by: Stefan Marr --- .../debugger/BinaryWebSocketHandler.java | 43 ---------- src/tools/debugger/FrontendConnector.java | 64 +++++++++----- src/tools/debugger/WebSocketHandler.java | 83 ++++++++++++++----- tools/kompos/src/vm-connection.ts | 2 +- tools/kompos/tests/test-setup.ts | 17 +++- 5 files changed, 126 insertions(+), 83 deletions(-) delete mode 100644 src/tools/debugger/BinaryWebSocketHandler.java diff --git a/src/tools/debugger/BinaryWebSocketHandler.java b/src/tools/debugger/BinaryWebSocketHandler.java deleted file mode 100644 index 17c47ee26..000000000 --- a/src/tools/debugger/BinaryWebSocketHandler.java +++ /dev/null @@ -1,43 +0,0 @@ -package tools.debugger; - -import java.net.InetSocketAddress; -import java.util.concurrent.CompletableFuture; - -import org.java_websocket.WebSocket; -import org.java_websocket.handshake.ClientHandshake; -import org.java_websocket.server.WebSocketServer; - -public class BinaryWebSocketHandler extends WebSocketServer{ - private static final int NUM_THREADS = 1; - private final CompletableFuture connection; - - public BinaryWebSocketHandler(final InetSocketAddress address) { - super(address, NUM_THREADS); - connection = new CompletableFuture<>(); - } - - @Override - public void onClose(final WebSocket conn, final int arg1, final String arg2, final boolean arg3) { - // no-op - } - - @Override - public void onError(final WebSocket conn, final Exception ex) { - WebDebugger.log("error:"); - ex.printStackTrace(); - } - - @Override - public void onMessage(final WebSocket conn, final String msg) { - // no-op - } - - @Override - public void onOpen(final WebSocket conn, final ClientHandshake handshake) { - connection.complete(conn); - } - - public CompletableFuture getConnection() { - return connection; - } -} diff --git a/src/tools/debugger/FrontendConnector.java b/src/tools/debugger/FrontendConnector.java index c54158ee5..b1c3dedec 100644 --- a/src/tools/debugger/FrontendConnector.java +++ b/src/tools/debugger/FrontendConnector.java @@ -1,6 +1,7 @@ package tools.debugger; import java.io.IOException; +import java.net.BindException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.util.ArrayList; @@ -9,7 +10,7 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; +import java.util.function.Function; import org.java_websocket.WebSocket; @@ -27,6 +28,8 @@ import tools.Tagging; import tools.TraceData; import tools.concurrency.ActorExecutionTrace; +import tools.debugger.WebSocketHandler.MessageHandler; +import tools.debugger.WebSocketHandler.TraceHandler; import tools.debugger.entities.ActivityType; import tools.debugger.entities.BreakpointType; import tools.debugger.entities.EntityType; @@ -64,8 +67,8 @@ public class FrontendConnector { /** * Receives requests from the client. */ - private final WebSocketHandler receiver; - private final BinaryWebSocketHandler binaryHandler; + private final MessageHandler receiver; + private final TraceHandler binaryHandler; /** * Sends requests to the client. @@ -80,9 +83,10 @@ public class FrontendConnector { private CompletableFuture clientConnected; private final Gson gson; - private static final int MESSAGE_PORT = 7977; - private static final int BINARY_PORT = 7978; - private static final int DEBUGGER_PORT = 8888; + private static final int MESSAGE_PORT = 7977; + private static final int BINARY_PORT = 7978; + private static final int DEBUGGER_PORT = 8888; + private static final int EPHEMERAL_PORT = 0; private final ArrayList notReady = new ArrayList<>(); // TODO rename: toBeSend @@ -98,14 +102,11 @@ public FrontendConnector(final Breakpoints breakpoints, try { log("[DEBUGGER] Initialize HTTP and WebSocket Server for Debugger"); - receiver = initializeWebSocket(MESSAGE_PORT, clientConnected); - - log("[DEBUGGER] Started WebSocket Server"); - - // TODO: first try the given ports, then try the ephemeral ports - binaryHandler = new BinaryWebSocketHandler(new InetSocketAddress(BINARY_PORT)); - binaryHandler.start(); - + receiver = initializeWebSocket(MESSAGE_PORT, port -> new MessageHandler(port, this, gson)); + binaryHandler = initializeWebSocket(BINARY_PORT, port -> new TraceHandler(port)); + log("[DEBUGGER] Started WebSocket Servers"); + log("[DEBUGGER] Message Handler: " + receiver.getPort()); + log("[DEBUGGER] Trace Handler: " + binaryHandler.getPort()); contentServer = initializeHttpServer(DEBUGGER_PORT, receiver.getPort(), binaryHandler.getPort()); @@ -123,15 +124,31 @@ public Breakpoints getBreakpoints() { return breakpoints; } - private WebSocketHandler initializeWebSocket(final int port, - final Future clientConnected) { - InetSocketAddress address = new InetSocketAddress(port); - WebSocketHandler server = new WebSocketHandler(address, this, gson); + private T tryInitializingWebSocket(final T server) throws Throwable { server.start(); + try { + server.awaitStartup(); + } catch (ExecutionException e) { + throw e.getCause(); + } return server; } - private HttpServer initializeHttpServer(final int port, + private T initializeWebSocket(final int port, final Function ctor) { + try { + return tryInitializingWebSocket(ctor.apply(port)); + } catch (BindException e) { + try { + return tryInitializingWebSocket(ctor.apply(EPHEMERAL_PORT)); + } catch (Throwable e1) { + throw new RuntimeException(e); + } + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + private HttpServer tryInitializingHttpServer(final int port, final int debuggerPort, final int tracePort) throws IOException { InetSocketAddress address = new InetSocketAddress(port); HttpServer httpServer = HttpServer.create(address, 0); @@ -141,6 +158,15 @@ private HttpServer initializeHttpServer(final int port, return httpServer; } + private HttpServer initializeHttpServer(final int port, + final int debuggerPort, final int tracePort) throws IOException { + try { + return tryInitializingHttpServer(port, debuggerPort, tracePort); + } catch (BindException e) { + return tryInitializingHttpServer(EPHEMERAL_PORT, debuggerPort, tracePort); + } + } + private void ensureConnectionIsAvailable() { assert receiver != null; assert sender != null; diff --git a/src/tools/debugger/WebSocketHandler.java b/src/tools/debugger/WebSocketHandler.java index 4efb69c58..582db420f 100644 --- a/src/tools/debugger/WebSocketHandler.java +++ b/src/tools/debugger/WebSocketHandler.java @@ -1,6 +1,9 @@ package tools.debugger; +import java.net.BindException; import java.net.InetSocketAddress; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import org.java_websocket.WebSocket; import org.java_websocket.handshake.ClientHandshake; @@ -12,22 +15,27 @@ import tools.debugger.message.Message.IncommingMessage; -public class WebSocketHandler extends WebSocketServer { +public abstract class WebSocketHandler extends WebSocketServer { private static final int NUM_THREADS = 1; - private final FrontendConnector connector; - private final Gson gson; + private final CompletableFuture connectionPort; - WebSocketHandler(final InetSocketAddress address, - final FrontendConnector connector, final Gson gson) { - super(address, NUM_THREADS); - this.connector = connector; - this.gson = gson; + WebSocketHandler(final int port) { + super(new InetSocketAddress(port), NUM_THREADS); + this.connectionPort = new CompletableFuture<>(); } @Override public void onStart() { + connectionPort.complete(getPort()); + } + public int awaitStartup() throws ExecutionException { + while (true) { + try { + return connectionPort.get(); + } catch (InterruptedException e) { /* Retry on interrupt. */ } + } } @Override @@ -39,20 +47,57 @@ public void onClose(final WebSocket conn, final int code, final String reason, WebDebugger.log("onClose: code=" + code + " " + reason); } - @Override - public void onMessage(final WebSocket conn, final String message) { - try { - IncommingMessage respond = gson.fromJson(message, IncommingMessage.class); - respond.process(connector, conn); - } catch (Throwable ex) { - VM.errorPrint("Error while processing msg:" + message); - ex.printStackTrace(); - } - } - @Override public void onError(final WebSocket conn, final Exception ex) { + if (ex instanceof BindException) { + connectionPort.completeExceptionally(ex); + return; + } WebDebugger.log("error:"); ex.printStackTrace(); } + + public static class MessageHandler extends WebSocketHandler { + private final FrontendConnector connector; + private final Gson gson; + + public MessageHandler(final int port, final FrontendConnector connector, + final Gson gson) { + super(port); + this.connector = connector; + this.gson = gson; + } + + @Override + public void onMessage(final WebSocket conn, final String message) { + try { + IncommingMessage respond = gson.fromJson(message, IncommingMessage.class); + respond.process(connector, conn); + } catch (Throwable ex) { + VM.errorPrint("Error while processing msg:" + message); + ex.printStackTrace(); + } + } + } + + public static class TraceHandler extends WebSocketHandler { + private final CompletableFuture connection; + + public TraceHandler(final int port) { + super(port); + connection = new CompletableFuture<>(); + } + + @Override + public void onOpen(final WebSocket conn, final ClientHandshake handshake) { + connection.complete(conn); + } + + @Override + public void onMessage(final WebSocket conn, final String message) { } + + public CompletableFuture getConnection() { + return connection; + } + } } diff --git a/tools/kompos/src/vm-connection.ts b/tools/kompos/src/vm-connection.ts index 4953ff175..bb75fb20d 100644 --- a/tools/kompos/src/vm-connection.ts +++ b/tools/kompos/src/vm-connection.ts @@ -59,7 +59,7 @@ export class VmConnection { }); } - private connectWebSockets(dbgPort: number, tracePort: number) { + protected connectWebSockets(dbgPort: number, tracePort: number) { console.assert(this.socket === null || this.socket.readyState === WebSocket.CLOSED); this.socket = new WebSocket(LOCAL_WS_URL + ":" + dbgPort); const ctrl = this.controller; diff --git a/tools/kompos/tests/test-setup.ts b/tools/kompos/tests/test-setup.ts index 618ab966f..80074d481 100644 --- a/tools/kompos/tests/test-setup.ts +++ b/tools/kompos/tests/test-setup.ts @@ -78,9 +78,13 @@ export class TestConnection extends VmConnection { private initConnection(): Promise { const promise = new Promise((resolve, reject) => { + const msgPortRe = /.*Message Handler:\s+(\d+)/m; + const tracePortRe = /.*Trace Handler:\s+(\d+)/m; this.connectionResolver = resolve; let connecting = false; let errOut = ""; + let msgPort = 0; + let tracePort = 0; this.somProc.on("exit", (code, signal) => { if (code !== 0) { @@ -105,9 +109,20 @@ export class TestConnection extends VmConnection { if (PRINT_SOM_OUTPUT) { console.log(dataStr); } + + let m = dataStr.match(msgPortRe); + if (m) { + msgPort = parseInt(m[1]); + } + m = dataStr.match(tracePortRe); + if (m) { + tracePort = parseInt(m[1]); + } + if (dataStr.includes("Started HTTP Server") && !connecting) { connecting = true; - this.connect(); + console.assert(msgPort > 0 && tracePort > 0); + this.connectWebSockets(msgPort, tracePort); } if (dataStr.includes("Failed starting WebSocket and/or HTTP Server")) { this.somProc.stderr.on("close", () => { From f7bfc9c166b459ef6f32d31087ea22dca06bfa08 Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Sat, 6 May 2017 01:41:42 +0200 Subject: [PATCH 6/7] Increase Kompos test timeout limit for Travis Signed-off-by: Stefan Marr --- tools/kompos/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/kompos/package.json b/tools/kompos/package.json index b1c8ca5e9..cb65de0b0 100644 --- a/tools/kompos/package.json +++ b/tools/kompos/package.json @@ -46,6 +46,6 @@ "compile": "node ./node_modules/typescript/bin/tsc && npm run copylibs", "lint": "node_modules/tslint/bin/tslint -c tslint.json --project tsconfig.json", "watch": "node ./node_modules/typescript/bin/tsc -w", - "test": "node ./node_modules/mocha/bin/mocha -r node-define -t 5000 -u bdd ./out/tests/" + "test": "node ./node_modules/mocha/bin/mocha -r node-define -t 10000 -u bdd ./out/tests/" } } From 25a15b9b3c85d38c87768ab3429d86843b41a01a Mon Sep 17 00:00:00 2001 From: Stefan Marr Date: Sat, 6 May 2017 01:50:39 +0200 Subject: [PATCH 7/7] Fix Signed-off-by: Stefan Marr --- src/tools/debugger/WebResourceHandler.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tools/debugger/WebResourceHandler.java b/src/tools/debugger/WebResourceHandler.java index dd3807bdc..797bbc61b 100644 --- a/src/tools/debugger/WebResourceHandler.java +++ b/src/tools/debugger/WebResourceHandler.java @@ -51,7 +51,6 @@ public void handle(final HttpExchange exchange) throws IOException { switch (requestedFile) { case "/ports.json": String jsonPorts = "{\"dbgPort\":" + debuggerPort + ",\"tracePort\":" + tracePort + "\"}"; - System.out.println(jsonPorts); exchange.sendResponseHeaders(200, jsonPorts.length()); exchange.getResponseBody().write(jsonPorts.getBytes(Charset.forName("UTF-8"))); exchange.close();