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
type section =
{ name : string
; addr : int64
; offset : int64
; size : int64
}
(** Currently, arguments of probes are not used by probes_lib. Each site may have
different arguments. *)
type probe_info =
{ name : string
; semaphores : int64 array (** address of the semaphore corresponding to the probe *)
; sites : int64 array
(** addresses of all the probe sites with the given name and semaphore. *)
}
type t =
{ filename : string
; inode : int
; shared_object : bool
(** is this a shared object or position-independent executable? *)
; probes : (string, probe_info) Hashtbl.t
; text_section : section
; data_section : section
; semaphores_section : section option (** semaphores live in ".probes" section *)
}
let mk_section (section : Owee_elf.section) : section =
{ name = section.sh_name_str
; addr = section.sh_addr
; offset = section.sh_offset
; size = section.sh_size
}
;;
exception Invalid_format of string
let verbose = ref false
let set_verbose b = verbose := b
(** Depends on the compiler version. *)
module Config = struct
type t =
{ unique_semaphore_per_name : bool
; separate_semaphore_for_ocaml_handlers : bool
; probe_site_may_be_absent : bool
}
let versions =
let tbl = Hashtbl.create 3 in
let t =
{ unique_semaphore_per_name = false
; separate_semaphore_for_ocaml_handlers = false
; probe_site_may_be_absent = false
}
in
let t1 =
{ unique_semaphore_per_name = true
; separate_semaphore_for_ocaml_handlers = true
; probe_site_may_be_absent = false
}
in
let t2 =
{ unique_semaphore_per_name = true
; separate_semaphore_for_ocaml_handlers = true
; probe_site_may_be_absent = true
}
in
Hashtbl.add tbl "ocaml" t;
Hashtbl.add tbl "ocaml.1" t1;
Hashtbl.add tbl "ocaml_1" t1;
Hashtbl.add tbl "ocaml_2" t2;
tbl
;;
let get provider = Hashtbl.find_opt versions provider
end
module Int64_set = Set.Make (Int64)
type tmp_probe_info =
{ mutable semaphores : Int64_set.t
; mutable sites : Int64_set.t
}
let check_or_set_provider current new_provider =
match !current with
| None -> current := Some new_provider
| Some cur_provider ->
if not (String.equal cur_provider new_provider)
then
raise
(Invalid_format
(Printf.sprintf
"Executable contains probe notes of different versions %s %s"
cur_provider
new_provider))
;;
let add (note : Owee_elf_notes.Stapsdt.t) ~acc ~provider ~filename =
match Config.get note.provider with
| None -> ()
| Some config ->
check_or_set_provider provider note.provider;
let semaphore =
match note.semaphore with
| None ->
raise
(Invalid_format
(Printf.sprintf
"Semaphore not found for OCaml probe %s at %Lx in %s.\n"
note.name
note.addr
filename))
| Some s ->
if config.separate_semaphore_for_ocaml_handlers then Int64.add s 2L else s
in
let get_sites (note : Owee_elf_notes.Stapsdt.t) =
if Int64.equal note.addr 0L
then
if config.probe_site_may_be_absent
then Int64_set.empty
else
raise
(Invalid_format
(Printf.sprintf
"Unexpected probe site address 0 in elf note for probe %s in file %s\n"
note.name
filename))
else Int64_set.singleton note.addr
in
(match Hashtbl.find_opt acc note.name with
| None ->
let tmp_probe_info =
{ semaphores = Int64_set.singleton semaphore; sites = get_sites note }
in
Hashtbl.add acc note.name tmp_probe_info
| Some ({ semaphores; sites } as tmp_probe_info : tmp_probe_info) ->
if config.unique_semaphore_per_name && not (Int64_set.mem semaphore semaphores)
then
raise
(Invalid_format
(Printf.sprintf
"Mismatch between probe sites in %s:\n\
adding probe %s at %Lx with semaphore at %Lx\n\
previously found at %Lx with semaphore at %Lx\n"
filename
note.name
note.addr
semaphore
(Int64_set.min_elt sites)
(Int64_set.min_elt semaphores)));
tmp_probe_info.sites <- Int64_set.union (get_sites note) sites;
tmp_probe_info.semaphores <- Int64_set.add semaphore semaphores)
;;
let read_notes ~filename map sections =
let acc = Hashtbl.create 13 in
let provider = ref None in
(try Owee_elf_notes.Stapsdt.iter map sections ~f:(add ~acc ~provider ~filename) with
| Owee_elf_notes.Section_not_found _ ->
());
let n = Hashtbl.length acc in
let notes = Hashtbl.create n in
Hashtbl.iter
(fun name { semaphores; sites } ->
let new_note =
{ name
; sites = sites |> Int64_set.to_seq |> Array.of_seq
; semaphores = semaphores |> Int64_set.to_seq |> Array.of_seq
}
in
Hashtbl.add notes name new_note)
acc;
notes
;;
let create ?fd ~filename () =
let inode, map =
let fd, finally =
match fd with
| Some fd -> fd, fun () -> ()
| None ->
let fd = Unix.openfile filename [ Unix.O_RDONLY ] 0 in
fd, fun () -> Unix.close fd
in
Fun.protect ~finally (fun () ->
let inode = (Unix.fstat fd).st_ino in
let len = Unix.lseek fd 0 Unix.SEEK_END in
let map =
Bigarray.array1_of_genarray
(Unix.map_file fd Bigarray.int8_unsigned Bigarray.c_layout false [| len |])
in
inode, map)
in
let , sections = Owee_elf.read_elf map in
let find_section_exn name =
match Owee_elf.find_section sections name with
| None ->
raise
(Invalid_format
(Printf.sprintf "Cannot find ELF section %s in %s\n" name filename))
| Some s -> mk_section s
in
let shared_object =
match header.e_type with
| 2 -> false
| 3 -> true
| e_type ->
raise
(Invalid_format
(Printf.sprintf "unexpected type %d of elf executable %s\n" e_type filename))
in
if !verbose then Printf.printf "header.e_type=%d pie=%b\n" header.e_type shared_object;
{ filename
; inode
; shared_object
; probes = read_notes ~filename map sections
; text_section = find_section_exn ".text"
; data_section = find_section_exn ".data"
; semaphores_section = Option.map mk_section (Owee_elf.find_section sections ".probes")
}
;;
let find_probe_note t name =
match Hashtbl.find_opt t.probes name with
| Some p -> p
| None -> raise (Failure (Printf.sprintf "Probe %s not found" name))
;;