-
Notifications
You must be signed in to change notification settings - Fork 87
HB-relationship involving thread creations while mutexes are held #1913
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
dabund24
wants to merge
28
commits into
goblint:master
Choose a base branch
from
dabund24:descendant-locksets
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
28 commits
Select commit
Hold shift + click to select a range
4f611d5
mustlock history analysis
dabund24 d4189b4
initial version of descendant lockset analysis
dabund24 4d5fee2
first descendant lockset racefree tests
dabund24 2cc4be9
first descendant lockset racing tests
dabund24 470279f
descendant lockset racing tests with multiple thread creations
dabund24 1f883f2
creation lockset query
dabund24 bb16da6
global descendant locksets
dabund24 8f46eb3
regression tests for global descendant locksets
dabund24 61cdb71
descendant lockset tests involving multiple mutexes
dabund24 67a62bd
Merge branch 'master' into descendant-locksets
dabund24 c8cdf89
fix incorrect indentatoin in regression test
dabund24 6c68a6c
add missing return statement to regression test
dabund24 6020267
add missing mutex initializations
dabund24 5bf536d
remove redundant whitespace in creation lockset analysis
dabund24 bd388a2
remove unused tid function parameter in `unlock` and `unknown_unlock`…
dabund24 66ad3f1
modify global domain to use `D` instances as values
dabund24 d4aec37
Merge branch 'master' into descendant-locksets
dabund24 743092a
recursive mutex regression tests for descendant locksets
dabund24 b161fed
Merge branch 'master' into descendant-locksets
dabund24 3461214
use `MapTop` instead of `MapBot` for inner map of descendant lockset …
dabund24 717b52f
some documentation in `descendantLockset` analysis
dabund24 ee18c9a
improve printing of `descendantLockset` analysis
dabund24 9f5b264
documentation for `mustlockHistory` analysis
dabund24 83d6f5a
remove unused dependencies from mustlock history analysis
dabund24 6785efa
Merge branch 'master' into descendant-locksets
dabund24 a71d29e
refactor functions related to thread descendants
dabund24 be913ba
locally abstract type for `happens_before` function in `descendantLoc…
dabund24 624254b
also print mustlock history in `A` of descendant lockset analysis
dabund24 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,180 @@ | ||
| (** descendant lockset analysis [descendantLockset] | ||
| analyzes a happened-before relationship related to thread creations with mutexes held. | ||
|
|
||
| Enabling [creationLockset] may improve the precision of this analysis. | ||
|
|
||
| @see https://github.com/goblint/analyzer/pull/1923 | ||
| *) | ||
|
|
||
| open Analyses | ||
| module TID = ThreadIdDomain.Thread | ||
| module TIDs = ConcDomain.ThreadSet | ||
| module Lockset = LockDomain.MustLockset | ||
| module TidToLocksetMapTop = MapDomain.MapTop (TID) (Lockset) | ||
|
|
||
| module Spec = struct | ||
| include IdentityUnitContextsSpec | ||
|
|
||
| (** [{ t_d |-> L }] | ||
|
|
||
| [t_d] was transitively created with all members of [L] held. | ||
| Additionally, no member of [L] could have been unlocked after the creation of [t_d] | ||
| *) | ||
| module D = MapDomain.MapBot (TID) (Lockset) | ||
|
|
||
| (** [{ t_0 |-> { t_d |-> L } }] | ||
|
|
||
| [{ t_d |-> L }] is the descendant lockset valid for the [V] value, | ||
| because [t_d] was created in [t_0] with the lockset being a superset of L. | ||
|
|
||
| We suspect [MapBot] to suffice for the inner map. To ensure soundness, we use [MapTop] instead | ||
| *) | ||
| module G = MapDomain.MapBot (TID) (TidToLocksetMapTop) | ||
|
|
||
| module V = struct | ||
| include TID | ||
| include StdV | ||
| end | ||
|
|
||
| let name () = "descendantLockset" | ||
| let startstate _ = D.empty () | ||
| let exitstate _ = D.empty () | ||
|
|
||
| let threadspawn_contribute_globals man tid must_ancestor_descendants = | ||
| let descendant_lockset = man.local in | ||
| let contribute_for_descendant t_d = | ||
| let creation_lockset = man.ask @@ Queries.CreationLockset t_d in | ||
| let to_contribute = | ||
| D.fold | ||
| (fun t_l l_dl acc -> | ||
| let l_cl = Queries.CL.find tid creation_lockset in | ||
| let l_inter = Lockset.inter l_cl l_dl in | ||
| TidToLocksetMapTop.add t_l l_inter acc) | ||
| descendant_lockset | ||
| (TidToLocksetMapTop.empty ()) | ||
| in | ||
| man.sideg t_d (G.singleton tid to_contribute) | ||
| in | ||
| TIDs.iter contribute_for_descendant must_ancestor_descendants | ||
|
|
||
| let threadspawn_compute_local_contribution man tid must_ancestor_descendants = | ||
| let lockset = man.ask Queries.MustLockset in | ||
| TIDs.fold | ||
| (fun t_d -> D.join (D.singleton t_d lockset)) | ||
| must_ancestor_descendants | ||
| man.local | ||
|
|
||
| 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 (ask_of_man fman) child_tid | ||
| in | ||
| threadspawn_contribute_globals man tid must_ancestor_descendants; | ||
| threadspawn_compute_local_contribution man tid must_ancestor_descendants | ||
| | _ -> man.local | ||
|
|
||
| let unlock man possibly_running_tids lock = | ||
| TIDs.fold | ||
| (fun des_tid -> | ||
| let old_value = D.find des_tid man.local in | ||
| let new_value = Lockset.remove lock old_value in | ||
| D.add des_tid new_value) | ||
| possibly_running_tids | ||
| (D.empty ()) | ||
|
|
||
| let unknown_unlock man possibly_running_tids = | ||
| TIDs.fold | ||
| (fun des_tid -> D.add des_tid (Lockset.empty ())) | ||
| possibly_running_tids | ||
| (D.empty ()) | ||
|
|
||
| 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.must_ancestor_running_descendants (ask_of_man man) tid | ||
| in | ||
| let lock_opt = LockDomain.MustLock.of_addr addr in | ||
| (match lock_opt with | ||
| | Some lock -> unlock man possibly_running_tids lock | ||
| | None -> unknown_unlock man possibly_running_tids) | ||
| | _ -> man.local) | ||
| | _ -> man.local | ||
|
|
||
| module A = struct | ||
| module DlLhProd = Printable.Prod3 (D) (G) (Queries.LH) | ||
|
|
||
| (** ego tid * (local descendant lockset * global descendant lockset * lock history) *) | ||
| include Printable.Prod (TID) (DlLhProd) | ||
|
|
||
| module type TidToLs = MapDomain.S with type key = TID.t and type value = Lockset.t | ||
|
|
||
| (** checks if program point 1 must happen before program point 2 | ||
| @param (t1,dl1) thread id and descendant lockset of program point 1 | ||
| @param (t2,lh2) thread id and mustlock history of program point 2 | ||
| @param M module of [dl1] | ||
| *) | ||
| let happens_before (type t) (t1, dl1) (t2, lh2) (module M : TidToLs with type t = t) = | ||
| let locks_held_creating_t2 = M.find t2 dl1 in | ||
| if Lockset.is_bot locks_held_creating_t2 | ||
| then false | ||
| else ( | ||
| let relevant_lh2_threads = | ||
| Lockset.fold | ||
| (fun lock -> TIDs.union (Queries.LH.find lock lh2)) | ||
| locks_held_creating_t2 | ||
| (TIDs.empty ()) | ||
| in | ||
| TIDs.exists | ||
| (fun t_lh -> | ||
| TID.must_be_ancestor t1 t_lh | ||
| && (TID.equal t_lh t2 || TID.must_be_ancestor t_lh t2)) | ||
| relevant_lh2_threads) | ||
|
|
||
| (** checks if the entire execution of a thread must happen before a program point | ||
| @param dlg1 glabal descendant lockset of the thread | ||
| @param (t2,lh2) thread id and mustlock history of the program point | ||
| *) | ||
| let happens_before_global dlg1 (t2, lh2) = | ||
| G.exists | ||
| (fun t dl_map -> happens_before (t, dl_map) (t2, lh2) (module TidToLocksetMapTop)) | ||
| dlg1 | ||
|
|
||
| let may_race (t1, (dl1, dlg1, lh1)) (t2, (dl2, dlg2, lh2)) = | ||
| not | ||
| (happens_before (t1, dl1) (t2, lh2) (module D) | ||
| || happens_before (t2, dl2) (t1, lh1) (module D) | ||
| || happens_before_global dlg1 (t2, lh2) | ||
| || happens_before_global dlg2 (t1, lh1)) | ||
|
|
||
| (* ego tid is already printed elsewhere *) | ||
| let pretty () (_, dl_dlg_lh) = DlLhProd.pretty () dl_dlg_lh | ||
| let show (_, dl_dlg_lh) = DlLhProd.show dl_dlg_lh | ||
| let to_yojson (_, dl_dlg_lh) = DlLhProd.to_yojson dl_dlg_lh | ||
| let printXml f (_, dl_dlg_lh) = DlLhProd.printXml f dl_dlg_lh | ||
|
|
||
| let should_print (_, (dl, dlg, lh)) = | ||
| let ls_not_empty _ ls = not @@ Lockset.is_empty ls in | ||
| D.exists ls_not_empty dl | ||
| || G.exists (fun _ -> TidToLocksetMapTop.exists ls_not_empty) dlg | ||
| || Queries.LH.exists (fun l tids -> not @@ TIDs.is_empty tids) lh | ||
| end | ||
|
|
||
| let access man _ = | ||
| let lh = man.ask Queries.MustlockHistory in | ||
| let tid_lifted = man.ask Queries.CurrentThreadId in | ||
| match tid_lifted with | ||
| | `Lifted tid -> tid, (man.local, man.global tid, lh) | ||
| | _ -> ThreadIdDomain.UnknownThread, (D.empty (), G.empty (), Queries.LH.empty ()) | ||
| end | ||
|
|
||
| let _ = | ||
| MCP.register_analysis | ||
| ~dep:[ "threadid"; "mutex"; "threadJoins"; "threadDescendants"; "mustlockHistory" ] | ||
| (module Spec : MCPSpec) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| (** must-lock history analysis [mustlockHistory] | ||
| collects for locks, in which threads a lock operation must have happened | ||
| before reaching the current program point. | ||
|
|
||
| @see https://github.com/goblint/analyzer/pull/1923 | ||
| *) | ||
|
|
||
| open Analyses | ||
| module TIDs = SetDomain.Reverse (ConcDomain.ThreadSet) | ||
| module Lock = LockDomain.MustLock | ||
|
|
||
| module Spec = struct | ||
dabund24 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| include IdentityUnitContextsSpec | ||
|
|
||
| (** [{ l |-> T }] | ||
|
|
||
| [l] must have been in all members of [T]. | ||
| *) | ||
| module D = Queries.LH | ||
|
|
||
| let name () = "mustlockHistory" | ||
| let startstate _ = D.empty () | ||
| let exitstate _ = D.empty () | ||
|
|
||
| let lock man tid lock = | ||
| let old_threadset = D.find lock man.local in | ||
| let new_threadset = TIDs.add tid old_threadset in | ||
| D.add lock new_threadset man.local | ||
|
|
||
| let event man e _ = | ||
| match e with | ||
| (* we only handle exclusive locks here *) | ||
| | Events.Lock (addr, true) -> | ||
| let tid_lifted = man.ask Queries.CurrentThreadId in | ||
| let lock_opt = Lock.of_addr addr in | ||
| (match tid_lifted, lock_opt with | ||
| | `Lifted tid, Some l -> lock man tid l | ||
| | _ -> man.local) | ||
| | _ -> man.local | ||
|
|
||
| let query man (type a) (x : a Queries.t) : a Queries.result = | ||
| match x with | ||
| | Queries.MustlockHistory -> (man.local : D.t) | ||
| | _ -> Queries.Result.top x | ||
| end | ||
|
|
||
| let _ = MCP.register_analysis ~dep:[ "threadid" ] (module Spec : MCPSpec) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.