Skip to content

Commit

Permalink
udap: Added udb and socket error handling
Browse files Browse the repository at this point in the history
Signed-off-by: mstreeter10 <[email protected]>
  • Loading branch information
mstreeter10 committed Jan 7, 2024
1 parent ac74c42 commit e978a6e
Show file tree
Hide file tree
Showing 6 changed files with 344 additions and 190 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ uni/xml/testnotwf
uni/xml/testvalid
uni/ulsp/ulsp
uni/ulsp/udap/udap
uni/ulsp/udap/progcom

# uflex
uni/uflex/flexgram.icn
Expand Down
2 changes: 1 addition & 1 deletion uni/ulsp/udap/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@ zip:
zip udap.zip Makefile *.icn

clean:
$(RM) -f *.u $(prog) uniclass*
$(RM) -f *.u $(prog) uniclass* progcom
264 changes: 173 additions & 91 deletions uni/ulsp/udap/communicator.icn
Original file line number Diff line number Diff line change
Expand Up @@ -3,119 +3,127 @@ package udap
link findre
import json

class Communicator(udb, udbSock, progSock, filePath, debuggerActive)
class Communicator(udb, udbSock, progSock, progComSock, filePath, progArgs)

# 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

if debuggerActive == "n" then {
udbPath := find_debugger()
if /udbPath then return "udap could not find udb"
udbPath := find_debugger()
if /udbPath then return "udap could not find udb"

if udb := open(udbPath || " -adapter " || port, "pw") then debuggerActive := "y"
else return "udap could not start udb"
}
udb := system(udbPath || " -adapter " || port, &null, &null, &null, "nowait")

udbSock := open_sock(port)
if /udbSock then return "udap failed to open udbSock: " || port

result := connect_udbsock(port)
if result ~== "success" then {
return result
}
progSock := open_sock(port + 10)
if /progSock then return "udap failed to open progSock: " || port + 10

return "success"
end

# Send a termination signal to udb.
method end_debugger()
close(udb)
debuggerActive := "n"
kill(\udb, 9)
end

# Returns udb's absolute path.
method find_debugger()
return pathfind("udb")
end

method stackTrace()
local udbRes, udbResTable, udbResList

udbRes := ""
udbResList := list()
# 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

write(udbSock, "bt")
udbResTableList := list()
frames := list()

#in json
udbRes := udb_output()

if udbRes ~== "" then {
every udbResTable := jtou(udbRes) do {
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(udbResList, udbResTable)
put(frames, udbResTable)
}
if udbResTable["type"] == "crash" then return udbResTable
}
}
}

return udbResList
return frames
end

method getScopes(frame)
local udbRes, udbResTable, scopes
# 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

udbRes := ""
udbResTableList := list()
scopes := list()

write(udbSock, "frame " || frame)
write(udbSock, "print -g")
write(udbSock, "print -l")
write(udbSock, "print -s")
write(udbSock, "print -p")
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)

#in json
udbRes := udb_output()

if udbRes ~== "" then {
every udbResTable := jtou(udbRes) do {
if member(udbResTable, "type") & 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 *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

method getVariables(variablesReference)
local udbRes, udbResTable, variables
# 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

udbRes := ""
udbResTableList := list()
variables := list()

if variablesReference = 1 then write(udbSock, "print -g")
if variablesReference = 2 then write(udbSock, "print -l")
if variablesReference = 3 then write(udbSock, "print -s")
if variablesReference = 4 then write(udbSock, "print -p")
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"

#in json
udbRes := udb_output()
every i := udb_input(cmd, 1) do put(udbResTableList, \i)

if udbRes ~== "" then {
every udbResTable := jtou(udbRes) do {
if member(udbResTable, "type") & member(udbResTable, "variables") then {
if udbResTable["type"] == "globals" | udbResTable["type"] == "locals" | udbResTable["type"] == "statics" | udbResTable["type"] == "params" then {
variables := udbResTable["variables"]
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
}
}
}
Expand All @@ -129,69 +137,141 @@ class Communicator(udb, udbSock, progSock, filePath, debuggerActive)
return variables
end

# Generator that suspends all udb commands needed for loading debuggee.
method load_cmds()
local dir
local dir, procs

filePath ? dir := tab(findre("\/[^\/]+$"))
suspend "dir args " || dir
suspend "load " || filePath

if procs := find_debugger() then {
procs ? procs := tab(findre("unicon") + 6)
procs ||:= "/ipl/procs"
suspend "dir args " || procs
}

if \progArgs then suspend "load " || filePath || " " || progArgs
else suspend "load " || filePath
end

# Sets the file path of the debuggee.
method set_filepath(fpath)
filePath := fpath
end

method connect_udbsock(port)
# 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"

if \udbSock then return "success"

every 1 to 5 do {
if udbSock := open(":" || port, "n") then {
return "success"
if sock := open(":" || port, "na") then {
return sock
}
else {
write("Attempting to open sock on port " || port || " again.")
delay(1000)
}
}

return "udap failed to connect to udb on port: " || port
write("udap failed to open port: " || port)
end

# Disconnects from udb.
method disconnect_udbsock()
close(udbSock)
close(\udbSock)
udbSock := &null
end

method udb_output()
local msg
# 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 := ""
while *select(udbSock, 200) > 0 do {
msg ||:= ready(udbSock)
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

method udb_input(exp)
local udbRes := "", resultTable
# 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()
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 suspend resultTable
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, res, resultTable
local breakpoints, bp, line, cond, udbResTable, udbResTableList, i

udbResTableList := list()

write(udbSock, "clear break")
udb_output()
udb_input("clear break", 1)

breakpoints := arguments["breakpoints"]
if *breakpoints = 0 then fail
Expand All @@ -200,14 +280,16 @@ class Communicator(udb, udbSock, progSock, filePath, debuggerActive)
line := breakpoints[bp]["line"]
cond := \breakpoints[bp]["condition"]

write(udbSock, "b " || arguments["source"]["name"] || ":" || line)
res := udb_output()
every i := udb_input("b " || arguments["source"]["name"] || ":" || line, 1) do put(udbResTableList, \i)

if res ~== "" & resultTable := jtou(res) then breakpoints[bp]["verified"] := resultTable["success"]
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

initially
debuggerActive := "n"
end
Binary file removed uni/ulsp/udap/progcom
Binary file not shown.
Loading

0 comments on commit e978a6e

Please sign in to comment.