(** 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