metrics_acsl.ml 12.3 KB
Newer Older
1
2
3
4
(**************************************************************************)
(*                                                                        *)
(*  This file is part of Frama-C.                                         *)
(*                                                                        *)
Andre Maroneze's avatar
Andre Maroneze committed
5
(*  Copyright (C) 2007-2020                                               *)
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
(*    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 licenses/LGPLv2.1).            *)
(*                                                                        *)
(**************************************************************************)

open Cil_types

type acsl_stats =
  { mutable f_requires: int; (** number of requires in function contracts *)
    mutable s_requires: int; (** number of requires in statement contracts *)
    mutable f_ensures: int;
    mutable s_ensures: int;
    mutable f_behaviors: int;
    mutable s_behaviors: int;
    mutable f_assumes: int;
    mutable s_assumes: int;
    mutable f_assigns: int;
    mutable s_assigns: int; (** does not include loop assigns. *)
    mutable f_froms: int;
    mutable s_froms: int; (** does not include loop dependencies. *)
    mutable invariants: int;
    mutable loop_assigns: int;
    mutable loop_froms: int;
    mutable variants: int;
    mutable asserts: int;
  }

let empty_acsl_stat () =
  { f_requires = 0;
    s_requires = 0;
    f_ensures = 0;
    s_ensures = 0;
    f_behaviors = 0;
    s_behaviors = 0;
    f_assumes = 0;
    s_assumes = 0;
    f_assigns = 0;
    s_assigns = 0;
    f_froms = 0;
    s_froms = 0;
    invariants = 0;
    loop_assigns = 0;
    loop_froms = 0;
    variants = 0;
    asserts = 0;
  }

let incr_f_requires stat = stat.f_requires <- stat.f_requires + 1
let incr_s_requires stat = stat.s_requires <- stat.s_requires + 1
let incr_f_ensures stat = stat.f_ensures <- stat.f_ensures + 1
let incr_s_ensures stat = stat.s_ensures <- stat.s_ensures + 1
let incr_f_behaviors stat = stat.f_behaviors <- stat.f_behaviors + 1
let incr_s_behaviors stat = stat.s_behaviors <- stat.s_behaviors + 1
let incr_f_assumes stat = stat.f_assumes <- stat.f_assumes + 1
let incr_s_assumes stat = stat.s_assumes <- stat.s_assumes + 1
let incr_f_assigns stat = stat.f_assigns <- stat.f_assigns + 1
let incr_s_assigns stat = stat.s_assigns <- stat.s_assigns + 1
let incr_f_froms stat = stat.f_froms <- stat.f_froms + 1
let incr_s_froms stat = stat.s_froms <- stat.s_froms + 1
let incr_invariants stat = stat.invariants <- stat.invariants + 1
let incr_loop_assigns stat = stat.loop_assigns <- stat.loop_assigns + 1
let incr_loop_froms stat = stat.loop_froms <- stat.loop_froms + 1
let incr_variants stat = stat.variants <- stat.variants + 1
let incr_asserts stat = stat.asserts <- stat.asserts + 1

let pretty_acsl_stats fmt stat =
  Format.fprintf fmt
    "@[<v 0>requires: %d total, %d in function contracts,\
        %d in statement contracts@;\
     ensures: %d total, %d in function contracts, %d in statement contracts@;\
     behaviors: %d total, %d in function contracts, %d in statement contracts@;\
     assumes: %d total, %d in function contracts, %d in statement contracts@;\
     assigns: %d total, %d in function contracts, %d in statement contracts@;\
     froms: %d total, %d in function contracts, %d in statement contracts@;\
     invariants: %d@;loop assigns: %d@;loop froms: %d@;variants: %d@;\
     asserts: %d@;@]"
    (stat.f_requires + stat.s_requires)
    stat.f_requires stat.s_requires 
    (stat.f_ensures + stat.s_ensures)
    stat.f_ensures stat.s_ensures
    (stat.f_behaviors + stat.s_behaviors)
    stat.f_behaviors stat.s_behaviors
    (stat.f_assumes + stat.s_assumes)
    stat.f_assumes stat.s_assumes
    (stat.f_assigns + stat.s_assigns)
    stat.f_assigns stat.s_assigns
    (stat.f_froms + stat.s_froms)
    stat.f_froms stat.s_froms
    stat.invariants stat.loop_assigns stat.loop_froms stat.variants
    stat.asserts

let pretty_acsl_stats_html fmt stat =
  Format.fprintf fmt
    "@[<v 0>@{<h3>Contract elements@}@;@{<table>@;\
     @{<tr>@{<td>@}\
     @{<td>total@}@{<td>function contract@}@{<td>statement contract@}@}@;\
     @{<tr>@;@{<td class=\"entry\">requires@}@;\
     @{<td class=\"stat\">%d@}@;@{<td class=\"stat\">%d@}@;\
     @{<td class=\"stat\">%d@}@}@;\
     @{<tr>@;@{<td class=\"entry\">requires@}@;\
     @{<td class=\"stat\">%d@}@;@{<td class=\"stat\">%d@}@;\
     @{<td class=\"stat\">%d@}@}@;\
     @{<tr>@;@{<td class=\"entry\">requires@}@;\
     @{<td class=\"stat\">%d@}@;@{<td class=\"stat\">%d@}@;\
     @{<td class=\"stat\">%d@}@}@;\
     @{<tr>@;@{<td class=\"entry\">requires@}@;\
     @{<td class=\"stat\">%d@}@;@{<td class=\"stat\">%d@}@;\
     @{<td class=\"stat\">%d@}@}@;\
     @{<tr>@;@{<td class=\"entry\">requires@}@;\
     @{<td class=\"stat\">%d@}@;@{<td class=\"stat\">%d@}@;\
     @{<td class=\"stat\">%d@}@}@;\
     @{<tr>@;@{<td class=\"entry\">requires@}@;\
     @{<td class=\"stat\">%d@}@;@{<td class=\"stat\">%d@}@;\
     @{<td class=\"stat\">%d@}@}@}@;\
     @{<h3>Simple code annotations@}@{<table>@;\
     @{<tr>@{<td class=\"entry\">invariants@}@{<td class=\"stat\">%d@}@}@;\
     @{<tr>@{<td class=\"entry\">loop assigns@}@{<td class=\"stat\">%d@}@}@;\
     @{<tr>@{<td class=\"entry\">loop froms@}@{<td class=\"stat\">%d@}@}@;\
     @{<tr>@{<td class=\"entry\">variants@}@{<td class=\"stat\">%d@}@}@;\
     @{<tr>@{<td class=\"entry\">asserts@}@{<td class=\"stat\">%d@}@}@;\
     @}@]"
    (stat.f_requires + stat.s_requires)
    stat.f_requires stat.s_requires 
    (stat.f_ensures + stat.s_ensures)
    stat.f_ensures stat.s_ensures
    (stat.f_behaviors + stat.s_behaviors)
    stat.f_behaviors stat.s_behaviors
    (stat.f_assumes + stat.s_assumes)
    stat.f_assumes stat.s_assumes
    (stat.f_assigns + stat.s_assigns)
    stat.f_assigns stat.s_assigns
    (stat.f_froms + stat.s_froms)
    stat.f_froms stat.s_froms
    stat.invariants stat.loop_assigns stat.loop_froms stat.variants
    stat.asserts

module Acsl_stats =
  Datatype.Make(
    struct
      type t = acsl_stats
      let reprs = [empty_acsl_stat ()]
      let name = "Metrics_acsl.acsl_stats"
      include Datatype.Serializable_undefined
      let pretty = pretty_acsl_stats
    end)

module Global_acsl_stats =
  State_builder.Ref(Acsl_stats)
    (struct
        let name = "Metrics_acsl.Global_acsl_stats"
        let dependencies =
          [ Ast.self; Annotations.code_annot_state; Annotations.funspec_state;
            Annotations.global_state
          ]
        let default = empty_acsl_stat
     end)

module Functions_acsl_stats =
  State_builder.Hashtbl
    (Kernel_function.Hashtbl)
    (Acsl_stats)
    (struct
      let name = "Metrics_acsl.Functions_acsl_stats"
      let dependencies =
        [Ast.self; Annotations.code_annot_state; Annotations.funspec_state]
      let size = 17
     end)

let get_kf_stats kf =
  try Functions_acsl_stats.find kf with Not_found -> empty_acsl_stat()

module Computed =
  State_builder.False_ref
    (struct
      let name = "Metrics_acsl.Computed"
      let dependencies = [ Global_acsl_stats.self; Functions_acsl_stats.self]
     end)

let treat_behavior local_stats ki b =
  let incr_behaviors =
    if ki = Kglobal then incr_f_behaviors else incr_s_behaviors
  in
  let incr_requires =
    if ki = Kglobal then incr_f_requires else incr_s_requires
  in
  let incr_ensures =
    if ki = Kglobal then incr_f_ensures else incr_s_ensures
  in
  let incr_assumes =
    if ki = Kglobal then incr_f_assumes else incr_s_assumes
  in
  let incr_assigns =
    if ki = Kglobal then incr_f_assigns else incr_s_assigns
  in
  let incr_froms =
    if ki = Kglobal then incr_f_froms else incr_s_froms
  in
  let incr_all f _ = f local_stats; f (Global_acsl_stats.get()) in
  incr_all incr_behaviors ();
  List.iter (incr_all incr_requires) b.b_requires;
  List.iter (incr_all incr_ensures) b.b_post_cond;
  List.iter (incr_all incr_assumes) b.b_assumes;
  (match b.b_assigns with
    | WritesAny -> ()
    | Writes l ->
      incr_all incr_assigns ();
      List.iter
        (function
          | (_,FromAny) -> ()
          | (_,From _) -> incr_all incr_froms ())
        l)
  (*TODO: allocation *)

let add_function_contract_stats kf =
  let local_stats = get_kf_stats kf in
  let treat_behavior _ b = treat_behavior local_stats Kglobal b in
  Annotations.iter_behaviors treat_behavior kf

let add_code_annot_stats stmt _ ca =
  let kf = Kernel_function.find_englobing_kf stmt in
  let local_stats = get_kf_stats kf in
  let incr_all f = f local_stats; f (Global_acsl_stats.get()) in
  match ca.annot_content with
    | AAssert _ -> incr_all incr_asserts
    | AStmtSpec (_,spec) ->
      List.iter (treat_behavior local_stats (Kstmt stmt)) spec.spec_behavior
    | AInvariant _ -> incr_all incr_invariants
    | AVariant _ -> incr_all incr_variants
    | AAssigns (_,WritesAny) -> ()
    | AAssigns (_,Writes l) ->
      incr_all incr_loop_assigns;
      List.iter
        (function (_,FromAny) -> () | (_,From _) -> incr_all incr_loop_froms) l
    | AAllocation _ -> () (* TODO *)
    | APragma _ | AExtended _ -> ()

let compute () =
  if not (Computed.get()) then begin
    Ast.compute();
    Annotations.iter_all_code_annot add_code_annot_stats;
    Globals.Functions.iter add_function_contract_stats;
    Computed.set true;
  end

let get_global_stats () = compute (); Global_acsl_stats.get ()

let dump_html_global fmt = pretty_acsl_stats_html fmt (get_global_stats())

let dump_html_by_function fmt =
  compute ();
  Functions_acsl_stats.iter
    (fun kf stats -> 
      Format.fprintf fmt "@{<h2>Function %a@}@;%a"
        Kernel_function.pretty kf pretty_acsl_stats_html stats)

let dump_acsl_stats fmt =
  Metrics_base.mk_hdr 1 fmt "ACSL Statistics";
  Format.pp_print_newline fmt ();
  if Metrics_parameters.ByFunction.get () then begin
    compute ();
    Functions_acsl_stats.iter
      (fun kf stats ->
        let kf_name = Format.asprintf "%a" Kernel_function.pretty kf in
        Format.fprintf fmt "@[<v 2>%a@;%a@]@;"
          (Metrics_base.mk_hdr 2) kf_name pretty_acsl_stats stats)
  end else pretty_acsl_stats fmt (get_global_stats())
    

let dump_acsl_stats_html fmt =
287
  Format.pp_set_formatter_stag_functions fmt Metrics_base.html_stag_functions;
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
  Format.fprintf fmt
    "@[<v 0> <!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\"\
          \"http://www.w3.org/TR/html4/strict.dtd\">@ \
      @{<html>@ \
      @{<head>@ \
       @{<title>%s@}@ \
       <meta content=\"text/html; charset=iso-8859-1\" \
        http-equiv=\"Content-Type\"/>@ \
        @{<style type=\"text/css\">%s@}@ \
      @}@ \
        @{<body>\
         @[<v 2>@ \
         @{<h1>%s@}@;\
         %t@]@}@}@]@?"
    "ACSL Metrics"
    Css_html.css
    (if Metrics_parameters.ByFunction.get ()
     then "Detailed ACSL statistics" else "Global ACSL statistics")
    (if Metrics_parameters.ByFunction.get ()
     then dump_html_global else dump_html_by_function)
    

let dump () =
  let out = Metrics_parameters.OutputFile.get () in
  if out <> "" then begin
    try
      let chan = open_out out in
      let fmt = Format.formatter_of_out_channel chan in
      (match Metrics_base.get_file_type out with
            | Metrics_base.Html -> dump_acsl_stats_html fmt
318
319
320
321
            | Metrics_base.Text -> dump_acsl_stats fmt
            | Metrics_base.Json ->
              raise (Log.FeatureRequest ("Metrics", "JSON format for ACSL metrics"))
      );
322
323
324
325
326
      close_out chan
    with Sys_error s ->
      Metrics_parameters.abort "Cannot open file %s (%s)" out s
  end else Metrics_parameters.result "%t" dump_acsl_stats