elf_arrows/src/renderer.ml
Théophile Bastian e90e51fc2a Match with file names instead of file ids
It seems possible that two runs of eg. gcc at different levels of
optimisation on the same files assigns different IDs to the same files.
To circumvent this, we use the file paths instead of file IDs.
2019-11-22 12:46:47 +01:00

218 lines
6.5 KiB
OCaml

(** 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 box_data_t = {
box_file : string;
box_line : int;
box_col : int;
box_class_id : int;
}
type addr_range_tag_t = int
type tag_addr_range_t = TaggedRange of addr_range_tag_t * addr_range_t *
box_data_t
module RangeTagMap = Map.Make(struct
type t = addr_range_tag_t
let compare = compare
end)
type render_data_t = {
render_prog_path : string ;
render_prog : RawAsm.asm_info_t ;
render_boxes : tag_addr_range_t list ;
render_prev_address : RawAsm.addr_t RawAsm.AddrMap.t ;
}
type multi_render_data_t = render_data_t list
type render_box_event_t =
BoxStart of addr_range_tag_t * RawAsm.addr_t * box_data_t
| BoxEnd of addr_range_tag_t * RawAsm.addr_t * box_data_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)
(** Make a map mapping addresses to the preceding instruction address appearing
in `prog` *)
let make_prev_address prog =
let _, addr_map =
List.fold_left (fun (prev_addr, cmap) sub ->
List.fold_left (fun (prev_addr, cmap) instr ->
(instr.RawAsm.instr_addr,
RawAsm.AddrMap.add instr.RawAsm.instr_addr prev_addr cmap)
)
(prev_addr, cmap)
sub.RawAsm.sub_asm
)
(0, RawAsm.AddrMap.empty)
prog
in
addr_map
(** Initializes a rendering data structure on the given program *)
let init_render_data prog path =
{
render_prog_path = path ;
render_prog = prog ;
render_boxes = [] ;
render_prev_address = make_prev_address prog ;
}
let fresh_box_id =
let next_id = ref 0 in
fun () ->
let out = !next_id in
incr next_id;
out
(** [add_box render_data range] adds a box around an address range, inclusive
of both bounds, in [render_data] and returns the new render data and the
newly inserted element's id *)
let add_box render_data range opt_data : render_data_t * int =
let elt_id = fresh_box_id () in
let data = (match opt_data with
| Some data -> data
| None -> {
box_file = "";
box_line = 0;
box_col = 0;
box_class_id = 0;
})
in
{
render_data with
render_boxes =
(TaggedRange(elt_id, range, data)) :: render_data.render_boxes
}, elt_id
(** Same as [add_box], with an included start, excluded end for range. *)
let add_box_excl render_data (addr_beg, addr_end) opt_data =
let lower_than_end addr = addr <= addr_end in
(match RawAsm.AddrMap.find_last_opt
lower_than_end render_data.render_prev_address with
| None ->
Format.eprintf "Box end address %x not found, ignoring box.@."
addr_end ;
raise Not_found
| Some (_, end_bound) -> add_box render_data (addr_beg, end_bound) opt_data
)
(** [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 multi_render_data] renders the given
[multi_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 multi_render_data =
renderer multi_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), data)) ->
(BoxStart(elt_id, range_beg, data))
:: (BoxEnd(elt_id, range_end, data))
:: 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