Add HTML rendering
This commit is contained in:
parent
5548c7d1c6
commit
700ca3db7f
3 changed files with 266 additions and 0 deletions
46
src/html_renderer.ml
Normal file
46
src/html_renderer.ml
Normal file
|
@ -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
|
||||
)
|
67
src/render_html.jingoo
Normal file
67
src/render_html.jingoo
Normal file
|
@ -0,0 +1,67 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
body {
|
||||
font-family: "mono";
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.subroutine {
|
||||
margin: 20px 0;
|
||||
}
|
||||
.sub_body {
|
||||
margin-left: 4ex;
|
||||
}
|
||||
|
||||
.addr {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.instr_bytes pre {
|
||||
display: inline;
|
||||
font-size: 12px;
|
||||
font-family: "mono";
|
||||
}
|
||||
|
||||
.instr_bytes {
|
||||
color: grey;
|
||||
}
|
||||
|
||||
.instr_box {
|
||||
background-color: #ffffb4;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
{% for subroutine in subroutines %}
|
||||
<div class="subroutine">
|
||||
<div class="sub_headline">
|
||||
<span class="addr">{{ subroutine.sub_addr }}</span>
|
||||
<span class="sub_section">[{{ subroutine.sub_section }}]</span>
|
||||
<span class="sub_name"><{{ subroutine.sub_name }}></span>:
|
||||
</div>
|
||||
<div class="sub_body">
|
||||
{% for row in subroutine.sub_asm %}
|
||||
{% for event in row.instr_events %}
|
||||
{% if event.typ == "start" %}
|
||||
<div class="instr_box" id="instr_box_{{ event.id }}">
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<div class="sub_instr">
|
||||
<span class="addr instr_addr">{{ row.instr_addr }}</span>
|
||||
<span class="instr_bytes"><pre>{{ row.instr_bytes }}</pre></span>
|
||||
<span class="instr_asm">{{ row.instr_asm }}</span>
|
||||
</div>
|
||||
{% for event in row.instr_events %}
|
||||
{% if event.typ == "end" %}
|
||||
</div> <!-- END instr_box_{{ event.id }} -->
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</body>
|
||||
</html>
|
153
src/renderer.ml
Normal file
153
src/renderer.ml
Normal file
|
@ -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
|
Loading…
Reference in a new issue