-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
udap: debug adapter protocol implementation
Attempt to start udb in adapter mode Debuggee communication reconfiguration Add debuggee error handling Add debug console handling Respond to stackTrace request Respond to scope and variables requests Respond to continue, next, stepIn, and stepOut Add runInTerminal features Add udb and socket error handling Signed-off-by: mstreeter10 <[email protected]>
- Loading branch information
1 parent
773ed0d
commit d55ba84
Showing
9 changed files
with
966 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
BASE = ../../.. | ||
include $(BASE)/Makedefs.uni | ||
|
||
UFLAGS=-s -u | ||
|
||
SRC=launch-dap.icn server.icn communicator.icn | ||
OBJ=launch-dap.u server.u communicator.u | ||
|
||
.PHONY:all | ||
|
||
all: dapcom udap | ||
|
||
udap: $(OBJ) | ||
$(UC) $(DASHG) -o udap $(OBJ) | ||
$(CP) udap$(EXE) ../../../bin | ||
|
||
launch-dap.u:launch-dap.icn server.u | ||
server.u:server.icn communicator.u | ||
communicator.u:communicator.icn | ||
|
||
dapcom:dapcom.icn | ||
$(UC) $(DASHG) -o dapcom dapcom.icn | ||
$(CP) dapcom$(EXE) ../../../bin | ||
|
||
zip: | ||
zip udap.zip Makefile *.icn | ||
|
||
clean: | ||
$(RM) -f *.u $(prog)$(EXE) uniclass* dapcom$(EXE) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,295 @@ | ||
package udap | ||
|
||
link findre | ||
import json | ||
|
||
class Communicator(udb, udbSock, tpsock, dapcomSock, filePath, tpArgs) | ||
|
||
# Attempt to start udb if not already active and connect to it. | ||
# Returns "success" if successful and an appropriate error string if otherwise. | ||
method start_debugger(port) | ||
local udbPath, dir, result | ||
|
||
udbPath := find_debugger() | ||
if /udbPath then return "udap could not find udb" | ||
|
||
udb := system(udbPath || " -adapter " || port, &null, &null, &null, "nowait") | ||
|
||
udbSock := open_sock(port) | ||
if /udbSock then return "udap failed to open udbSock: " || port | ||
|
||
tpsock := open_sock(port + 10) | ||
if /tpsock then return "udap failed to open tpsock: " || port + 10 | ||
|
||
return "success" | ||
end | ||
|
||
# Send a termination signal to udb. | ||
method end_debugger() | ||
kill(\udb, 9) | ||
end | ||
|
||
# Returns udb's absolute path. | ||
method find_debugger() | ||
return pathfind("udb") | ||
end | ||
|
||
# Returns a list of tables containing stack trace information. | ||
# A table of with key "type" set to "crash" is returned if udb experiences an error. | ||
method stack_trace() | ||
local udbResTable, udbResTableList, i, frames | ||
|
||
udbResTableList := list() | ||
frames := list() | ||
|
||
every i := udb_input("bt", 1) do { | ||
put(udbResTableList, \i) | ||
} | ||
if *udbResTableList ~= 0 then { | ||
every udbResTable := !udbResTableList do { | ||
if member(udbResTable, "type") then { | ||
if udbResTable["type"] == "frame" then { | ||
udbResTable["name"] := replace(udbResTable["name"], "\"", "\\\"") | ||
udbResTable["consoleMsg"] := replace(udbResTable["consoleMsg"], "\"", "\\\"") | ||
put(frames, udbResTable) | ||
} | ||
if udbResTable["type"] == "crash" then return udbResTable | ||
} | ||
} | ||
} | ||
|
||
return frames | ||
end | ||
|
||
# Returns a list of tables containing scope information. | ||
# A table of with key "type" set to "crash" is returned if udb experiences an error. | ||
method get_scopes(frame) | ||
local udbResTableList, udbResTable, scopes, i, j, k, l, m | ||
|
||
udbResTableList := list() | ||
scopes := list() | ||
|
||
every i := udb_input("frame " || frame, 1) do put(udbResTableList, \i) | ||
every j := udb_input("print -g", 1) do put(udbResTableList, \j) | ||
every k := udb_input("print -l", 1) do put(udbResTableList, \k) | ||
every l := udb_input("print -s", 1) do put(udbResTableList, \l) | ||
every m := udb_input("print -p", 1) do put(udbResTableList, \m) | ||
|
||
if *udbResTableList ~= 0 then { | ||
every udbResTable := !udbResTableList do { | ||
if member(udbResTable, "type") then { | ||
if member(udbResTable, "variables") then { | ||
if udbResTable["type"] == "globals" & *udbResTable["variables"] > 0 then { | ||
put(scopes, table("name", "Globals", "variablesReference", 1)) | ||
} | ||
if udbResTable["type"] == "locals" & *udbResTable["variables"] > 0 then { | ||
put(scopes, table("name", "Locals", "variablesReference", 2)) | ||
} | ||
if udbResTable["type"] == "statics" & *udbResTable["variables"] > 0 then { | ||
put(scopes, table("name", "Statics", "variablesReference", 3)) | ||
} | ||
if udbResTable["type"] == "params" & *udbResTable["variables"] > 0 then { | ||
put(scopes, table("name", "Parameters", "variablesReference", 4)) | ||
} | ||
} | ||
if udbResTable["type"] == "crash" then return udbResTable | ||
} | ||
} | ||
} | ||
|
||
return scopes | ||
end | ||
|
||
# Returns a list of tables containing variable information. | ||
# A table of with key "type" set to "crash" is returned if udb experiences an error. | ||
method get_variables(variablesReference) | ||
local udbResTable, udbResTableList, variables, cmd, i | ||
|
||
udbResTableList := list() | ||
variables := list() | ||
|
||
if variablesReference = 1 then cmd := "print -g" | ||
else if variablesReference = 2 then cmd := "print -l" | ||
else if variablesReference = 3 then cmd := "print -s" | ||
else if variablesReference = 4 then cmd := "print -p" | ||
|
||
every i := udb_input(cmd, 1) do put(udbResTableList, \i) | ||
|
||
if *udbResTableList ~= 0 then { | ||
every udbResTable := !udbResTableList do { | ||
if member(udbResTable, "type") then { | ||
if member(udbResTable, "variables") then { | ||
if udbResTable["type"] == "globals" | udbResTable["type"] == "locals" | udbResTable["type"] == "statics" | udbResTable["type"] == "params" then { | ||
variables := udbResTable["variables"] | ||
} | ||
} | ||
if udbResTable["type"] == "crash" then return udbResTable | ||
} | ||
} | ||
} | ||
|
||
every variable := !variables do { | ||
variable["value"] := replace(variable["value"], "\"", "\\\"") | ||
variable["type"] := replace(variable["type"], "\"", "\\\"") | ||
variable["variablesReference"] := 0 | ||
} | ||
|
||
return variables | ||
end | ||
|
||
# Generator that suspends all udb commands needed for loading debuggee. | ||
method load_cmds() | ||
local dir, procs | ||
|
||
filePath ? dir := tab(findre("\/[^\/]+$")) | ||
suspend "dir args " || dir | ||
|
||
if procs := find_debugger() then { | ||
procs ? procs := tab(findre("unicon") + 6) | ||
procs ||:= "/ipl/procs" | ||
suspend "dir args " || procs | ||
} | ||
|
||
if \tpArgs then suspend "load " || filePath || " " || tpArgs | ||
else suspend "load " || filePath | ||
end | ||
|
||
# Sets the file path of the debuggee. | ||
method set_filepath(fpath) | ||
filePath := fpath | ||
end | ||
|
||
# Attempts to open a specified port. Returns communication source if successful. | ||
method open_sock(port) | ||
local sock | ||
if /port then return "udb communication port not declared" | ||
|
||
every 1 to 5 do { | ||
if sock := open(":" || port, "na") then { | ||
return sock | ||
} | ||
else { | ||
write("Attempting to open sock on port " || port || " again.") | ||
delay(1000) | ||
} | ||
} | ||
|
||
write("udap failed to open port: " || port) | ||
end | ||
|
||
# Disconnects from udb. | ||
method disconnect_udbsock() | ||
close(\udbSock) | ||
udbSock := &null | ||
end | ||
|
||
# Attempts to read udb socket output. | ||
# Returns what was read or fails if reading isn't possible. | ||
# If 'wait' is not null, process will wait a maximum of 5 seconds for udb to respond. | ||
# 'wait' should be not null if udap needs a response from udb. | ||
method udb_output(wait) | ||
local msg, i, seg, failsafe | ||
|
||
failsafe := 0 | ||
msg := "" | ||
if /wait then { | ||
while *select(udbSock, 200) > 0 do { | ||
if failsafe > 50 then fail | ||
seg := ready(udbSock) | ||
if /seg then fail | ||
msg ||:= seg | ||
failsafe +:= 1 | ||
} | ||
} | ||
else { | ||
if *select(udbSock, 5000) > 0 then { | ||
seg := ready(udbSock) | ||
if /seg then fail | ||
msg ||:= seg | ||
} | ||
while *select(udbSock, 200) > 0 do { | ||
if failsafe > 50 then fail | ||
seg := ready(udbSock) | ||
if /seg then fail | ||
msg ||:= seg | ||
failsafe +:= 1 | ||
} | ||
} | ||
|
||
write("udb -> udap: " || msg) | ||
return msg | ||
end | ||
|
||
# Sends udb a command as 'exp' and returns what was output. | ||
# A table of with key "type" set to "crash" is returned if udb experiences an error or if reading isn't possible. | ||
# If 'wait' is not null, process will wait a maximum of 5 seconds for udb to respond. | ||
# 'wait' should be not null if udap needs a response from udb. | ||
# Will always return a crash table if a crash has happened unless if a call with 'resetError' not null has been executed. | ||
method udb_input(exp, wait, resetError) | ||
local udbRes, resultTable | ||
static errorCalled | ||
initial errorCalled := &null | ||
|
||
if \resetError then { | ||
errorCalled := &null | ||
return | ||
} | ||
|
||
if \errorCalled then return errorCalled | ||
|
||
write("udap -> udb: " || exp) | ||
write(udbSock, exp) | ||
udbRes := udb_output(wait) | ||
|
||
if /udbRes then { | ||
resultTable := table("type", "crash", "errornumber", 1040) | ||
resultTable["errortext"] := "socket error" | ||
errorCalled := resultTable | ||
return resultTable | ||
} | ||
|
||
if udbRes ~== "" then { | ||
every resultTable := jtou(udbRes) do { | ||
if member(resultTable, "type") then | ||
if resultTable["type"] == "crash" then | ||
errorCalled := resultTable | ||
suspend resultTable | ||
} | ||
} | ||
end | ||
|
||
# Returns the communcation source used to communicate with udb. | ||
method get_communication_source() | ||
if \udbSock then return udbSock | ||
end | ||
|
||
# Attempts to set breakpoints given DAP setBreakpoints request as 'arguments'. | ||
# Gives a "verified" key to each breakpoint table and sets it based on if the breakpoint was successfully set or not. | ||
# A table of with key "type" set to "crash" is returned if udb experiences an error. | ||
method set_breakpoints(arguments) | ||
local breakpoints, bp, line, cond, udbResTable, udbResTableList, i | ||
|
||
udbResTableList := list() | ||
|
||
udb_input("clear break", 1) | ||
|
||
breakpoints := arguments["breakpoints"] | ||
if *breakpoints = 0 then fail | ||
|
||
every bp := 1 to *breakpoints do { | ||
line := breakpoints[bp]["line"] | ||
cond := \breakpoints[bp]["condition"] | ||
|
||
every i := udb_input("b " || arguments["source"]["name"] || ":" || line, 1) do put(udbResTableList, \i) | ||
|
||
if *udbResTableList ~= 0 then { | ||
every udbResTable := !udbResTableList do { | ||
if member(udbResTable, "type") then | ||
if udbResTable["type"] == "crash" then return udbResTable | ||
breakpoints[bp]["verified"] := udbResTable["success"] | ||
} | ||
} | ||
else breakpoints[bp]["verified"] := "__false__" | ||
} | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
# | ||
# relay communication between the target program's std in/out, and udap socket. | ||
# | ||
global sock | ||
procedure main(argv) | ||
port := (if &features == "MacOS" then "127.0.0.1" else "") || ":" || pop(argv) | ||
every trap("SIGINT" | "SIGHUP" | "SIGPIPE", onExit) | ||
|
||
every !5 do | ||
if sock := open(port, "n") then | ||
break | ||
else | ||
delay(1000) | ||
|
||
\sock | stop("failed to connect to ",port, " ", &errortext ) | ||
repeat every s := !select([sock, &input]) do | ||
writes(ready(s === sock)) | writes(sock, ready()) | ||
end | ||
|
||
procedure onExit(non) | ||
close(\sock) | ||
stop() | ||
end |
Oops, something went wrong.