summaryrefslogtreecommitdiff
path: root/debugger/symbols.ml
blob: 8ed9b9db76f3ea9232b413acc03521628a97803b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
(**************************************************************************)
(*                                                                        *)
(*                                 OCaml                                  *)
(*                                                                        *)
(*           Jerome Vouillon, projet Cristal, INRIA Rocquencourt          *)
(*           OCaml port by John Malecki and Xavier Leroy                  *)
(*                                                                        *)
(*   Copyright 1996 Institut National de Recherche en Informatique et     *)
(*     en Automatique.                                                    *)
(*                                                                        *)
(*   All rights reserved.  This file is distributed under the terms of    *)
(*   the GNU Lesser General Public License version 2.1, with the          *)
(*   special exception on linking described in the file LICENSE.          *)
(*                                                                        *)
(**************************************************************************)

(* Handling of symbol tables (globals and events) *)

open Instruct
open Debugger_config (* Toplevel *)
open Program_loading
open Debugcom
open Events
module String = Misc.Stdlib.String

let modules =
  ref ([] : string list)

let program_source_dirs =
  ref ([] : string list)

let events_by_pc =
  (Hashtbl.create 257 : (pc, debug_event) Hashtbl.t)
let events_by_module =
  (Hashtbl.create 17 : (string, int * debug_event array) Hashtbl.t)
let all_events_by_module =
  (Hashtbl.create 17 : (string, int * debug_event list) Hashtbl.t)

let partition_modules evl =
  let rec partition_modules' ev evl =
    match evl with
      [] -> [ev],[]
    | ev'::evl ->
       let evl,evll = partition_modules' ev' evl in
       if ev.ev_module = ev'.ev_module then ev::evl,evll else [ev],evl::evll
  in
  match evl with
    [] -> []
  | ev::evl -> let evl,evll = partition_modules' ev evl in evl::evll

let relocate_event orig ev =
  ev.ev_pos <- orig + ev.ev_pos;
  match ev.ev_repr with
    Event_parent repr -> repr := ev.ev_pos
  | _                 -> ()

let read_symbols' bytecode_file =
  let ic = open_in_bin bytecode_file in
  begin try
    Bytesections.read_toc ic;
    ignore(Bytesections.seek_section ic "SYMB");
  with Bytesections.Bad_magic_number | Not_found ->
    prerr_string bytecode_file; prerr_endline " is not a bytecode file.";
    raise Toplevel
  end;
  Symtable.restore_state (input_value ic);
  begin try
    ignore (Bytesections.seek_section ic "DBUG")
  with Not_found ->
    prerr_string bytecode_file; prerr_endline " has no debugging info.";
    raise Toplevel
  end;
  let num_eventlists = input_binary_int ic in
  let dirs = ref String.Set.empty in
  let eventlists = ref [] in
  for _i = 1 to num_eventlists do
    let orig = input_binary_int ic in
    let evl = (input_value ic : debug_event list) in
    (* Relocate events in event list *)
    List.iter (relocate_event orig) evl;
    let evll = partition_modules evl in
    eventlists := evll @ !eventlists;
    dirs :=
      List.fold_left (fun s e -> String.Set.add e s) !dirs (input_value ic)
  done;
  begin try
    ignore (Bytesections.seek_section ic "CODE")
  with Not_found ->
    (* The file contains only debugging info,
       loading mode is forced to "manual" *)
    set_launching_function (List.assoc "manual" loading_modes)
  end;
  close_in_noerr ic;
  !eventlists, !dirs

let clear_symbols () =
  modules := [];
  program_source_dirs := [];
  Hashtbl.clear events_by_pc; Hashtbl.clear events_by_module;
  Hashtbl.clear all_events_by_module

let add_symbols frag all_events =
  List.iter
    (fun evl ->
      List.iter
        (fun ev ->
          Hashtbl.add events_by_pc {frag; pos = ev.ev_pos} ev)
        evl)
    all_events;

  List.iter
    (function
        [] -> ()
      | ev :: _ as evl ->
          let md = ev.ev_module in
          let cmp ev1 ev2 = compare (Events.get_pos ev1).Lexing.pos_cnum
                                    (Events.get_pos ev2).Lexing.pos_cnum
          in
          let sorted_evl = List.sort cmp evl in
          modules := md :: !modules;
          Hashtbl.add all_events_by_module md (frag, sorted_evl);
          let real_evl =
            List.filter
              (function
                 {ev_kind = Event_pseudo} -> false
               | _                        -> true)
              sorted_evl
          in
          Hashtbl.add events_by_module md (frag, Array.of_list real_evl))
    all_events

let read_symbols frag bytecode_file =
  let all_events, all_dirs = read_symbols' bytecode_file in
  program_source_dirs := !program_source_dirs @ (String.Set.elements all_dirs);
  add_symbols frag all_events

let erase_symbols frag =
  let pcs = Hashtbl.fold (fun pc _ pcs ->
      if pc.frag = frag then pc :: pcs else pcs)
    events_by_pc []
  in
  List.iter (Hashtbl.remove events_by_pc) pcs;

  let mds = Hashtbl.fold (fun md (frag', _) mds ->
      if frag' = frag then md :: mds else mds)
    events_by_module []
  in
  List.iter (Hashtbl.remove events_by_module) mds;
  List.iter (Hashtbl.remove all_events_by_module) mds;
  modules := List.filter (fun md -> not (List.mem md mds)) !modules

let code_fragments () =
  let frags =
    Hashtbl.fold
      (fun _ (frag, _) l -> frag :: l)
      all_events_by_module []
  in
  List.sort_uniq compare frags

let modules_in_code_fragment frag' =
  Hashtbl.fold (fun md (frag, _) l ->
      if frag' = frag then md :: l else l)
    all_events_by_module []

let any_event_at_pc pc =
  { ev_frag = pc.frag; ev_ev = Hashtbl.find events_by_pc pc }

let event_at_pc pc =
  match any_event_at_pc pc with
    { ev_ev = { ev_kind = Event_pseudo } } -> raise Not_found
  | ev -> ev

let set_event_at_pc pc =
 try ignore(event_at_pc pc); set_event pc
 with Not_found -> ()

(* List all events in module *)
let events_in_module mdle =
  try
    Hashtbl.find all_events_by_module mdle
  with Not_found ->
    0, []

(* Binary search of event at or just after char *)
let find_event ev char =
  let rec bsearch lo hi =
    if lo >= hi then begin
      if (Events.get_pos ev.(hi)).Lexing.pos_cnum < char
      then raise Not_found
      else hi
    end else begin
      let pivot = (lo + hi) / 2 in
      let e = ev.(pivot) in
      if char <= (Events.get_pos e).Lexing.pos_cnum
      then bsearch lo pivot
      else bsearch (pivot + 1) hi
    end
  in
  if Array.length ev = 0 then
    raise Not_found
  else
    bsearch 0 (Array.length ev - 1)

(* Return first event after the given position. *)
(* Raise [Not_found] if module is unknown or no event is found. *)
let event_at_pos md char =
  let ev_frag, ev = Hashtbl.find events_by_module md in
  { ev_frag; ev_ev = ev.(find_event ev char) }

(* Return event closest to given position *)
(* Raise [Not_found] if module is unknown or no event is found. *)
let event_near_pos md char =
  let ev_frag, ev = Hashtbl.find events_by_module md in
  try
    let pos = find_event ev char in
    (* Desired event is either ev.(pos) or ev.(pos - 1),
       whichever is closest *)
    if pos > 0 && char - (Events.get_pos ev.(pos - 1)).Lexing.pos_cnum
                  <= (Events.get_pos ev.(pos)).Lexing.pos_cnum - char
    then { ev_frag; ev_ev = ev.(pos - 1) }
    else { ev_frag; ev_ev = ev.(pos) }
  with Not_found ->
    let pos = Array.length ev - 1 in
    if pos < 0 then raise Not_found;
    { ev_frag; ev_ev = ev.(pos) }

(* Flip "event" bit on all instructions *)
let set_all_events frag =
  Hashtbl.iter
    (fun pc ev ->
       match ev.ev_kind with
         Event_pseudo -> ()
       | _ when pc.frag = frag -> set_event pc
       | _ -> ())
    events_by_pc

(* Previous `pc'. *)
(* Save time if `update_current_event' is called *)
(* several times at the same point. *)
let old_pc = ref (None : pc option)

(* Recompute the current event *)
let update_current_event () =
  match Checkpoints.current_pc () with
    None ->
      Events.current_event := None;
      old_pc := None
  | (Some pc) as opt_pc when opt_pc <> !old_pc ->
      Events.current_event :=
        begin try
          Some (event_at_pc pc)
        with Not_found ->
          None
        end;
      old_pc := opt_pc
  | _ ->
      ()