Skip to content
This repository has been archived by the owner on Jan 16, 2018. It is now read-only.

Commit

Permalink
Integrate debugger frontend
Browse files Browse the repository at this point in the history
  • Loading branch information
jgranick committed Nov 20, 2013
1 parent 95d8aba commit e494f3b
Show file tree
Hide file tree
Showing 8 changed files with 4,848 additions and 0 deletions.
1,411 changes: 1,411 additions & 0 deletions cpp/debugger/CommandLineController.hx

Large diffs are not rendered by default.

2,209 changes: 2,209 additions & 0 deletions cpp/debugger/DebuggerThread.hx

Large diffs are not rendered by default.

138 changes: 138 additions & 0 deletions cpp/debugger/HaxeProtocol.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/** **************************************************************************
* HaxeProtocol.hx
*
* Copyright 2013 TiVo Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
************************************************************************** **/

package cpp.debugger;

import cpp.debugger.IController;


/**
* This is a helper utility class that handles all message formatting for the
* haxe serialization based remote debugger protocol.
**/
class HaxeProtocol
{
public static function writeClientIdentification(output : haxe.io.Output)
{
output.writeString(gClientIdentification);
}

public static function writeServerIdentification(output : haxe.io.Output)
{
output.writeString(gServerIdentification);
}

public static function readClientIdentification(input : haxe.io.Input)
{
var id = input.read(gClientIdentification.length);
if (id.toString() != gClientIdentification) {
throw "Unexpected client identification string: " + id;
}
}

public static function readServerIdentification(input : haxe.io.Input)
{
var id = input.read(gServerIdentification.length);
if (id.toString() != gServerIdentification) {
throw "Unexpected server identification string: " + id;
}
}

public static function writeCommand(output : haxe.io.Output,
command : Command)
{
writeDynamic(output, command);
}

public static function writeMessage(output : haxe.io.Output,
message : Message)
{
writeDynamic(output, message);
}

public static function readCommand(input : haxe.io.Input) : Command
{
var raw : Dynamic = readDynamic(input);

// Convert to command and return
try {
return cast(raw, Command);
}
catch (e : Dynamic) {
throw "Expected Command, but got " + raw + ": " + e;
}
}

public static function readMessage(input : haxe.io.Input) : Message
{
var raw : Dynamic = readDynamic(input);

// Convert to command and return
try {
return cast(raw, Message);
}
catch (e : Dynamic) {
throw "Expected Message, but got " + raw + ": " + e;
}
}

private static function writeDynamic(output : haxe.io.Output,
value : Dynamic)
{
// Serialize it
var string = haxe.Serializer.run(value);

// Write its length
var msg_len = string.length;
var msg_len_raw = haxe.io.Bytes.alloc(8);

for (i in 0 ... 8) {
msg_len_raw.set(7 - i, (msg_len % 10) + 48);
msg_len = Std.int(msg_len / 10);
}

output.write(msg_len_raw);
output.writeString(string);
}

private static function readDynamic(input : haxe.io.Input) : Dynamic
{
var msg_len_raw = input.read(8);

// Convert to number
var msg_len : Int = 0;
for (i in 0 ... 8) {
msg_len *= 10;
msg_len += msg_len_raw.get(i) - 48; // 48 is ASCII '0'
}

// Validate the length. Don't allow messages larger than
// 100K.
if (msg_len > (100 * 1024)) {
throw "Read bad message length: " + msg_len + ".";
}

// Read and deserialize message
return haxe.Unserializer.run(input.read(msg_len).toString());
}

private static var gClientIdentification =
"Haxe debug client v1.0 coming at you!\n\n";
private static var gServerIdentification =
"Haxe debug server v1.0 ready and willing, sir!\n\n";
}
163 changes: 163 additions & 0 deletions cpp/debugger/HaxeRemote.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/** **************************************************************************
* HaxeRemote.hx
*
* Copyright 2013 TiVo Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
************************************************************************** **/

package cpp.debugger;

import cpp.debugger.HaxeProtocol;
import cpp.debugger.IController;

#if cpp
import cpp.vm.Mutex;
#elseif neko
import neko.vm.Mutex;
#else
#error "HaxeRemote supported only for cpp and neko targets"
#end


/**
* This class creates a networked command-line debugger that communicates with
* a peer using Haxe serialization format. This class should be instantiated
* int the main() function of any program that wishes to be debugged via a
* remote interface.
**/
class HaxeRemote implements IController
{
/**
* Creates a debugger which will read input from the interface provided
* by the given remote host, and emit output to that remote host.
*
* If the program was not compiled with debugging support, a String
* exception is thrown.
*
* @param startStopped if true, when the breakpoint starts, all other
* threads of the process are stopped until the user instructs the
* debugger to continue those threads. If false, all threads of
* the program will continue to run when the debugger is started
* and will not stop until a debugger command instructs them to do
* so.
* @param host is the host name of the debugging server to connect to
* @param port is the port of the debugging server to connect to
**/
public function new(startStopped : Bool, host : String, port : Int = 6972)
{
mHost = host;
mPort = port;
mSocket = null;
mSocketMutex = new Mutex();
mAcceptMutex = new Mutex();
mThread = new DebuggerThread(this, startStopped);
}

public function getNextCommand() : Command
{
while (true) {
var socket;
mSocketMutex.acquire();
if (mSocket == null) {
this.connectLocked();
}
socket = mSocket;
mSocketMutex.release();
try {
return HaxeProtocol.readCommand(socket.input);
}
catch (e : Dynamic) {
Sys.println("Failed to read command from server at " +
mHost + ":" + mPort + ": " + e);
Sys.println("Closing connection and trying again.");
socket.close();
mSocketMutex.acquire();
if (mSocket == socket) {
mSocket = null;
}
mSocketMutex.release();
}
}
}

/**
* Called when the debugger has a message to deliver. Note that this may
* be called by multiple threads simultaneously if an asynchronous thread
* event occurs. The implementation should probably lock as necessary.
*
* @param message is the message
**/
public function acceptMessage(message : Message) : Void
{
while (true) {
var socket;
mSocketMutex.acquire();
if (mSocket == null) {
this.connectLocked();
}
socket = mSocket;
mSocketMutex.release();
mAcceptMutex.acquire();
try {
HaxeProtocol.writeMessage(socket.output, message);
mAcceptMutex.release();
return;
}
catch (e : Dynamic) {
mAcceptMutex.release();
Sys.println("Failed to deliver message to server at " +
mHost + ":" + mPort + ": " + e);
Sys.println("Closing connection and trying again.");
socket.close();
mSocketMutex.acquire();
if (mSocket == socket) {
mSocket = null;
}
mSocketMutex.release();
}
}
}

private function connectLocked()
{
mSocket = new sys.net.Socket();
while (true) {
try {
var host = new sys.net.Host(mHost);
if (host.ip == 0) {
throw "Name lookup error.";
}
mSocket.connect(host, mPort);
HaxeProtocol.writeClientIdentification(mSocket.output);
HaxeProtocol.readServerIdentification(mSocket.input);
Sys.println("Connected to debugging server at " +
mHost + ":" + mPort + ".");
return;
}
catch (e : Dynamic) {
Sys.println("Failed to connect to debugging server at " +
mHost + ":" + mPort + " : " + e);
}
Sys.println("Trying again in 3 seconds.");
Sys.sleep(3);
}
}

private var mHost : String;
private var mPort : Int;
private var mSocket : sys.net.Socket;
private var mSocketMutex : Mutex;
private var mAcceptMutex : Mutex;
private var mThread : DebuggerThread;
}
Loading

0 comments on commit e494f3b

Please sign in to comment.