blob: 5137cf4d0f61d78c7162ddca49a65e88340529eb [file] [log] [blame] [edit]
open Ds
open Al
open Al_util
open Print
type state =
| Step of int
| StepInstr of string * int
| Continue of int
let debug = ref false
let break_points = ref []
let is_bp name =
List.exists
(fun bp -> bp = String.lowercase_ascii name || bp = String.uppercase_ascii name)
!break_points
let state = ref (Step 1)
let help_msg =
"
s[tep] {number}?
Take n steps
si|stepinstr {number}?
Step over n AL instructions
c[ontinue] {number}?
Continue until the n-th break point
b[reak] {algorithm name}*
Add break points
rm|remove {algorithm name}*
Remove break points
bp|breakpoints
Print all break points
al
Print AL context stack
wasm
Print Wasm context stack
v[ar] {variable name} {field|index}*
Print a value selected from an AL variable
f[rame] {field|index}*
Print a value selected from the current Wasm frame
l[ocal] {index} {field|index}*
Print a value selected from the current Wasm frame's locals
(shorthand for `frame LOCALS`)
m[odule] {field} {index}
Print an index from the current Wasm frame's module
(shorthand for `frame MODULE`)
st[ore] {field|index}*
Print a value from the Wasm store
z|state {field|index}*
Print a value indexed from the current Wasm frame's module
(shorthand for the composition of module and store lookup)
q[uit]
Quit
h[elp]
Print help message
"
let allow_command ctx =
let is_entry name il = name |> lookup_algo |> body_of_algo = il in
match !state with
| Step n ->
if n = 1 then
true
else
(state := Step (n-1); false)
| StepInstr (name, n) when name == AlContext.get_name ctx ->
if n = 1 then
true
else
(state := StepInstr (name, n-1); false)
| Continue n ->
(match ctx with
| AlContext.Al (name, _, il, _, _) :: _
when is_bp name && is_entry name il ->
if n = 1 then
true
else
(state := Continue (n-1); false)
| _ -> false
)
| _ -> false
let rec access_paths paths v =
match paths with
| [] -> v
| path :: t when int_of_string_opt path <> None ->
v
|> unwrap_listv
|> (fun arr -> Array.get !arr (int_of_string path))
|> access_paths t
| path :: t ->
v
|> unwrap_strv
|> List.assoc path
|> (!)
|> access_paths t
let access_store paths =
Store.access (List.hd paths)
|> access_paths (List.tl paths)
let access_frame paths =
WasmContext.get_current_context "FRAME_"
|> args_of_casev
|> Fun.flip List.nth 1
|> access_paths paths
let access_state paths =
if List.length paths < 2 then access_frame ("MODULE" :: paths) else
let field = List.hd paths in
access_frame ["MODULE"; field; List.nth paths 1]
|> unwrap_natv_to_int
|> (fun i -> access_store (field :: string_of_int i :: Util.Lib.List.drop 2 paths))
let access_env (ctx : AlContext.t) s paths =
match ctx with
| (Al (_, _, _, env, _) | Enter (_, _, env)) :: _ ->
lookup_env_opt s env |> Option.get |> access_paths paths
| _ -> failwith "not in scope"
let print_path path =
if int_of_string_opt path <> None then
"[" ^ path ^ "]"
else
"." ^ path
let print_value access root paths =
print_endline (
root ^ String.concat "" (List.map print_path paths) ^
try " = " ^ string_of_value (access paths)
with _ -> " does not exist"
)
let rec do_debug ctx =
let _ = print_string "\ndebugger> " in
read_line ()
|> String.split_on_char ' '
|> List.filter ((<>) "")
|> handle_command ctx
and handle_command ctx = function
| ("h" | "help") :: _ ->
print_endline help_msg;
do_debug ctx
| ("b" | "break") :: t ->
if t = [] then
print_endline (String.concat " " !break_points)
else
break_points := !break_points @ t;
do_debug ctx
| ("bp" | "breakpoints") :: [] ->
print_endline (String.concat " " !break_points);
do_debug ctx
| ("rm" | "remove") :: t ->
break_points := List.filter (fun e -> not (List.mem e t)) !break_points;
do_debug ctx
| ("s" | "step") :: t ->
(match t with
| [] ->
state := Step 1
| [n] when int_of_string_opt n <> None ->
state := Step (int_of_string n)
| _ ->
handle_command ctx ["help"]
)
| ("si" | "stepinstr") :: t ->
(match ctx with
| (AlContext.Al (name, _, il, _, _) | AlContext.Enter (name, il, _)) :: _
when List.length il > 0 ->
(match t with
| [] ->
state := StepInstr (name, 1)
| [n] when int_of_string_opt n <> None ->
state := StepInstr (name, int_of_string n)
| _ ->
handle_command ctx ["help"]
)
| _ ->
handle_command ctx ("step" :: t)
)
| ("c" | "continue") :: t ->
(match t with
| [] ->
state := Continue 1
| [n] when int_of_string_opt n <> None ->
state := Continue (int_of_string n)
| _ ->
handle_command ctx ["help"]
)
| "al" :: [] ->
ctx |> List.map AlContext.string_of_context |> List.iter print_endline;
do_debug ctx
| "wasm" :: [] ->
WasmContext.string_of_context_stack () |> print_endline;
do_debug ctx
| ("st" | "store") :: t ->
print_value access_store "store" t;
do_debug ctx
| ("f" | "frame") :: t ->
print_value access_frame "frame" t;
do_debug ctx
| ("l" | "local") :: t ->
print_value access_frame "frame" ("LOCALS" :: t);
do_debug ctx
| ("m" | "module") :: t ->
print_value access_frame "frame" ("MODULE" :: t);
do_debug ctx
| ("z" | "state") :: t ->
print_value access_state "state" t;
do_debug ctx
| ("v" | "var") :: s :: t ->
print_value (access_env ctx s) s t;
do_debug ctx
| ("q" | "quit") :: [] ->
debug := false
| _ ->
handle_command ctx ["help"]
let run ctx =
if !debug && allow_command ctx then do_debug ctx