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
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
(** {1 Some helpers} *)
module Fmt = CCFormat
(** {2 Time facilities} *)
type timestamp = float
let timestamp_sub a b = a -. b
let timestamp_add a b = a +. b
let timestamp_cmp = CCFloat.compare
let get_time_mon_() : timestamp = Unix.gettimeofday()
let start_ = get_time_mon_()
let total_time_s () = timestamp_sub (get_time_mon_()) start_
let start_time () = start_
(** {2 Misc} *)
(** Debug section *)
module Section = struct
let null_level = -1
type t = {
descr : descr;
full_name : string;
mutable level : int;
mutable cur_level: int lazy_t;
}
and descr =
| Root
| Sub of string * t * t list
let[@inline] cur_level s = Lazy.force s.cur_level
let compute_cur_level_ s =
if s.level <> null_level then s.level
else match s.descr with
| Root -> 0
| Sub (_, parent, []) -> cur_level parent
| Sub (_, parent, [i]) -> max (cur_level parent) (cur_level i)
| Sub (_, parent, inheriting) ->
List.fold_left
(fun m i -> max m (cur_level i))
(cur_level parent) inheriting
let mk ?(level=null_level) descr full_name : t =
let rec self = {
descr; full_name; level; cur_level= lazy (compute_cur_level_ self);
} in
self
let root : t = mk ~level:0 Root ""
let compute_full_name (d:descr) =
let buf = Buffer.create 15 in
let rec add d = match d with
| Root -> true
| Sub (name, parent, _) ->
let parent_is_root = add parent.descr in
if not parent_is_root then Buffer.add_char buf '.';
Buffer.add_string buf name;
false
in
ignore (add d);
Buffer.contents buf
let full_name s = s.full_name
let section_table = Hashtbl.create 15
let invalidate_cache () =
root.cur_level <- lazy (compute_cur_level_ root);
Hashtbl.iter (fun _ s -> s.cur_level <- lazy (compute_cur_level_ s)) section_table
let set_debug s i = assert (i>=0); s.level <- i; invalidate_cache ()
let clear_debug s = s.level <- null_level; invalidate_cache()
let get_debug s =
if s.level=null_level then None else Some s.level
let make ?(parent=root) ?(inheriting=[]) name =
if name="" then invalid_arg "Section.make: empty name";
let descr = Sub(name, parent, inheriting) in
let name' = compute_full_name descr in
try
Hashtbl.find section_table name'
with Not_found ->
let sec = mk descr name' in
Hashtbl.add section_table name' sec;
sec
let iter yield =
yield ("", root);
Hashtbl.iter (fun name sec -> yield (name,sec)) section_table
end
let break_on_debug = ref false
let wait_user_input () =
ignore (input_line stdin)
let set_debug = Section.set_debug Section.root
let get_debug () = Section.root.Section.level
let debug_fmt_ = Format.std_formatter
let debugf_real ~section msg k =
let now = total_time_s() in
if section == Section.root
then Format.fprintf debug_fmt_ "@{<Black>@[<4>%.3f[]@}@ " now
else Format.fprintf debug_fmt_ "@{<Black>@[<4>%.3f[%s]@}@ "
now section.Section.full_name;
k (Format.kfprintf
(fun fmt ->
Format.fprintf fmt "@]@.";
if !break_on_debug then wait_user_input();
)
debug_fmt_ msg)
let[@inline] debugf ?(section=Section.root) l msg k =
if l <= Section.cur_level section then (
debugf_real ~section msg k
)
let[@inline] debug ?section l msg = debugf ?section l "%s" (fun k->k msg)
let ksprintf_noc ~f fmt =
let buf = Buffer.create 32 in
let out = Format.formatter_of_buffer buf in
Format.kfprintf
(fun _ -> Format.pp_print_flush out (); f (Buffer.contents buf)) out fmt
let pp_error_prefix out () = Format.fprintf out "@{<Red>Error@}: "
let err_spf fmt =
Fmt.ksprintf fmt
~f:(fun s -> Fmt.sprintf "@[%a@,%s@]" pp_error_prefix () s)
let warn_fmt_ = Format.err_formatter
let warnf msg =
Format.fprintf warn_fmt_ "@[<2>@{<Magenta>[Warning]@}: ";
Format.kfprintf
(fun out -> Format.fprintf out "@]@.")
Format.err_formatter msg
let warn msg = warnf "%s" msg
exception Error of string * string
let () =
Printexc.register_printer
(function
| Error (where,msg) ->
Some (err_spf "error in %s:@ %s" where msg)
| Invalid_argument msg ->
Some (err_spf "@[<2>invalid_argument: %s@]" msg)
| _ -> None)
let error ~where msg = raise (Error (where,msg))
let errorf ~where msg = Fmt.ksprintf ~f:(error ~where) msg
let pp_pos pos =
let open Lexing in
Printf.sprintf "line %d, column %d" pos.pos_lnum (pos.pos_cnum - pos.pos_bol)
external set_memory_limit_stub : int -> unit = "logtk_set_memory_limit"
let set_memory_limit n =
if n <= 0 then invalid_arg "set_memory_limit: expect positive arg";
debugf 1 "limit memory to %d MB" (fun k-> k n);
set_memory_limit_stub n
external set_time_limit_stub : int -> unit = "logtk_set_time_limit"
let set_time_limit n =
if n <= 0 then invalid_arg "set_time_limit: expect positive arg";
debugf 1 "limit time to %ds" (fun k->k n);
set_time_limit_stub n
module Exn = struct
let pp_stack buf d =
Printf.bprintf buf "\nstack:\n%s"
(Printexc.raw_backtrace_to_string (Printexc.get_callstack d))
let fmt_stack out d =
Format.fprintf out "\nstack:\n%s"
(Printexc.raw_backtrace_to_string (Printexc.get_callstack d))
let pp_backtrace buf () =
if Printexc.backtrace_status () then (
Buffer.add_string buf "\nbacktrace:\n";
Buffer.add_string buf (Printexc.get_backtrace ())
)
let fmt_backtrace out () =
if Printexc.backtrace_status () then
Format.fprintf out "\nbacktrace:\n%s" (Printexc.get_backtrace ())
let string_of_backtrace () =
if Printexc.backtrace_status ()
then "\nbacktrace:\n" ^ Printexc.get_backtrace ()
else "<no backtrace>"
end
(** {2 profiling facilities} *)
(** A profiler (do not call recursively) *)
type profiler = {
prof_name : string;
mutable prof_total : timestamp;
mutable prof_calls : int;
mutable prof_max : timestamp;
mutable prof_enter : timestamp;
}
let enable_profiling = ref false
let profilers = ref []
let mk_profiler name =
let prof = {
prof_name = name;
prof_enter = 0.;
prof_total = 0.;
prof_calls = 0;
prof_max = 0.;
} in
profilers := prof :: !profilers;
prof
let enter_prof profiler =
if !enable_profiling
then profiler.prof_enter <- get_time_mon_ ()
let exit_prof profiler =
if !enable_profiling then (
let stop = get_time_mon_ () in
let delta = timestamp_sub stop profiler.prof_enter in
profiler.prof_total <- timestamp_add profiler.prof_total delta;
profiler.prof_calls <- profiler.prof_calls + 1;
if delta > profiler.prof_max then profiler.prof_max <- delta;
)
let with_prof p f x =
if !enable_profiling then (
enter_prof p;
try
let y = f x in
exit_prof p;
y
with e ->
exit_prof p;
raise e
) else f x
let show_profilers out () =
Format.fprintf out "@[<v>";
Format.fprintf out
"@[%39s ---------- --------- --------- --------- ---------@]@,"
(String.make 39 '-');
Format.fprintf out
"@[%-39s %10s %9s %9s %9s %9s@]@,"
"function" "#calls" "total" "% total" "max" "average";
let profilers =
List.sort
(fun p1 p2 -> - (timestamp_cmp p1.prof_total p2.prof_total))
!profilers
in
let tot = total_time_s ()in
List.iter
(fun profiler -> if profiler.prof_calls > 0 then
Format.fprintf out "@[%-39s %10d %9.4f %9.2f %9.4f %9.4f@]@,"
profiler.prof_name
profiler.prof_calls
profiler.prof_total
(profiler.prof_total *. 100. /. tot)
profiler.prof_max
(profiler.prof_total /. (float_of_int profiler.prof_calls))
)
profilers;
Format.fprintf out "@]";
()
(** Print profiling data upon exit *)
let () =
at_exit
(fun () ->
if !enable_profiling then Format.eprintf "%a@." show_profilers ())
(** {2 Runtime statistics} *)
type stat = string * int64 ref
let mk_stat, print_global_stats =
let stats = ref [] in
(fun name ->
let stat = (name, ref 0L) in
stats := stat :: !stats;
stat),
(fun ~ () ->
let stats = List.sort (fun (n1,_)(n2,_) -> String.compare n1 n2) !stats in
List.iter
(fun (name, cnt) -> Format.printf "%sstat: %-35s ... %Ld@." comment name !cnt)
stats)
let incr_stat (_, count) = count := Int64.add !count Int64.one (** increment given statistics *)
let add_stat (_, count) num = count := Int64.add !count (Int64.of_int num) (** add to stat *)
(** {Flags as integers} *)
module Flag = struct
type gen = int ref
let create () = ref 1
let get_new gen =
let n = !gen in
if n < 0 then failwith "Flag.get_new: too many flags allocated";
gen := 2*n;
n
end
(** {2 Others} *)
let finally ~do_ f =
try
let x = f () in
do_ ();
x
with e ->
do_ ();
raise e
let pp_pair ?(sep=", ") pa pb out (a,b) =
Format.fprintf out "@[%a%s%a@]" pa a sep pb b
let pp_sep sep out () = Format.fprintf out "%s@," sep
let pp_list ?(sep=", ") pp = Fmt.list ~sep:(pp_sep sep) pp
let pp_seq ?(sep=", ") pp = Fmt.seq ~sep:(pp_sep sep) pp
let pp_list0 ?(sep=" ") pp_x out = function
| [] -> ()
| l -> Format.fprintf out " %a" (pp_list ~sep pp_x) l
let tstp_needs_escaping s =
assert (s<>"");
s.[0] = '_' ||
CCString.exists (function ' ' | '#' | '$' | '+' | '-' | '/' -> true | _ -> false) s
let pp_str_tstp out s =
CCFormat.string out (if tstp_needs_escaping s then "'" ^ String.escaped s ^ "'" else s)
let pp_var_tstp out s = CCFormat.string out (CCString.capitalize_ascii s)
let ord_option c o1 o2 = match o1, o2 with
| None, None -> 0
| None, Some _ -> -1
| Some _, None -> 1
| Some x1, Some x2 -> c x1 x2
let take_drop_while f l = CCList.take_while f l, CCList.drop_while f l
let map_product ~f l =
let product a b =
List.fold_left
(fun acc1 l1 -> List.fold_left
(fun acc2 l2 -> (List.rev_append l1 l2) :: acc2)
acc1 b)
[] a
in
match l with
| [] -> []
| l1 :: tail ->
List.fold_left
(fun acc x -> product (f x) acc)
(f l1)
tail
let seq_map_l ~f l =
let rec aux l yield = match l with
| [] -> yield []
| x :: tail ->
let ys = f x in
List.iter
(fun y -> aux tail (fun l -> yield (y::l)))
ys
in
aux l
let seq_zipi seq k =
let i = ref 0 in
seq (fun x -> k (!i, x); incr i)
let invalid_argf msg = Fmt.ksprintf msg ~f:invalid_arg
let failwithf msg = Fmt.ksprintf msg ~f:failwith
module Int_map = CCMap.Make(CCInt)
module Int_set = CCSet.Make(CCInt)
let escape_dot s =
let b = Buffer.create (String.length s + 5) in
String.iter
(fun c ->
begin match c with
| '|' | '\\' | '{' | '}' | '<' | '>' | '"' ->
Buffer.add_char b '\\'; Buffer.add_char b c
| '\n' -> Buffer.add_string b "\\l";
| _ -> Buffer.add_char b c
end)
s;
Buffer.contents b
(** {2 File utils} *)
type 'a or_error = ('a, string) CCResult.t
(** Call given command with given output, and return its output as a string *)
let popen ~cmd ~input : _ or_error =
try
let from, into = Unix.open_process cmd in
output_string into input;
close_out into;
let output = CCIO.read_all from in
ignore (Unix.close_process (from, into));
CCResult.return output
with Unix.Unix_error (e, _, _) ->
let msg = Unix.error_message e in
CCResult.fail msg