-
Notifications
You must be signed in to change notification settings - Fork 88
Expand file tree
/
Copy pathcreationLockset.ml
More file actions
137 lines (118 loc) · 5.49 KB
/
creationLockset.ml
File metadata and controls
137 lines (118 loc) · 5.49 KB
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
(** creation lockset analysis [creationLockset]
constructs edges on the graph over all threads, where the edges are labelled with must-locksets:
(t_1) ---L--> (t_0) is represented by global t_1 t_0 = L and means that t_1 is protected by all members of L from t_0
@see Bachelor's Thesis "Leveraging th Potential of Inter-Threaded Locksets in Abstract Interpretation". Available upon request.
*)
open Analyses
module TID = ThreadIdDomain.Thread
module TIDs = ConcDomain.ThreadSet
module Lock = LockDomain.MustLock
module Lockset = LockDomain.MustLockset
module Spec = struct
include IdentityUnitContextsSpec
module D = Lattice.Unit
module V = TIDV
module G = Queries.CL
let name () = "creationLockset"
let startstate _ = D.bot ()
let exitstate _ = D.bot ()
(** register a global contribution: global.[child_tid] \supseteq [to_contribute]
@param man man at program point
@param to_contribute new edges from [child_tid] to ego thread to register
@param child_tid
*)
let contribute_locks man to_contribute child_tid = man.sideg child_tid to_contribute
let threadspawn man ~multiple lval f args fman =
let tid_lifted = man.ask Queries.CurrentThreadId in
let child_tid_lifted = fman.ask Queries.CurrentThreadId in
match tid_lifted, child_tid_lifted with
| `Lifted tid, `Lifted child_tid when TID.must_be_ancestor tid child_tid ->
let must_ancestor_descendants =
ThreadDescendants.must_ancestor_descendants_closure fman child_tid
in
let lockset = man.ask Queries.MustLockset in
let to_contribute = G.singleton tid lockset in
TIDs.iter (contribute_locks man to_contribute) must_ancestor_descendants
| _ -> ()
(** handle unlock of mutex [lock] *)
let unlock man tid possibly_running_tids lock =
let shrink_locksets des_tid =
let old_creation_lockset = G.find tid (man.global des_tid) in
(* Bot - {something} = Bot. This is exactly what we want in this case! *)
let updated_creation_lockset = Lockset.remove lock old_creation_lockset in
let to_contribute = G.singleton tid updated_creation_lockset in
man.sideg des_tid to_contribute
in
TIDs.iter shrink_locksets possibly_running_tids
(** handle unlock of an unknown mutex. Assumes that any mutex could have been unlocked *)
let unknown_unlock man tid possibly_running_tids =
let evaporate_locksets des_tid =
let to_contribute = G.singleton tid (Lockset.empty ()) in
man.sideg des_tid to_contribute
in
TIDs.iter evaporate_locksets possibly_running_tids
let event man e _ =
match e with
| Events.Unlock addr ->
let tid_lifted = man.ask Queries.CurrentThreadId in
(match tid_lifted with
| `Lifted tid ->
let possibly_running_tids =
ThreadDescendants.get_must_ancestor_running_descendants man tid
in
let lock_opt = LockDomain.MustLock.of_addr addr in
(match lock_opt with
| Some lock -> unlock man tid possibly_running_tids lock
| None -> unknown_unlock man tid possibly_running_tids)
| _ -> ())
| _ -> ()
module A = struct
(** ego tid * must-lockset * creation-lockset *)
include Printable.Prod3 (TID) (Lockset) (G)
let name () = "creationLockset"
(** checks if [cl1] has a mapping ([tp1] |-> [ls1])
such that [ls1] and [ls2] are not disjoint and [tp1] != [t2]
@param cl1 creation-lockset of thread [t1] at first program point
@param t2 thread id at second program point
@param ls2 lockset at second program point
@returns whether [t1] must be running mutually exclusive with second program point
*)
let one_protected_inter_threaded_other_intra_threaded cl1 t2 ls2 =
G.exists (fun tp1 ls1 -> not (Lockset.disjoint ls1 ls2 || TID.equal tp1 t2)) cl1
(** checks if [cl1] has a member ([tp1] |-> [ls1]) and [cl2] has a member ([tp2] |-> [ls2])
such that [ls1] and [ls2] are not disjoint and [tp1] != [tp2]
@param cl1 creation-lockset of first thread [t1]
@param cl2 creation-lockset of second thread [t2]
@returns whether [t1] and [t2] must be running mutually exclusive
*)
let both_protected_inter_threaded cl1 cl2 =
G.exists (one_protected_inter_threaded_other_intra_threaded cl1) cl2
let may_race (t1, ls1, cl1) (t2, ls2, cl2) =
not
(both_protected_inter_threaded cl1 cl2
|| one_protected_inter_threaded_other_intra_threaded cl1 t2 ls2
|| one_protected_inter_threaded_other_intra_threaded cl2 t1 ls1)
(* TID and Lockset components are directly queried from other analyses and already are printed by them *)
let pretty () (_, _, cl) = G.pretty () cl
let show (_, _, cl) = G.show cl
let to_yojson (_, _, cl) = G.to_yojson cl
let printXml f (_, _, cl) = G.printXml f cl
let should_print (_t, _ls, cl) = G.exists (fun _ ls -> not @@ Lockset.is_empty ls) cl
end
let access man _ =
let tid_lifted = man.ask Queries.CurrentThreadId in
match tid_lifted with
| `Lifted tid ->
let lockset = man.ask Queries.MustLockset in
let creation_lockset = man.global tid in
tid, lockset, creation_lockset
| _ -> ThreadIdDomain.UnknownThread, Lockset.empty (), G.empty ()
let query man (type a) (x : a Queries.t) : a Queries.result =
match x with
| Queries.CreationLockset t -> (man.global t : G.t)
| _ -> Queries.Result.top x
end
let _ =
MCP.register_analysis
~dep:[ "threadid"; "mutex"; "threadJoins"; "threadDescendants" ]
(module Spec : MCPSpec)