You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
199 lines
6.7 KiB
199 lines
6.7 KiB
(*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*)
|
|
|
|
open! IStd
|
|
module F = Format
|
|
|
|
(** Domain for thread-type. The main goals are
|
|
- Track code paths that are explicitly on UI/BG thread (via annotations, or assertions).
|
|
- Maintain UI/BG-thread-ness through the call stack (if a caller is of unknown status and
|
|
callee is on UI/BG thread then caller must be on the UI/BG thread too).
|
|
- Traces with "UI-thread" status cannot interleave but all other combinations can.
|
|
- Top is AnyThread, which means that there are executions on both UI and BG threads on
|
|
this method.
|
|
- Bottom is UnknownThread, and used as initial state.
|
|
*)
|
|
module ThreadDomain : sig
|
|
type t = UnknownThread | UIThread | BGThread | AnyThread
|
|
|
|
include AbstractDomain.WithBottom with type t := t
|
|
end
|
|
|
|
(** Abstraction of a path that represents a lock, special-casing comparison
|
|
to work over type, base variable modulo this and access list *)
|
|
module Lock : sig
|
|
include PrettyPrintable.PrintableOrderedType with type t = AccessPath.t
|
|
|
|
val owner_class : t -> Typ.name option
|
|
(** Class of the root variable of the path representing the lock *)
|
|
|
|
val pp_locks : F.formatter -> t -> unit
|
|
|
|
val describe : F.formatter -> t -> unit
|
|
end
|
|
|
|
module Event : sig
|
|
type t =
|
|
| LockAcquire of Lock.t
|
|
| MayBlock of (string * StarvationModels.severity)
|
|
| StrictModeCall of string
|
|
[@@deriving compare]
|
|
|
|
val describe : F.formatter -> t -> unit
|
|
end
|
|
|
|
module LockState : AbstractDomain.WithTop
|
|
|
|
(** a lock acquisition with location information *)
|
|
module Acquisition : sig
|
|
type t = private
|
|
{lock: Lock.t; loc: Location.t [@compare.ignore]; procname: Typ.Procname.t [@compare.ignore]}
|
|
[@@deriving compare]
|
|
end
|
|
|
|
(** A set of lock acquisitions with source locations and procnames. *)
|
|
module Acquisitions : sig
|
|
include PrettyPrintable.PPSet with type elt = Acquisition.t
|
|
|
|
val lock_is_held : Lock.t -> t -> bool
|
|
(** is the given lock in the set *)
|
|
end
|
|
|
|
(** An event and the currently-held locks at the time it occurred. *)
|
|
module CriticalPairElement : sig
|
|
type t = private {acquisitions: Acquisitions.t; event: Event.t; thread: ThreadDomain.t}
|
|
end
|
|
|
|
(** A [CriticalPairElement] equipped with a call stack.
|
|
The intuition is that if we have a critical pair `(locks, event)` in the summary
|
|
of a method then there is a trace of that method where `event` occurs, and right
|
|
before it occurs the locks held are exactly `locks` (no over/under approximation).
|
|
We call it "critical" because the information here alone determines deadlock conditions.
|
|
*)
|
|
module CriticalPair : sig
|
|
type t = private {elem: CriticalPairElement.t; loc: Location.t; trace: CallSite.t list}
|
|
|
|
include PrettyPrintable.PrintableOrderedType with type t := t
|
|
|
|
val get_loc : t -> Location.t
|
|
(** outermost callsite location *)
|
|
|
|
val get_earliest_lock_or_call_loc : procname:Typ.Procname.t -> t -> Location.t
|
|
(** outermost callsite location OR lock acquisition *)
|
|
|
|
val may_deadlock : t -> t -> bool
|
|
(** two pairs can run in parallel and satisfy the conditions for deadlock *)
|
|
|
|
val make_trace :
|
|
?header:string -> ?include_acquisitions:bool -> Typ.Procname.t -> t -> Errlog.loc_trace
|
|
|
|
val is_uithread : t -> bool
|
|
(** is pair about an event on the UI thread *)
|
|
|
|
val can_run_in_parallel : t -> t -> bool
|
|
(** can two pairs describe events on two threads that can run in parallel *)
|
|
end
|
|
|
|
module CriticalPairs : AbstractDomain.FiniteSetS with type elt = CriticalPair.t
|
|
|
|
module GuardToLockMap : AbstractDomain.WithTop
|
|
|
|
(** Tracks whether a variable has been tested for whether we execute on UI thread, or
|
|
has been assigned an executor object. *)
|
|
module Attribute : sig
|
|
type t = Nothing | ThreadGuard | Executor of StarvationModels.executor_thread_constraint
|
|
|
|
include AbstractDomain.WithTop with type t := t
|
|
end
|
|
|
|
(** Tracks all variables assigned values of [Attribute] *)
|
|
module AttributeDomain : sig
|
|
include
|
|
AbstractDomain.InvertedMapS
|
|
with type key = HilExp.AccessExpression.t
|
|
and type value = Attribute.t
|
|
|
|
val is_thread_guard : HilExp.AccessExpression.t -> t -> bool
|
|
|
|
val get_executor_constraint :
|
|
HilExp.AccessExpression.t -> t -> StarvationModels.executor_thread_constraint option
|
|
|
|
val exit_scope : Var.t list -> t -> t
|
|
end
|
|
|
|
(** A record of scheduled parallel work: the method scheduled to run, where, and on what thread. *)
|
|
module ScheduledWorkItem : sig
|
|
type t = {procname: Typ.Procname.t; loc: Location.t; thread: ThreadDomain.t}
|
|
|
|
include PrettyPrintable.PrintableOrderedType with type t := t
|
|
end
|
|
|
|
module ScheduledWorkDomain : AbstractDomain.FiniteSetS with type elt = ScheduledWorkItem.t
|
|
|
|
type t =
|
|
{ guard_map: GuardToLockMap.t
|
|
; lock_state: LockState.t
|
|
; critical_pairs: CriticalPairs.t
|
|
; attributes: AttributeDomain.t
|
|
; thread: ThreadDomain.t
|
|
; scheduled_work: ScheduledWorkDomain.t }
|
|
|
|
include AbstractDomain.WithBottom with type t := t
|
|
|
|
val acquire : ?tenv:Tenv.t -> t -> procname:Typ.Procname.t -> loc:Location.t -> Lock.t list -> t
|
|
(** simultaneously acquire a number of locks, no-op if list is empty *)
|
|
|
|
val release : t -> Lock.t list -> t
|
|
(** simultaneously release a number of locks, no-op if list is empty *)
|
|
|
|
val blocking_call : callee:Typ.Procname.t -> StarvationModels.severity -> loc:Location.t -> t -> t
|
|
|
|
val strict_mode_call : callee:Typ.Procname.t -> loc:Location.t -> t -> t
|
|
|
|
val add_guard :
|
|
acquire_now:bool
|
|
-> procname:Typ.Procname.t
|
|
-> loc:Location.t
|
|
-> Tenv.t
|
|
-> t
|
|
-> HilExp.t
|
|
-> Lock.t
|
|
-> t
|
|
(** Install a mapping from the guard expression to the lock provided, and optionally lock it. *)
|
|
|
|
val lock_guard : procname:Typ.Procname.t -> loc:Location.t -> Tenv.t -> t -> HilExp.t -> t
|
|
(** Acquire the lock the guard was constructed with. *)
|
|
|
|
val remove_guard : t -> HilExp.t -> t
|
|
(** Destroy the guard and release its lock. *)
|
|
|
|
val unlock_guard : t -> HilExp.t -> t
|
|
(** Release the lock the guard was constructed with. *)
|
|
|
|
val schedule_work :
|
|
Location.t -> StarvationModels.executor_thread_constraint -> t -> Typ.Procname.t -> t
|
|
(** record the fact that a method is scheduled to run on a certain thread/executor *)
|
|
|
|
type summary =
|
|
{ critical_pairs: CriticalPairs.t
|
|
; thread: ThreadDomain.t
|
|
; scheduled_work: ScheduledWorkDomain.t
|
|
; return_attribute: Attribute.t }
|
|
|
|
val empty_summary : summary
|
|
|
|
val pp_summary : F.formatter -> summary -> unit
|
|
|
|
val integrate_summary :
|
|
?tenv:Tenv.t -> ?lhs:HilExp.AccessExpression.t -> CallSite.t -> t -> summary -> t
|
|
(** apply a callee summary to the current abstract state;
|
|
[lhs] is the expression assigned the returned value, if any *)
|
|
|
|
val summary_of_astate : Procdesc.t -> t -> summary
|
|
|
|
val filter_blocking_calls : t -> t
|