From 700ca3db7f8651db2a764d15a814c5f25d4779b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Mon, 18 Nov 2019 11:34:50 +0100 Subject: [PATCH] Add HTML rendering --- src/html_renderer.ml | 46 +++++++++++++ src/render_html.jingoo | 67 ++++++++++++++++++ src/renderer.ml | 153 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 266 insertions(+) create mode 100644 src/html_renderer.ml create mode 100644 src/render_html.jingoo create mode 100644 src/renderer.ml diff --git a/src/html_renderer.ml b/src/html_renderer.ml new file mode 100644 index 0000000..f8b38cb --- /dev/null +++ b/src/html_renderer.ml @@ -0,0 +1,46 @@ +(** HTML Renderer + + Renders the output of computations on a given program as an HTML webpage. + *) + +(** Box `AnnotAsm.asm_info_t` as a Jingoo tvalue *) +let render_prog_box annotated_prog = Jingoo.Jg_types.(Renderer.AnnotAsm.( + let render_addr addr = (Format.sprintf "%04x" addr) in + List.map (fun render_sub -> + [ + ("sub_section", box_string render_sub.sub_section); + ("sub_name", box_string render_sub.sub_name); + ("sub_addr", box_string (Format.sprintf "%016x" render_sub.sub_addr)); + ("sub_asm", List.map (fun row -> [ + ("instr_addr", box_string (render_addr row.instr_addr)); + ("instr_bytes", box_string ( + Format.asprintf "%a" Asm_acquire.pp_hex_bytes row.instr_bytes + )); + ("instr_asm", box_string row.instr_asm); + ("instr_events", (List.map (fun event -> + let typ, id, bound = Renderer.(match event with + | BoxStart(id, bound) -> "start", id, bound + | BoxEnd(id, bound) -> "end", id, bound + ) in + [ + ("typ", box_string typ); + ("id", box_int id); + ("bound", box_string @@ render_addr bound); + ] |> box_obj) + row.instr_annot.Renderer.events) + |> box_list); + ] |> box_obj) render_sub.sub_asm + |> box_list; + )] |> box_obj + ) annotated_prog + |> box_list + )) + +(** [render render_data] renders the given [render_data] to a string. *) +let render render_data = Jingoo.( + let annotated_prog = Renderer.render_data_to_annotated_asm render_data in + let models = [ + ("subroutines", render_prog_box annotated_prog); + ] in + Jg_template.from_file "src/render_html.jingoo" ~models:models +) diff --git a/src/render_html.jingoo b/src/render_html.jingoo new file mode 100644 index 0000000..d7db314 --- /dev/null +++ b/src/render_html.jingoo @@ -0,0 +1,67 @@ + + + + + + + + {% for subroutine in subroutines %} +
+
+ {{ subroutine.sub_addr }} + [{{ subroutine.sub_section }}] + <{{ subroutine.sub_name }}>: +
+
+ {% for row in subroutine.sub_asm %} + {% for event in row.instr_events %} + {% if event.typ == "start" %} +
+ {% endif %} + {% endfor %} +
+ {{ row.instr_addr }} +
{{ row.instr_bytes }}
+ {{ row.instr_asm }} +
+ {% for event in row.instr_events %} + {% if event.typ == "end" %} +
+ {% endif %} + {% endfor %} + {% endfor %} +
+
+ {% endfor %} + + diff --git a/src/renderer.ml b/src/renderer.ml new file mode 100644 index 0000000..09af1ab --- /dev/null +++ b/src/renderer.ml @@ -0,0 +1,153 @@ +(** Abstract Renderer + + Abstract generic renderer, with tools to ease and unify the output of + computations on a given program in any chosen format. + *) + +module RawAsm = Asm_info.NoAnnot + +type addr_range_t = RawAsm.addr_t * RawAsm.addr_t +type addr_range_tag_t = int +type tag_addr_range_t = TaggedRange of addr_range_tag_t * addr_range_t + +type render_data_t = { + render_prog : RawAsm.asm_info_t ; + render_boxes : tag_addr_range_t list ; +} + +type render_box_event_t = + BoxStart of addr_range_tag_t * RawAsm.addr_t + | BoxEnd of addr_range_tag_t * RawAsm.addr_t + +type asm_annot_type = { + events : render_box_event_t list +} +module AnnotAsm = Asm_info.S(struct type instr_annot_t = asm_annot_type end) + +(** Initializes a rendering data structure on the given program *) +let init_render_data prog = + { render_prog = prog ; + render_boxes = [] + } + +(** [add_box render_data range] adds a box around an address range in + [render_data] and returns the new render data and the newly inserted + element's id *) +let add_box render_data range : render_data_t * int = + let elt_id = (match render_data.render_boxes with + | [] -> 0 + | TaggedRange(prev_id, _)::_ -> prev_id + 1) in + + { + render_data with + render_boxes = (TaggedRange(elt_id, range)) :: render_data.render_boxes + }, elt_id + +(** [to_file renderer render_data path] renders the given [render_data] to a + file at [path] using the provided specific renderer *) +let to_file renderer render_data path = + let handle = open_out path in + render_data + |> renderer + |> output_string handle ; + close_out handle + +(** [to_string renderer render_data] renders the given [render_data] to a + returned string using the provided specific renderer. + This function is provided for unification and clarity, but is an alias for + [renderer]. +*) +let to_string renderer render_data = + renderer render_data + +let extract_event_addr box_event = match box_event with + | BoxStart(_, addr) -> addr + | BoxEnd(_, addr) -> addr + +(** [boxes_to_events render_data] converts the list of boxes of `render_data` + to a list of events, that is, an address-ordered list of + `render_box_event_t` +*) +let boxes_to_events render_data = + let compare_events e1 e2 = + match (compare (extract_event_addr e1) (extract_event_addr e2)) with + | 0 -> (match e1, e2 with + | BoxEnd(_), BoxStart(_) -> -1 + | BoxStart(_), BoxEnd(_) -> 1 + | BoxStart(id1, _), BoxStart(id2, _) -> compare id1 id2 + | BoxEnd(id1, _), BoxEnd(id2, _) -> compare id1 id2 + ) + | n -> n + in + + let unordered_events = List.fold_left + (fun accu (TaggedRange(elt_id, (range_beg, range_end))) -> + (BoxStart(elt_id, range_beg)) + :: (BoxEnd(elt_id, range_end)) + :: accu + ) + [] + render_data.render_boxes + in + let ordered_events = List.sort + compare_events + unordered_events + in + ordered_events + +(** Transforms `render_data` into an `asm_info_t` whose asm lines are annotated + with events, for ease of rendering *) +let render_data_to_annotated_asm render_data : AnnotAsm.asm_info_t = + let render_events = boxes_to_events render_data in + + (* Splits `events` into two lists: a list of events whose addresses match + `addr` and the remaining tail list. *) + let extract_current_events events addr = + let reached_stop event = (extract_event_addr event) > addr in + let rec fold_until accu l = match l with + | (hd::_) as cur_l when reached_stop hd -> (List.rev accu), cur_l + | hd::tl -> fold_until (hd::accu) tl + | [] -> (List.rev accu), [] + in + fold_until [] events + in + + let map_asm render_events lst = + let asm, leftover_events = List.fold_left + (fun (accu, render_events) asm_instr -> + let current_events, future_events = + extract_current_events render_events RawAsm.(asm_instr.instr_addr) + in + ( + { + AnnotAsm.instr_addr = RawAsm.(asm_instr.instr_addr) ; + AnnotAsm.instr_bytes = RawAsm.(asm_instr.instr_bytes) ; + AnnotAsm.instr_asm = RawAsm.(asm_instr.instr_asm) ; + AnnotAsm.instr_annot = { + events = current_events + } + }::accu + ), future_events + ) + ([], render_events) + lst + in + (List.rev asm, leftover_events) + in + let annotated_asm, _ = + List.fold_left (fun (accu, render_events) sub -> + let mapped_asm, n_render_events = + map_asm render_events RawAsm.(sub.sub_asm) in + ( + { + AnnotAsm.sub_section = RawAsm.(sub.sub_section) ; + AnnotAsm.sub_name = RawAsm.(sub.sub_name) ; + AnnotAsm.sub_addr = RawAsm.(sub.sub_addr) ; + AnnotAsm.sub_asm = mapped_asm ; + } :: accu + ), n_render_events + ) + ([], render_events) + render_data.render_prog + in + List.rev annotated_asm