package codex

  1. Overview
  2. Docs
The Codex library for building static analysers based on abstract interpretation

Install

dune-project
 Dependency

Authors

Maintainers

Sources

1.0-rc4.tar.gz
md5=bc7266a140c6886add673ede90e335d3
sha512=8da42c0ff2c1098c5f9cb2b5b43b306faf7ac93b8f5ae00c176918cee761f249ff45b29309f31a05bbcf6312304f86a0d5a000eb3f1094d3d3c2b9b4c7f5c386

doc/src/codex.tracelog/tracelog.ml.html

Source file tracelog.ml

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
(**************************************************************************)
(*  This file is part of the Codex semantics library.                     *)
(*                                                                        *)
(*  Copyright (C) 2013-2025                                               *)
(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
(*         alternatives)                                                  *)
(*                                                                        *)
(*  you can redistribute it and/or modify it under the terms of the GNU   *)
(*  Lesser General Public License as published by the Free Software       *)
(*  Foundation, version 2.1.                                              *)
(*                                                                        *)
(*  It is distributed in the hope that it will be useful,                 *)
(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
(*  GNU Lesser General Public License for more details.                   *)
(*                                                                        *)
(*  See the GNU Lesser General Public License version 2.1                 *)
(*  for more details (enclosed in the file LICENSE).                      *)
(*                                                                        *)
(**************************************************************************)

(* -*- compile-command: "OCAMLRUNPARAM=\"b\" ocamlfind ocamlc common.ml terminal.mli terminal.ml tracelog.mli tracelog.ml -package unix -linkpkg && ./a.out" -*- *)

include Common;;

let log_binary_trace = ref false
let print_backtraces = ref true
let set_log_binary_trace bool = log_binary_trace := bool
type 'a printf =  ('a, Format.formatter, unit) format -> 'a;;
type 'a log = 'a printf -> unit;;
type location = ..

module type S  = sig
  val error : 'a log -> unit
  val warning : 'a log -> unit
  val notice : 'a log -> unit
  val info : 'a log -> unit
  val debug : 'a log -> unit
  val fatal : 'a log -> 'b
  val not_yet_implemented : 'a log -> unit
  val trace: 'a log -> ?loc:location -> ?bintrace:_ Syntax_tree.Location_identifier.t -> ?pp_ret:(Format.formatter -> 'b -> unit) -> (unit -> 'b) -> 'b
  val fatal_handler: ('a -> 'b) -> ('a -> 'b)
end;;

module Dummy: S = struct
  let error _ = ()
  let warning _ = ()
  let notice _ = ()
  let info _ = ()
  let debug _ = ()
  let fatal _ =
    raise (Fatal "dummy")
  let not_yet_implemented = fatal
  let trace _ ?loc:_ ?bintrace:_ ?pp_ret:_ f = f ()
  let fatal_handler f x =
    try f x with
    | Fatal _ -> exit 1
    | _ -> exit 2
end

(* TODO: Use standard log levels, liike syslog
   debug; debug-level messages.
   info;  informational. Those are normally not displayed.
   notice; significant
   warning;
   error;
   critical;

   ;; traces occur on the info and debug levels.

 *)
module Level = struct
  let critical = 2            (* Always printed: the reason why we use this program. *)
  let error = 3               (* The program does not run normally. *)
  let warning = 4             (* There is possibly a problem.  *)
  let notice = 5              (* All messages above this level are displayed. *)
  let info = 6                (* Normally not displayed. *)
  let trace = info            (* Tracing information is normally not displayed. *)
  let debug = 7               (* Normally not displayed; extra verbose. *)
end;;


let current_level = ref Level.warning;; (* Print warnings but not feedback messages by default. *)

let set_verbosity_level x =
  let open Level in
  let vb = match x with
    | `Critical -> critical
    | `Error -> error
    | `Warning -> warning
    | `Info -> info
    | `Notice -> notice
    | `Debug -> debug
  in current_level := vb

let get_verbosity_level() =
  let open Level in
  let current_level = !current_level in
  if current_level == error then `Error
  else if current_level == warning then `Warning
  else if current_level == info then `Info
  else if current_level == notice then `Notice
  else if current_level == debug then `Debug
  else assert false
;;

let location_stack = ref [];;

let reset_location_stack () = location_stack := []

let current_location() = match !location_stack with
  | [] -> None
  | hd::_ -> Some hd
let current_location_stack() = !location_stack

let r_pp_location_stack = ref (fun fmt locstack ->
    match locstack with
    | [] -> ()
    | _ -> Format.fprintf fmt "<cannot print location>"
  )
;;

let set_pp_location_stack f = r_pp_location_stack := f;;

let pp_location_stack fmt loc =
  (* If we already print trace information, do not reprint the position. *)
  if !current_level >= Level.trace
  then ()
  else !r_pp_location_stack fmt loc
;;

module MakeGeneric
    (V:sig val get_verbosity_level: unit -> int end)
    (Category:sig val category:string end) = struct

  let maybe_log_bintrace level log =
    if !log_binary_trace then begin
      (Binarytrace.add_trace_to_file (Category.category)
         (Single {category=Category.category;
                  severity = level;
                  content= Format.asprintf "%t" (fun fmt -> log (Format.fprintf fmt))}))
    end


  let print ?(last=false) f = f (Terminal.printf Category.category ~last)

  (* Note: reusing the buffer is not faster. *)
  let string_of_log: type a. a log -> string = fun log ->
    let buf = Buffer.create 150 in
    let ppf = Format.formatter_of_buffer buf in
    log (Format.fprintf ppf);
    Format.pp_print_flush ppf ();
    Buffer.contents buf
  ;;

  (* Not sure if this is useful. *)
  let log_of_string str : 'a log = fun m -> m (format_of_string "%s") str

  let log_concat (log1:'a log) (log2:'b log) : 'c log =
    fun (m:'c printf) -> m "%t%t" (fun fmt -> log1 (Format.fprintf fmt)) (fun fmt -> log2 (Format.fprintf fmt))
  ;;

  let print_at_level level f =
    if V.get_verbosity_level() >= level
    then (maybe_log_bintrace level f;
          print f)
  ;;

  let notice f = print_at_level Level.notice f
  let info f = print_at_level Level.info f

  let generic_error level underlined_title log =
    if V.get_verbosity_level() >= Level.error
    then begin
      maybe_log_bintrace level log;
      let colored_log fmt =
        Format.pp_open_stag fmt (Weight Bold);
        Format.pp_open_stag fmt (Color Red);
        pp_location_stack fmt !location_stack;
        Format.pp_open_stag fmt (Underline true);
        Format.fprintf fmt underlined_title ;
        Format.pp_close_stag fmt ();
        Format.fprintf fmt ": %t" (fun fmt -> log (Format.fprintf fmt));
        Format.pp_close_stag fmt ();
        Format.pp_close_stag fmt ()
      in
      print (fun p -> p "%t" colored_log)
    end
  ;;
  let error log = generic_error Level.error "Error" log;;

  let warning log =
    if V.get_verbosity_level() >= Level.error
    then begin
      maybe_log_bintrace Level.warning log;
      let colored_log fmt =
        Format.pp_open_stag fmt (Color Red);
        pp_location_stack fmt !location_stack;
        Format.pp_open_stag fmt (Underline true);
        Format.fprintf fmt "Warning";
        Format.pp_close_stag fmt ();
        Format.fprintf fmt ": %t" (fun fmt -> log (Format.fprintf fmt));
        Format.pp_close_stag fmt ();
      in
      print (fun p -> p "%t" colored_log)
    end
  ;;

  let debug log =
    if V.get_verbosity_level() >= Level.debug
    then begin
      maybe_log_bintrace Level.debug log;
      let colored_log fmt =
        Format.pp_open_stag fmt (Weight Faint);
        Format.pp_open_stag fmt (Color Cyan);
        Format.pp_open_stag fmt (Italic true);
        log (Format.fprintf fmt);
        Format.pp_close_stag fmt ();
        Format.pp_close_stag fmt ();
        Format.pp_close_stag fmt ()
      in
      print (fun p -> p "%t" colored_log)
    end
  ;;

  (** Fail, but reuse the message in the failwith argument.  *)
  let fatal: type a. a log -> 'b =  fun log ->
    generic_error Level.critical "Fatal Error" log;
    raise (Fatal (Format.asprintf "%s:%t" (Category.category) (fun fmt -> log (Format.fprintf fmt))))

  let fatal_handler f x =
    try f x
    with
    | Fatal s ->
      (* The error message is already printed by fatal. *)
      let backtrace = (Printexc.get_backtrace()) in
      print (fun p -> p "%s" backtrace);
      exit 1
    | e ->
      let exc_string = Printexc.to_string e in
      let backtrace = Printexc.get_backtrace() in
      generic_error Level.critical "Uncaught exception" (fun p -> p "%s" exc_string);
      print (fun p -> p "The full backtrace is:\n%s" backtrace);
      exit 2
  ;;


  let not_yet_implemented log =
    fatal (log_concat (log_of_string "Not yet implemented: ") log)
  ;;

  (** - If [loc] is not None, push the loc on the stack of locations.
      - If printed: increase the nesting level.
      -

      - TODO: Add a log level; default would be info (or trace).

  *)

  (* TODO: Merge the two locations *)
  let trace log ?(loc:location option) ?(bintrace: _ Syntax_tree.Location_identifier.t option) ?pp_ret f =
    let open Binarytrace in
    if !log_binary_trace then begin
           (Binarytrace.add_trace_to_file (Category.category)
              (Node {loc=bintrace;
                     category=Category.category;
                     content= string_of_log log}))
    end;

    let pop_loc =
      match loc with
        | None -> fun () -> ()
        | Some loc ->
          location_stack := loc::!location_stack;
          fun () -> location_stack := List.tl !location_stack
    in
    (* Note: we do the try with only with if for performance purposes. *)
    let display_trace = V.get_verbosity_level() >= Level.trace in
    begin try
        let with_color log = fun fmt ->
          Format.pp_open_stag fmt (Color Green);
          log (Format.fprintf fmt);
          Format.pp_close_stag fmt ()
        in
      if display_trace then begin
        print ~last:false (fun p -> p "%t" @@ with_color log);
        Terminal.open_nesting ();
      end;
      (* Binarytrace.open_nesting (); *)
      let res = f() in
      if display_trace then begin
        (match pp_ret with
          | Some pp_ret ->
            print ~last:true (fun p -> p "%t" @@ with_color (fun p -> p "Result: %a" pp_ret res));
          | None ->
            print ~last:true (fun p -> p "%t" @@ with_color (fun p -> p "Result: <not printed>")););
        Terminal.close_nesting ();
      end;
      if !log_binary_trace then begin
        let result = match pp_ret with
          | Some pp_ret ->
            let resultstr = string_of_log (fun p -> p "%a" pp_ret res) in
            Result (Some resultstr)
          | None ->
            Result None
        in
        Binarytrace.add_trace_to_file (Category.category) result;
        (* Binarytrace.close_nesting (); *)
      end;
      pop_loc();
      res
    with e ->     (* close nesting without returning, and raise with the same backtrace. *)
      let bt = Printexc.get_raw_backtrace () in
      if display_trace then begin
        print ~last:(not !print_backtraces) (fun p -> p "No result (exception %s)" (Printexc.to_string e));
        if !print_backtraces then begin
          print ~last:true (fun p -> p "%s" (Printexc.raw_backtrace_to_string bt));
          print_backtraces := false (* avoid reprinting the same backtrace again *)
        end;
        Terminal.close_nesting ();
      end;
      pop_loc();
        if !log_binary_trace then
          Binarytrace.add_trace_to_file (Category.category) (Result None);
        (* Binarytrace.close_nesting ();  *)
      Printexc.raise_with_backtrace e bt
  end

;;




end;;




module Make = MakeGeneric[@inlined hint](struct let get_verbosity_level() = !current_level end)
module MakeDebug = MakeGeneric(struct let get_verbosity_level() = Level.debug end)
module MakeSilent = MakeGeneric(struct let get_verbosity_level() = 1 end)
module Test() = struct
  module Logs=Make(struct let category = "test" end);;

  set_verbosity_level `Debug;;

  Logs.notice (fun m -> m "Test 0 message %s %a ads" "Hu" Format.pp_print_string "Ha");;
  Logs.notice (fun m -> m "Test simple message\n");;
  Logs.warning (fun m -> m "Test 1 warning with newline in middle %s %a@\nads" "Hu" Format.pp_print_string "Ha");;
  Logs.error (fun m -> m "Test 2 error with newline at the end %s %a@\n" "Hu" Format.pp_print_string "Ha");;
  (* Logs.output := false;; *)
  Logs.error (fun m -> m "Test 3 warning with newline in middle %s %a@\naaend" "Hu" Format.pp_print_string "Ha");;
  Logs.debug (fun m -> m "Test 4 debug output  %s %a@\naaend" "Hu" Format.pp_print_string "Ha");;
  try Logs.not_yet_implemented (fun p -> p "FEATURE") with _ -> ();;
  try Logs.fatal (fun m -> m "Test 5 fatal %s %a@\nad" "Hu" Format.pp_print_string "Ha") with _ -> ();;


  let my_add a b =
    Logs.trace (fun p -> p "Evaluating %d + %d" a b) ~pp_ret:Format.pp_print_int (fun () -> a + b)
  ;;

  my_add (my_add 2 3) (my_add 4 5);; (* Flat. *)

  type expr =
    | Add of expr * expr
    | Cst of int
  let rec pp_expr fmt = function
    | Cst i -> Format.fprintf fmt "%d" i
    | Add(e1,e2) -> Format.fprintf fmt "(%a + %a)" pp_expr e1 pp_expr e2

  let rec eval' = function
    | Cst i -> i
    | Add(e1,e2) ->
      Logs.debug (fun p -> p "Before\n e1");
      let r1 = eval e1 in
      Logs.debug (fun p -> p "Middle");
      let r2 = eval e2 in
      Logs.debug (fun p -> p "End");
      r1 + r2
  and eval e =
    Logs.trace (fun p -> p "Evaluating @\nexpression %a" pp_expr e) ~pp_ret:Format.pp_print_int (fun () -> eval' e);;

  eval (Add (Add(Cst 2,Cst 3),Add(Cst 4,Cst 5)));;

  exception Custom;;
  let rec eval' = function
    | Cst i when i == 5 -> raise Custom
    | Cst i -> i
    | Add(e1,e2) ->
      Logs.debug (fun p -> p "Before\n e1");
      let r1 = eval e1 in
      Logs.debug (fun p -> p "Middle");
      let r2 = eval e2 in
      Logs.debug (fun p -> p "End");
      r1 + r2
  and eval e =
    Logs.trace (fun p -> p "Evaluating @\nexpression %a" pp_expr e) ~pp_ret:Format.pp_print_int (fun () -> eval' e);;

  try eval (Add (Add(Cst 2,Cst 3),Add(Cst 4,Cst 5)))
  with Custom -> 3;;



end;;

(* module M = Test();; *)