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
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
; pie : bool (** is this a 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
}
let versions =
let tbl = Hashtbl.create 2 in
Hashtbl.add
tbl
"ocaml"
{ unique_semaphore_per_name = false; separate_semaphore_for_ocaml_handlers = false };
let t1 =
{ unique_semaphore_per_name = true; separate_semaphore_for_ocaml_handlers = true }
in
Hashtbl.add tbl "ocaml.1" t1;
Hashtbl.add tbl "ocaml_1" t1;
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
(match Hashtbl.find_opt acc note.name with
| None ->
let tmp_probe_info =
{ semaphores = Int64_set.singleton semaphore
; sites = Int64_set.singleton note.addr
}
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.add note.addr 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 ~filename =
let fd = Unix.openfile filename [ Unix.O_RDONLY ] 0 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
Unix.close fd;
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 is_pie = function
| 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 (is_pie header.e_type);
{ filename
; pie = is_pie header.e_type
; 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))
;;