Source file mt_analysis_fixpoint.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
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
456
457
458
459
460
461
462
open Mt_types
open Mt_shared_vars_types
open Mt_mutexes_types
open Mt_thread
let mark_new_messages_received analysis =
let th = analysis.curr_thread in
let is_send = function SendMsg _ -> true | _ -> false in
let send_before = Trace.find_events is_send th.th_amap
and send_after = Trace.find_events is_send (curr_events analysis) in
let diff = EventsSet.diff send_after send_before in
if not (EventsSet.is_empty diff) then
let queues = EventsSet.fold
(fun evt queues -> match evt with
| SendMsg (q, _) -> Mqueue.Set.add q queues
| _ -> queues) diff Mqueue.Set.empty
in
Mt_options.debug "@[New message(s) sent@ on@ queue(s) %a@]"
(Pretty_utils.pp_iter Mqueue.Set.iter Mqueue.pretty) queues;
iter_threads analysis
(fun th ->
let should_recompute _stack = function
| ReceiveMsg (q, _, _) -> Mqueue.Set.mem q queues
| _ -> false
in
if Trace.exists th.th_amap should_recompute
then (Mt_options.debug "Marking %a as having received new message(s)"
ThreadState.pretty th;
ThreadState.recompute_because th NewMsgReceived)
);
;;
let record_end_of_thread_analysis analysis =
let th = analysis.curr_thread in
Mt_options.feedback ~level:2
"* Starting to save the state of the value analysis";
let results = Eva_results.get_results () in
th.th_value_results <- Some results;
if Mt_options.ToDisk.get () then
let th = ThreadState.label th |> Mt_lib.sanitize_filename in
let name = Format.sprintf "%s%s_iteration_%d.sav"
(Mt_options.ToDiskPrefix.get ())
th analysis.iteration in
Project.save (Filepath.of_string name)
else begin
let p = lazy(
let pname = Format.asprintf "%a, iteration %d"
ThreadState.pretty th analysis.iteration
in
Project.create_by_copy ~last:false pname)
in
match Mt_options.KeepProjects.get () with
| "all" ->
th.th_projects <- Lazy.force p :: th.th_projects
| "last" ->
List.iter (fun project -> Project.remove ~project ()) th.th_projects;
th.th_projects <- [Lazy.force p]
| "none" -> ()
| _ -> assert false
end;
Mt_options.feedback ~level:2 "* state saved";
mark_new_messages_received analysis;
Mt_options.feedback ~level:2 "* Computing shared variables";
let state_accesser = Mt_memory.Types.Global in
let read_written =
Mt_shared_vars.read_written_by_thread
(Mt_shared_vars.stmt_is_multithreaded analysis state_accesser)
th.th_eva_thread
in
th.th_read_written <- read_written;
Mt_options.result ~level:3 "@[<v 0>Globals accessed by thread:@ %a@]"
AccessesByZone.pretty_map read_written;
Mt_options.feedback ~level:2 "* shared variables computed";
Mt_interferences.add_last_analysis analysis;
th.th_amap <- curr_events analysis;
Mt_options.feedback ~level:2 "* Computing cfg";
th.th_cfg <- Mt_cfg.make_cfg th;
th.th_read_written_cfg <- Mt_cfg.cfg_accesses th.th_eva_thread th.th_cfg;
Mt_options.feedback ~level:2 "* Cfg computed";
;;
let compute_thread analysis th =
let time = Sys.time () in
Project.clear
~selection:(State_selection.with_dependencies Messages.self) ();
Messages.reset_once_flag ();
Mt_options.feedback ~level:2 "* Computing value analysis for thread %a"
Thread.pretty th.th_eva_thread;
Mt_options.debug "@[<hov>Arguments@ %a@]"
(Pretty_utils.pp_list Cvalue.V.pretty) th.th_params;
Mt_options.debug ~level:2 "Initial state %a"
Cvalue.Model.pretty th.th_init_state;
analysis.curr_thread <- th;
analysis.curr_events_stack <- [];
Datatype.Int.Hashtbl.clear analysis.memexec_cache;
Mt_lib.clear_value_results ();
Globals.set_entry_point (Kernel_function.get_name th.th_fun) false;
Eva_results.set_initial_state th.th_init_state;
Eva_results.set_main_args th.th_params;
Thread.set_current th.th_eva_thread;
Analysis.compute ();
if Mt_options.ShowTime.get () then
Mt_options.feedback ~level:2
"* Value analysis computed for thread %a, %f sec"
ThreadState.pretty th (Sys.time () -. time);
;;
let recompute_shared_vars_changed analysis before =
iter_threads analysis
(fun th ->
try AccessesByZone.fold
(fun z _ () ->
if not (Locations.Zone.is_included z before) then raise Exit)
th.th_read_written ()
with Exit -> ThreadState.recompute_because th PotentialSharedVarsChanged
)
;;
(** Recompute all the threads that are not [th], and that read a value
that has changed between [before] and [now] *)
let recompute_shared_vars_values_changed analysis th before now =
let changed_zone b offsm z =
let default () =
let zb = Locations.Zone.inject b Int_Intervals.top in
Locations.Zone.join z zb
in
try
match Cvalue.Model.find_base b before with
| `Top | `Bottom -> assert false
| `Value offsm' ->
if Cvalue.V_Offsetmap.equal offsm offsm' then z
else default ()
with Not_found -> default ()
in
match now with
| Cvalue.Model.Top | Cvalue.Model.Bottom -> assert false
| Cvalue.Model.Map now ->
let z_changed =
Cvalue.Model.fold changed_zone now Locations.Zone.bottom
in
iter_threads analysis
(fun th' ->
if not (ThreadState.equal th' th) then
try
AccessesByZone.fold
(fun z accesses () ->
if Locations.Zone.intersects z_changed z &&
(SetStmtIdAccess.exists
(fun (op, _, _) -> RW.is_read op)
accesses)
then begin
ThreadState.recompute_because th' SharedVarsValuesChanged;
raise Exit
end)
th'.th_read_written ()
with Exit -> ()
)
;;
let compute_shared_vars analysis =
let _imprecise =
Mt_options.feedback "***** Computing shared variables";
let (ww_accesses, rw_accesses), _ =
Mt_shared_vars.Global.concurrent_accesses_all_threads
(threads analysis) in
let accesses = ww_accesses @ rw_accesses in
Mt_options.debug ~level:2 "Global concurrent var accesses:@.%a"
(Mt_shared_vars.Global.pretty_concurrent_accesses ()) accesses;
let all_zones = Mt_shared_vars.Global.all_zones_accessed accesses in
Mt_options.result ~level:3 "@[<hov 2>Imprecise locations to watch: %a@]"
Locations.Zone.pretty all_zones;
if not (Locations.Zone.equal all_zones analysis.concurrent_accesses)
then (
let before = analysis.concurrent_accesses in
Mt_options.feedback ~level:2 "@[<v>Concurrent imprecise accesses have \
changed: before@ @[<hov 2> %a@]@ vs.@ @[<hov 2> %a@]"
Locations.Zone.pretty before Locations.Zone.pretty all_zones;
let after = Locations.Zone.join before all_zones in
analysis.concurrent_accesses <- after;
recompute_shared_vars_changed analysis before;
)
in
let precise =
let (ww_accesses, rw_accesses), zmap =
Mt_shared_vars.Precise.concurrent_accesses_all_threads
(threads analysis) in
if Mt_options.DumpSharedVarsValues.get () > 0 then
Mt_shared_vars.Precise.display_shared_vars_value zmap;
let written = Mt_shared_vars.Precise.enumerate_written_vars_value zmap in
let all_accesses = ww_accesses @ rw_accesses in
let fmt = Format.fprintf fmt "Possible read/write data races:" in
Mt_options.printf ~level:1 ~header " @[<v 0>%a@]"
Mt_mutexes.pretty_with_mutexes rw_accesses;
if Mt_options.WriteWriteRaces.get () then begin
let fmt = Format.fprintf fmt "Possible write/write data races:" in
Mt_options.printf ~level:1 ~header " @[<v 0>%a@]"
Mt_mutexes.pretty_with_mutexes ww_accesses;
end;
let all_zones = Mt_shared_vars.Precise.all_zones_accessed (ww_accesses @ rw_accesses) in
Mt_options.result ~level:2 "@[<hov 2>Shared memory:@ %a@]"
Locations.Zone.pretty all_zones;
if not (Locations.Zone.equal all_zones analysis.precise_concurrent_accesses)
then (
let before = analysis.precise_concurrent_accesses in
Mt_options.feedback ~level:2
"@[<v>Concurrent precise var accesses have changed: before@ \
@[<hov 2> %a@]@ \
vs.@ \
@[<hov 2> %a@]@]"
Locations.Zone.pretty before Locations.Zone.pretty all_zones;
analysis.precise_concurrent_accesses <- all_zones;
);
all_accesses, written
in
precise
;;
let store_written_value analysis lw =
let aux th =
let l = List.filter (fun (id, _, _) -> Thread.equal id th.th_eva_thread) lw in
let old_written = th.th_values_written in
let written = Mt_shared_vars.Precise.join_shared_values l in
let priority, hint =
Widen_type.hints_from_keys Cil.dummyStmt (Widen_type.default ())
in
let written = Cvalue.Model.widen ~priority ~hint old_written written in
let changed = not (Cvalue.Model.equal written old_written) in
if changed then
recompute_shared_vars_values_changed analysis th old_written written;
if Mt_options.DumpSharedVarsValues.get () > 0 &&
not (Cvalue.Model.equal Cvalue.Model.empty_map written)
then
Mt_options.result "@[Write summary for %a%t:@ %a@]"
ThreadState.pretty th
(fun fmt -> if changed then Format.fprintf fmt " (updated)")
Cvalue.Model.pretty written;
th.th_values_written <- written
in
iter_threads analysis aux
let one_iteration analysis =
iter_threads analysis
(fun th ->
if not (SetRecomputeReason.is_empty th.th_to_recompute) then (
if Mt_thread.should_compute_thread th then
if Cvalue.Model.is_reachable th.th_init_state then (
Mt_options.feedback
"@[<hov 2>*** Computing thread %a,@ iteration %d@ (%a)@]"
ThreadState.pretty th analysis.iteration
(Pretty_utils.pp_iter ~sep:",@ "
SetRecomputeReason.iter RecomputeReason.pretty)
th.th_to_recompute;
compute_thread analysis th;
record_end_of_thread_analysis analysis;
Mt_options.feedback "*** Thread %a computed" ThreadState.pretty th;
) else (
Mt_options.feedback "@[<hov 2>*** Thread %a has been@ created but@ \
not started. Skipping.@]" ThreadState.pretty th
)
else (
Mt_options.feedback "*** Skipping thread %a as requested"
ThreadState.pretty th;
);
th.th_to_recompute <- SetRecomputeReason.empty;
) else
Mt_options.debug "No need to recompute thread %a" ThreadState.pretty th
);
Mt_options.feedback "***** Threads computed for iteration %d."
analysis.iteration;
Mt_options.feedback ~level:2 "* Computing live threads and locked mutexes";
iter_threads analysis (Mt_cfg.update_cfg_contexts analysis);
Mt_options.feedback ~level:2 "* threads and mutexes computed";
let precise_accesses, written = compute_shared_vars analysis in
analysis.concurrent_accesses_by_nodes <- precise_accesses;
store_written_value analysis written;
let mutexes = Mt_mutexes.mutexes_protecting_zones' precise_accesses in
Mt_options.result "@[<v 0>Mutexes for concurrent accesses:@ %a@]"
MutexesByZone.pretty mutexes;
if Mt_options.CheckProtections.get () then begin
let protections = Mt_mutexes.check_protection analysis precise_accesses in
Mt_options.result "Detailed shared zones protections@.%a"
Mt_mutexes.pretty_protections protections;
let ill_protected = Mt_mutexes.ill_protected precise_accesses protections in
let need_sync = Mt_mutexes.need_sync ill_protected in
if need_sync <> [] then begin
let pp fmt (stmt, z) =
Format.fprintf fmt "@[%a (for %a)@]"
Cil_datatype.Location.pretty (Cil_datatype.Stmt.loc stmt)
Locations.Zone.pretty z
in
Mt_options.result "Statements needing manual synchronisation@.%a"
(Pretty_utils.pp_list ~pre:"@[<v>" ~sep:"@ " ~suf:"@]" pp) need_sync
end;
end;
Mt_options.feedback "***** Shared variables computed";
fold_threads analysis false
(fun th v -> v || not (SetRecomputeReason.is_empty th.th_to_recompute))
;;
let mark_shared_nodes_kind analysis =
let precise_accesses = analysis.concurrent_accesses_by_nodes in
let shared_vars = Mt_shared_vars.Precise.all_zones_accessed precise_accesses in
iter_threads analysis
(fun th -> Mt_shared_vars.Precise.remove_non_concur_zones_from_cfg
shared_vars th.th_cfg
);
Mt_shared_vars.Precise.mark_concur_access_in_cfg precise_accesses;
if (not (Mt_options.KeepWhiteNodes.get ()) ||
not (Mt_options.KeepGreenNodes.get ()))
&& not (Mt_options.FullCfg.get ())
then
iter_threads analysis
(fun th ->
let keep =
match Mt_options.KeepWhiteNodes.get (),
Mt_options.KeepGreenNodes.get () with
| false, false -> Mt_cfg_types.ConcurrentAccess
| false, true -> Mt_cfg_types.SharedVarNonConcurrentAccess
| true, true -> Mt_cfg_types.NotReallySharedVar
| true, false ->
Mt_options.warning ~once:true
"Incoherent@ combination@ of@ options@ %s@ \
and@ %s.@ Only@ non-shared@ variables@ will@ be@ removed."
Mt_options.KeepWhiteNodes.option_name
Mt_options.KeepGreenNodes.option_name;
Mt_cfg_types.SharedVarNonConcurrentAccess
in
let cfg = Mt_cfg.remove_superfluous_nodes ~keep th.th_cfg in
th.th_cfg <- cfg;
);
;;
let reach_fixpoint analysis =
Mt_options.feedback "******* Starting to iterate";
let rec aux i =
Mt_options.feedback "***** Iteration %d" i;
analysis.iteration <- i;
let continue = one_iteration analysis in
if continue && i < Mt_options.StopAfter.get () then aux (i+1)
else
if continue then
Mt_options.feedback
"@[<v 0>\
@[<hov 2>******* Analysis stopped after@ \
%d iterations.@ Remaining@ to@ do:@]@ \
%a@]" i
(fun fmt () -> iter_threads analysis
(fun th -> if not (SetRecomputeReason.is_empty th.th_to_recompute) then
Format.fprintf fmt "@[<hov 2>Thread %a:@ %a@]@ "
ThreadState.pretty_detailed th
(Pretty_utils.pp_iter ~sep:",@ " ~pre:"" ~suf:""
SetRecomputeReason.iter RecomputeReason.pretty)
th.th_to_recompute
)
) ()
else
Mt_options.feedback "******* Analysis performed, %d iterations" i
in
aux 1