[starvation][whole-program] add a bit of typestate/dataflow

Summary:
- Unify treatment of modelled and annotated executors by making things go through attributes.
- Add a return attribute to summaries, so that we can track flows of thread guards/executors/future stuff through returned values.
- Dispatch modeled functions to model summaries.

This will help in following diffs where runnables will also go through attributes.

Reviewed By: skcho

Differential Revision: D18660185

fbshipit-source-id: e26b1083e
master
Nikos Gorogiannis 5 years ago committed by Facebook Github Bot
parent 61c0a92682
commit 20a7e9d75b

@ -1422,6 +1422,8 @@ module Fieldname = struct
let equal = [%compare.equal: t] let equal = [%compare.equal: t]
let is_java = function Java _ -> true | Clang _ -> false
module T = struct module T = struct
type nonrec t = t type nonrec t = t

@ -639,6 +639,8 @@ module Fieldname : sig
val equal : t -> t -> bool val equal : t -> t -> bool
(** Equality for field names. *) (** Equality for field names. *)
val is_java : t -> bool
module Set : Caml.Set.S with type elt = t module Set : Caml.Set.S with type elt = t
(** Set for fieldnames *) (** Set for fieldnames *)

@ -235,12 +235,12 @@ let schedules_work_on_bg_thread =
type executor_thread_constraint = ForUIThread | ForNonUIThread | ForUnknownThread type executor_thread_constraint = ForUIThread | ForNonUIThread | ForUnknownThread
[@@deriving equal] [@@deriving equal]
(* Executors are usually stored in fields and annotated according to what type of thread (* Executors are sometimes stored in fields and annotated according to what type of thread
they schedule work on. Given an expression representing such a field, try to find the kind of they schedule work on. Given an expression representing such a field, try to find the kind of
annotation constraint, if any. *) annotation constraint, if any. *)
let rec get_executor_thread_constraint tenv (receiver : HilExp.AccessExpression.t) = let rec get_executor_thread_annotation_constraint tenv (receiver : HilExp.AccessExpression.t) =
match receiver with match receiver with
| FieldOffset (_, field_name) -> | FieldOffset (_, field_name) when Typ.Fieldname.is_java field_name ->
Typ.Fieldname.Java.get_class field_name Typ.Fieldname.Java.get_class field_name
|> Typ.Name.Java.from_string |> Tenv.lookup tenv |> Typ.Name.Java.from_string |> Tenv.lookup tenv
|> Option.map ~f:(fun (tstruct : Typ.Struct.t) -> tstruct.fields @ tstruct.statics) |> Option.map ~f:(fun (tstruct : Typ.Struct.t) -> tstruct.fields @ tstruct.statics)
@ -250,7 +250,7 @@ let rec get_executor_thread_constraint tenv (receiver : HilExp.AccessExpression.
else if Annotations.(ia_ends_with annot for_non_ui_thread) then Some ForNonUIThread else if Annotations.(ia_ends_with annot for_non_ui_thread) then Some ForNonUIThread
else None ) else None )
| Dereference prefix -> | Dereference prefix ->
get_executor_thread_constraint tenv prefix get_executor_thread_annotation_constraint tenv prefix
| _ -> | _ ->
None None
@ -274,7 +274,7 @@ let get_run_method_from_runnable tenv runnable =
(* Syntactically match for certain methods known to return executors. *) (* Syntactically match for certain methods known to return executors. *)
let get_executor_effect ~attrs_of_pname tenv callee actuals = let get_returned_executor ~attrs_of_pname tenv callee actuals =
let type_check = let type_check =
lazy lazy
( attrs_of_pname callee ( attrs_of_pname callee

@ -39,7 +39,7 @@ val schedules_work : Tenv.t -> Typ.Procname.t -> bool
type executor_thread_constraint = ForUIThread | ForNonUIThread | ForUnknownThread type executor_thread_constraint = ForUIThread | ForNonUIThread | ForUnknownThread
[@@deriving equal] [@@deriving equal]
val get_executor_thread_constraint : val get_executor_thread_annotation_constraint :
Tenv.t -> HilExp.AccessExpression.t -> executor_thread_constraint option Tenv.t -> HilExp.AccessExpression.t -> executor_thread_constraint option
(** given an executor receiver, get its thread constraint, if any. [None] means lookup somehow failed, (** given an executor receiver, get its thread constraint, if any. [None] means lookup somehow failed,
whereas [Some UnknownThread] means the receiver is an unannotated executor. *) whereas [Some UnknownThread] means the receiver is an unannotated executor. *)
@ -47,7 +47,7 @@ val get_executor_thread_constraint :
val get_run_method_from_runnable : Tenv.t -> HilExp.AccessExpression.t -> Typ.Procname.t option val get_run_method_from_runnable : Tenv.t -> HilExp.AccessExpression.t -> Typ.Procname.t option
(** given a receiver, find the [run()] method in the appropriate class *) (** given a receiver, find the [run()] method in the appropriate class *)
val get_executor_effect : val get_returned_executor :
attrs_of_pname:(Typ.Procname.t -> ProcAttributes.t option) attrs_of_pname:(Typ.Procname.t -> ProcAttributes.t option)
-> Tenv.t -> Tenv.t
-> Typ.Procname.t -> Typ.Procname.t

@ -72,21 +72,16 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
astate astate
let do_thread_assert_effect (ret_var, _) callee (astate : Domain.t) = let get_exp_attributes tenv exp (astate : Domain.t) =
let open Domain in let open Domain in
match ConcurrencyModels.get_thread_assert_effect callee with AttributeDomain.find_opt exp astate.attributes
| BackgroundThread -> |> IOption.if_none_evalopt ~f:(fun () ->
{astate with thread= ThreadDomain.BGThread} StarvationModels.get_executor_thread_annotation_constraint tenv exp
| MainThread -> |> Option.map ~f:(fun constr -> Attribute.Executor constr) )
{astate with thread= ThreadDomain.UIThread}
| MainThreadIfTrue ->
let attributes = AttributeDomain.add ret_var Attribute.Thread astate.attributes in
{astate with attributes}
| UnknownThread ->
astate
let do_work_scheduling tenv callee actuals loc (astate : Domain.t) = let do_work_scheduling tenv callee actuals loc (astate : Domain.t) =
let open Domain in
let schedule_work runnable init thread = let schedule_work runnable init thread =
StarvationModels.get_run_method_from_runnable tenv runnable StarvationModels.get_run_method_from_runnable tenv runnable
|> Option.fold ~init ~f:(Domain.schedule_work loc thread) |> Option.fold ~init ~f:(Domain.schedule_work loc thread)
@ -94,16 +89,14 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
match actuals with match actuals with
| [HilExp.AccessExpression executor; HilExp.AccessExpression runnable] | [HilExp.AccessExpression executor; HilExp.AccessExpression runnable]
when StarvationModels.schedules_work tenv callee -> when StarvationModels.schedules_work tenv callee ->
let thread_opt = let thread =
IList.force_until_first_some match get_exp_attributes tenv executor astate with
[ (* match an executor call that we have a model for *) | Some (Attribute.Executor constr) ->
lazy (StarvationModels.get_executor_thread_constraint tenv executor) constr
; (* match an executor call where the executor is stored in a variable *) | _ ->
lazy (Domain.AttributeDomain.get_executor_constraint executor astate.attributes) StarvationModels.ForUnknownThread
; (* the receiver is an executor stored in a variable for which we have no knowledge *) in
lazy (Some StarvationModels.ForUnknownThread) ] schedule_work runnable astate thread
in
Option.fold thread_opt ~init:astate ~f:(schedule_work runnable)
| HilExp.AccessExpression runnable :: _ | HilExp.AccessExpression runnable :: _
when StarvationModels.schedules_work_on_ui_thread tenv callee -> when StarvationModels.schedules_work_on_ui_thread tenv callee ->
schedule_work runnable astate StarvationModels.ForUIThread schedule_work runnable astate StarvationModels.ForUIThread
@ -114,17 +107,43 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
astate astate
let do_executor_effect tenv (ret_var, _) callee actuals astate = let do_assignment tenv lhs_access_exp rhs_exp (astate : Domain.t) =
HilExp.get_access_exprs rhs_exp
|> List.filter_map ~f:(fun exp -> get_exp_attributes tenv exp astate)
|> List.reduce ~f:Domain.Attribute.join
|> Option.value_map ~default:astate ~f:(fun attribute ->
let attributes = Domain.AttributeDomain.add lhs_access_exp attribute astate.attributes in
{astate with attributes} )
let do_call ProcData.{tenv; summary} lhs callee actuals loc astate =
let open Domain in let open Domain in
StarvationModels.get_executor_effect ~attrs_of_pname tenv callee actuals let get_returned_executor_summary () =
|> Option.fold ~init:astate ~f:(fun acc effect -> StarvationModels.get_returned_executor ~attrs_of_pname tenv callee actuals
let attributes = |> Option.map ~f:(fun thread_constraint ->
AttributeDomain.add ret_var (Attribute.Executor effect) astate.attributes {empty_summary with return_attribute= Attribute.Executor thread_constraint} )
in in
{acc with attributes} ) let get_thread_assert_summary () =
match ConcurrencyModels.get_thread_assert_effect callee with
| BackgroundThread ->
Some {empty_summary with thread= ThreadDomain.BGThread}
| MainThread ->
Some {empty_summary with thread= ThreadDomain.UIThread}
| MainThreadIfTrue ->
Some {empty_summary with return_attribute= Attribute.ThreadGuard}
| UnknownThread ->
None
in
let get_callee_summary () = Payload.read ~caller_summary:summary ~callee_pname:callee in
let callsite = CallSite.make callee loc in
get_returned_executor_summary ()
|> IOption.if_none_evalopt ~f:get_thread_assert_summary
|> IOption.if_none_evalopt ~f:get_callee_summary
|> Option.fold ~init:astate ~f:(Domain.integrate_summary ~tenv ~lhs callsite)
let exec_instr (astate : Domain.t) {ProcData.summary; tenv; extras} _ (instr : HilInstr.t) = let exec_instr (astate : Domain.t) ({ProcData.summary; tenv; extras} as procdata) _
(instr : HilInstr.t) =
let open ConcurrencyModels in let open ConcurrencyModels in
let open StarvationModels in let open StarvationModels in
let get_lock_path = get_lock_path extras in let get_lock_path = get_lock_path extras in
@ -134,20 +153,17 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
List.filter_map ~f:get_lock_path locks |> Domain.acquire ~tenv astate ~procname ~loc List.filter_map ~f:get_lock_path locks |> Domain.acquire ~tenv astate ~procname ~loc
in in
let do_unlock locks astate = List.filter_map ~f:get_lock_path locks |> Domain.release astate in let do_unlock locks astate = List.filter_map ~f:get_lock_path locks |> Domain.release astate in
let do_call callee loc astate =
let callsite = CallSite.make callee loc in
Payload.read ~caller_summary:summary ~callee_pname:callee
|> Option.fold ~init:astate ~f:(Domain.integrate_summary ~tenv callsite)
in
match instr with match instr with
| Assign _ | Call (_, Indirect _, _, _, _) -> | Assign (lhs_access_exp, rhs_exp, _) ->
astate do_assignment tenv lhs_access_exp rhs_exp astate
| Metadata (Sil.ExitScope (vars, _)) -> | Metadata (Sil.ExitScope (vars, _)) ->
{astate with attributes= Domain.AttributeDomain.exit_scope vars astate.attributes} {astate with attributes= Domain.AttributeDomain.exit_scope vars astate.attributes}
| Metadata _ -> | Metadata _ ->
astate astate
| Assume (assume_exp, _, _, _) -> | Assume (assume_exp, _, _, _) ->
do_assume assume_exp astate do_assume assume_exp astate
| Call (_, Indirect _, _, _, _) ->
astate
| Call (_, Direct callee, actuals, _, _) when should_skip_analysis tenv callee actuals -> | Call (_, Direct callee, actuals, _, _) when should_skip_analysis tenv callee actuals ->
astate astate
| Call (ret_base, Direct callee, actuals, _, loc) -> ( | Call (ret_base, Direct callee, actuals, _, loc) -> (
@ -178,19 +194,17 @@ module TransferFunctions (CFG : ProcCfg.S) = struct
| NoEffect when is_java && is_strict_mode_violation tenv callee actuals -> | NoEffect when is_java && is_strict_mode_violation tenv callee actuals ->
Domain.strict_mode_call ~callee ~loc astate Domain.strict_mode_call ~callee ~loc astate
| NoEffect when is_java -> ( | NoEffect when is_java -> (
let astate = let ret_exp = HilExp.AccessExpression.base ret_base in
do_thread_assert_effect ret_base callee astate let astate = do_work_scheduling tenv callee actuals loc astate in
|> do_executor_effect tenv ret_base callee actuals
|> do_work_scheduling tenv callee actuals loc
in
match may_block tenv callee actuals with match may_block tenv callee actuals with
| Some sev -> | Some sev ->
Domain.blocking_call ~callee sev ~loc astate Domain.blocking_call ~callee sev ~loc astate
| None -> | None ->
do_call callee loc astate ) do_call procdata ret_exp callee actuals loc astate )
| NoEffect -> | NoEffect ->
(* in C++/Obj C we only care about deadlocks, not starvation errors *) (* in C++/Obj C we only care about deadlocks, not starvation errors *)
do_call callee loc astate ) let ret_exp = HilExp.AccessExpression.base ret_base in
do_call procdata ret_exp callee actuals loc astate )
let pp_session_name _node fmt = F.pp_print_string fmt "starvation" let pp_session_name _node fmt = F.pp_print_string fmt "starvation"
@ -241,7 +255,7 @@ let analyze_procedure {Callbacks.exe_env; summary} =
in in
Analyzer.compute_post proc_data ~initial Analyzer.compute_post proc_data ~initial
|> Option.map ~f:filter_blocks |> Option.map ~f:filter_blocks
|> Option.map ~f:Domain.summary_of_astate |> Option.map ~f:(Domain.summary_of_astate proc_desc)
|> Option.fold ~init:summary ~f:(fun acc payload -> Payload.update_summary payload acc) |> Option.fold ~init:summary ~f:(fun acc payload -> Payload.update_summary payload acc)
@ -580,7 +594,7 @@ let get_summary_of_scheduled_work (work_item : Domain.ScheduledWorkItem.t) =
let callsite = CallSite.make work_item.procname work_item.loc in let callsite = CallSite.make work_item.procname work_item.loc in
Summary.OnDisk.get work_item.procname Summary.OnDisk.get work_item.procname
|> Option.bind ~f:(fun (summary : Summary.t) -> Payloads.starvation summary.payloads) |> Option.bind ~f:(fun (summary : Summary.t) -> Payloads.starvation summary.payloads)
|> Option.map ~f:(Domain.integrate_summary ?tenv:None callsite astate) |> Option.map ~f:(Domain.integrate_summary callsite astate)
|> Option.map ~f:(fun (astate : Domain.t) -> astate.critical_pairs) |> Option.map ~f:(fun (astate : Domain.t) -> astate.critical_pairs)

@ -444,7 +444,7 @@ module GuardToLockMap = struct
end end
module Attribute = struct module Attribute = struct
type t = Nothing | Thread | Executor of StarvationModels.executor_thread_constraint type t = Nothing | ThreadGuard | Executor of StarvationModels.executor_thread_constraint
[@@deriving equal] [@@deriving equal]
let top = Nothing let top = Nothing
@ -455,8 +455,8 @@ module Attribute = struct
( match t with ( match t with
| Nothing -> | Nothing ->
"Nothing" "Nothing"
| Thread -> | ThreadGuard ->
"Thread" "ThreadGuard"
| Executor StarvationModels.ForUIThread -> | Executor StarvationModels.ForUIThread ->
"Executor(UI)" "Executor(UI)"
| Executor StarvationModels.ForNonUIThread -> | Executor StarvationModels.ForNonUIThread ->
@ -474,26 +474,22 @@ module Attribute = struct
end end
module AttributeDomain = struct module AttributeDomain = struct
include AbstractDomain.InvertedMap (Var) (Attribute) include AbstractDomain.SafeInvertedMap (HilExp.AccessExpression) (Attribute)
let is_thread_guard (acc_exp : HilExp.AccessExpression.t) t = let is_thread_guard acc_exp t =
match acc_exp with find_opt acc_exp t |> Option.exists ~f:(function Attribute.ThreadGuard -> true | _ -> false)
| Base (v, _) ->
find_opt v t |> Option.exists ~f:(function Attribute.Thread -> true | _ -> false)
| _ ->
false
let get_executor_constraint (acc_exp : HilExp.AccessExpression.t) t = let get_executor_constraint acc_exp t =
match acc_exp with find_opt acc_exp t |> Option.bind ~f:(function Attribute.Executor c -> Some c | _ -> None)
| Base (v, _) ->
find_opt v t |> Option.bind ~f:(function Attribute.Executor c -> Some c | _ -> None)
| _ ->
None
let exit_scope vars t = let exit_scope vars t =
let pred key _value = not (List.exists vars ~f:(Var.equal key)) in let pred key _value =
HilExp.AccessExpression.get_base key
|> fst
|> fun v -> not (List.exists vars ~f:(Var.equal v))
in
filter pred t filter pred t
end end
@ -646,26 +642,47 @@ let schedule_work loc thread_constraint astate procname =
type summary = type summary =
{critical_pairs: CriticalPairs.t; thread: ThreadDomain.t; scheduled_work: ScheduledWorkDomain.t} { critical_pairs: CriticalPairs.t
; thread: ThreadDomain.t
; scheduled_work: ScheduledWorkDomain.t
; return_attribute: Attribute.t }
let empty_summary : summary =
{ critical_pairs= CriticalPairs.bottom
; thread= ThreadDomain.bottom
; scheduled_work= ScheduledWorkDomain.bottom
; return_attribute= Attribute.top }
let pp_summary fmt (summary : summary) = let pp_summary fmt (summary : summary) =
F.fprintf fmt "{thread= %a; critical_pairs= %a; scheduled_work= %a}" ThreadDomain.pp F.fprintf fmt "{thread= %a; critical_pairs= %a; scheduled_work= %a; return_attributes= %a}"
summary.thread CriticalPairs.pp summary.critical_pairs ScheduledWorkDomain.pp ThreadDomain.pp summary.thread CriticalPairs.pp summary.critical_pairs ScheduledWorkDomain.pp
summary.scheduled_work summary.scheduled_work Attribute.pp summary.return_attribute
let integrate_summary ?tenv callsite (astate : t) (summary : summary) = let integrate_summary ?tenv ?lhs callsite (astate : t) (summary : summary) =
let critical_pairs' = let critical_pairs' =
CriticalPairs.with_callsite summary.critical_pairs ?tenv astate.lock_state callsite CriticalPairs.with_callsite summary.critical_pairs ?tenv astate.lock_state callsite
astate.thread astate.thread
in in
{ astate with { astate with
critical_pairs= CriticalPairs.join astate.critical_pairs critical_pairs' critical_pairs= CriticalPairs.join astate.critical_pairs critical_pairs'
; thread= ThreadDomain.integrate_summary ~caller:astate.thread ~callee:summary.thread } ; thread= ThreadDomain.integrate_summary ~caller:astate.thread ~callee:summary.thread
; attributes=
Option.value_map lhs ~default:astate.attributes ~f:(fun lhs_exp ->
AttributeDomain.add lhs_exp summary.return_attribute astate.attributes ) }
let summary_of_astate : t -> summary = let summary_of_astate : Procdesc.t -> t -> summary =
fun astate -> fun proc_desc astate ->
{ critical_pairs= astate.critical_pairs { critical_pairs= astate.critical_pairs
; thread= astate.thread ; thread= astate.thread
; scheduled_work= astate.scheduled_work } ; scheduled_work= astate.scheduled_work
; return_attribute=
(let return_var_exp =
HilExp.AccessExpression.base
( Var.of_pvar (Pvar.get_ret_pvar (Procdesc.get_proc_name proc_desc))
, Procdesc.get_ret_type proc_desc )
in
AttributeDomain.find_opt return_var_exp astate.attributes
|> Option.value ~default:Attribute.Nothing ) }

@ -105,14 +105,17 @@ module GuardToLockMap : AbstractDomain.WithTop
(** Tracks whether a variable has been tested for whether we execute on UI thread, or (** Tracks whether a variable has been tested for whether we execute on UI thread, or
has been assigned an executor object. *) has been assigned an executor object. *)
module Attribute : sig module Attribute : sig
type t = Nothing | Thread | Executor of StarvationModels.executor_thread_constraint type t = Nothing | ThreadGuard | Executor of StarvationModels.executor_thread_constraint
include AbstractDomain.WithTop with type t := t include AbstractDomain.WithTop with type t := t
end end
(** Tracks all variables assigned values of [Attribute] *) (** Tracks all variables assigned values of [Attribute] *)
module AttributeDomain : sig module AttributeDomain : sig
include AbstractDomain.InvertedMapS with type key = Var.t and type value = Attribute.t include
AbstractDomain.InvertedMapS
with type key = HilExp.AccessExpression.t
and type value = Attribute.t
val is_thread_guard : HilExp.AccessExpression.t -> t -> bool val is_thread_guard : HilExp.AccessExpression.t -> t -> bool
@ -176,12 +179,20 @@ val schedule_work :
(** record the fact that a method is scheduled to run on a certain thread/executor *) (** record the fact that a method is scheduled to run on a certain thread/executor *)
type summary = type summary =
{critical_pairs: CriticalPairs.t; thread: ThreadDomain.t; scheduled_work: ScheduledWorkDomain.t} { 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 pp_summary : F.formatter -> summary -> unit
val integrate_summary : ?tenv:Tenv.t -> CallSite.t -> t -> summary -> t 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 : t -> summary val summary_of_astate : Procdesc.t -> t -> summary
val filter_blocking_calls : t -> t val filter_blocking_calls : t -> t

@ -0,0 +1,104 @@
/*
* 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.
*/
import android.os.Binder;
import android.os.RemoteException;
import java.util.concurrent.Executor;
class AttributeFlows {
static Binder binder;
private static void doTransact() {
try {
binder.transact(0, null, null, 0);
} catch (RemoteException e) {
}
}
private Executor getBackgroundExecutor() {
return Executors.getBackgroundExecutor();
}
private Executor indirectlyGetBackgroundExecutor() {
return getBackgroundExecutor();
}
private Executor getForegroundExecutor() {
return Executors.getForegroundExecutor();
}
private Executor indirectlyGetForegroundExecutor() {
return getForegroundExecutor();
}
// starvation via scheduling a transaction on UI thread
public void postBlockingCallToUIThreadBad() {
indirectlyGetForegroundExecutor()
.execute(
new Runnable() {
@Override
public void run() {
doTransact();
}
});
}
// no report here
public void postBlockingCallToNonUIThreadOk() {
indirectlyGetBackgroundExecutor()
.execute(
new Runnable() {
@Override
public void run() {
doTransact();
}
});
}
@ForUiThread private final Executor mUiThreadExecutor = null;
@ForNonUiThread private final Executor mNonUiThreadExecutor = null;
private Executor getAnnotatedBackgroundExecutor() {
return mNonUiThreadExecutor;
}
private Executor indirectlyGetAnnotatedBackgroundExecutor() {
return getAnnotatedBackgroundExecutor();
}
private Executor getAnnotatedForegroundExecutor() {
return mUiThreadExecutor;
}
private Executor indirectlyGetAnnotatedForegroundExecutor() {
return getAnnotatedForegroundExecutor();
}
// starvation via scheduling a transaction on UI thread
public void postBlockingCallToAnnnotatedUIThreadBad() {
indirectlyGetAnnotatedForegroundExecutor()
.execute(
new Runnable() {
@Override
public void run() {
doTransact();
}
});
}
// no report here
public void postBlockingCallToAnnotatedNonUIThreadOk() {
indirectlyGetAnnotatedBackgroundExecutor()
.execute(
new Runnable() {
@Override
public void run() {
doTransact();
}
});
}
}

@ -0,0 +1,32 @@
/*
* 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.
*/
import java.util.concurrent.Executor;
// modeled executors
class Executors {
static Executor uiExecutor;
static Executor getForegroundExecutor() {
return uiExecutor;
}
static Executor bgExecutor;
static Executor getBackgroundExecutor() {
return bgExecutor;
}
public static void postOnUiThread(Runnable runnable) {}
public static void runOnUiThread(Runnable runnable) {}
public static void postOnUiThreadDelayed(Runnable runnable, long delayMs) {}
public static void scheduleGuaranteedDelayed(
Runnable job, long delayMillis, long lastExecution) {}
}

@ -7,7 +7,6 @@
import android.os.Binder; import android.os.Binder;
import android.os.RemoteException; import android.os.RemoteException;
import java.util.concurrent.Executor;
class ModeledExecutors { class ModeledExecutors {
static Binder binder; static Binder binder;
@ -116,27 +115,3 @@ class ModeledExecutors {
}); });
} }
} }
// modeled executors
class Executors {
static Executor uiExecutor;
static Executor getForegroundExecutor() {
return uiExecutor;
}
static Executor bgExecutor;
static Executor getBackgroundExecutor() {
return bgExecutor;
}
public static void postOnUiThread(Runnable runnable) {}
public static void runOnUiThread(Runnable runnable) {}
public static void postOnUiThreadDelayed(Runnable runnable, long delayMs) {}
public static void scheduleGuaranteedDelayed(
Runnable job, long delayMillis, long lastExecution) {}
}

@ -1,15 +1,17 @@
codetoanalyze/java/starvation-whole-program/AttributeFlows.java, AttributeFlows.postBlockingCallToAnnnotatedUIThreadBad():void, 84, STARVATION, no_bucket, ERROR, [`void AttributeFlows.postBlockingCallToAnnnotatedUIThreadBad()`,Method call: `void AttributeFlows$3.run()`,Method call: `void AttributeFlows.doTransact()`,calls `boolean Binder.transact(int,Parcel,Parcel,int)`]
codetoanalyze/java/starvation-whole-program/AttributeFlows.java, AttributeFlows.postBlockingCallToUIThreadBad():void, 41, STARVATION, no_bucket, ERROR, [`void AttributeFlows.postBlockingCallToUIThreadBad()`,Method call: `void AttributeFlows$1.run()`,Method call: `void AttributeFlows.doTransact()`,calls `boolean Binder.transact(int,Parcel,Parcel,int)`]
codetoanalyze/java/starvation-whole-program/Deadlock.java, Deadlock.postDeadlockBad():void, 19, DEADLOCK, no_bucket, ERROR, [[Trace 1] `void Deadlock.postDeadlockBad()`,Method call: `void Deadlock$1.run()`, locks `this.monitorA` in `class Deadlock`, locks `this.monitorB` in `class Deadlock`,[Trace 2] `void Deadlock.postDeadlockBad()`,Method call: `void Deadlock$2.run()`, locks `this.monitorB` in `class Deadlock`, locks `this.monitorA` in `class Deadlock`] codetoanalyze/java/starvation-whole-program/Deadlock.java, Deadlock.postDeadlockBad():void, 19, DEADLOCK, no_bucket, ERROR, [[Trace 1] `void Deadlock.postDeadlockBad()`,Method call: `void Deadlock$1.run()`, locks `this.monitorA` in `class Deadlock`, locks `this.monitorB` in `class Deadlock`,[Trace 2] `void Deadlock.postDeadlockBad()`,Method call: `void Deadlock$2.run()`, locks `this.monitorB` in `class Deadlock`, locks `this.monitorA` in `class Deadlock`]
codetoanalyze/java/starvation-whole-program/Deadlock.java, Deadlock.postDeadlockBad():void, 30, DEADLOCK, no_bucket, ERROR, [[Trace 1] `void Deadlock.postDeadlockBad()`,Method call: `void Deadlock$2.run()`, locks `this.monitorB` in `class Deadlock`, locks `this.monitorA` in `class Deadlock`,[Trace 2] `void Deadlock.postDeadlockBad()`,Method call: `void Deadlock$1.run()`, locks `this.monitorA` in `class Deadlock`, locks `this.monitorB` in `class Deadlock`] codetoanalyze/java/starvation-whole-program/Deadlock.java, Deadlock.postDeadlockBad():void, 30, DEADLOCK, no_bucket, ERROR, [[Trace 1] `void Deadlock.postDeadlockBad()`,Method call: `void Deadlock$2.run()`, locks `this.monitorB` in `class Deadlock`, locks `this.monitorA` in `class Deadlock`,[Trace 2] `void Deadlock.postDeadlockBad()`,Method call: `void Deadlock$1.run()`, locks `this.monitorA` in `class Deadlock`, locks `this.monitorB` in `class Deadlock`]
codetoanalyze/java/starvation-whole-program/Deadlock.java, Deadlock.postOnBGThreadBad():void, 73, DEADLOCK, no_bucket, ERROR, [[Trace 1] `void Deadlock.postOnBGThreadBad()`,Method call: `void Deadlock$5.run()`, locks `this.monitorE` in `class Deadlock`, locks `this.monitorF` in `class Deadlock`,[Trace 2] `void Deadlock.postOnBGThreadBad()`,Method call: `void Deadlock$6.run()`, locks `this.monitorF` in `class Deadlock`, locks `this.monitorE` in `class Deadlock`] codetoanalyze/java/starvation-whole-program/Deadlock.java, Deadlock.postOnBGThreadBad():void, 73, DEADLOCK, no_bucket, ERROR, [[Trace 1] `void Deadlock.postOnBGThreadBad()`,Method call: `void Deadlock$5.run()`, locks `this.monitorE` in `class Deadlock`, locks `this.monitorF` in `class Deadlock`,[Trace 2] `void Deadlock.postOnBGThreadBad()`,Method call: `void Deadlock$6.run()`, locks `this.monitorF` in `class Deadlock`, locks `this.monitorE` in `class Deadlock`]
codetoanalyze/java/starvation-whole-program/Deadlock.java, Deadlock.postOnBGThreadBad():void, 84, DEADLOCK, no_bucket, ERROR, [[Trace 1] `void Deadlock.postOnBGThreadBad()`,Method call: `void Deadlock$6.run()`, locks `this.monitorF` in `class Deadlock`, locks `this.monitorE` in `class Deadlock`,[Trace 2] `void Deadlock.postOnBGThreadBad()`,Method call: `void Deadlock$5.run()`, locks `this.monitorE` in `class Deadlock`, locks `this.monitorF` in `class Deadlock`] codetoanalyze/java/starvation-whole-program/Deadlock.java, Deadlock.postOnBGThreadBad():void, 84, DEADLOCK, no_bucket, ERROR, [[Trace 1] `void Deadlock.postOnBGThreadBad()`,Method call: `void Deadlock$6.run()`, locks `this.monitorF` in `class Deadlock`, locks `this.monitorE` in `class Deadlock`,[Trace 2] `void Deadlock.postOnBGThreadBad()`,Method call: `void Deadlock$5.run()`, locks `this.monitorE` in `class Deadlock`, locks `this.monitorF` in `class Deadlock`]
codetoanalyze/java/starvation-whole-program/DirectStarvation.java, DirectStarvation.postBlockingCallToUIThreadBad():void, 29, STARVATION, no_bucket, ERROR, [`void DirectStarvation.postBlockingCallToUIThreadBad()`,Method call: `void DirectStarvation$1.run()`,Method call: `void DirectStarvation.doTransact()`,calls `boolean Binder.transact(int,Parcel,Parcel,int)`] codetoanalyze/java/starvation-whole-program/DirectStarvation.java, DirectStarvation.postBlockingCallToUIThreadBad():void, 29, STARVATION, no_bucket, ERROR, [`void DirectStarvation.postBlockingCallToUIThreadBad()`,Method call: `void DirectStarvation$1.run()`,Method call: `void DirectStarvation.doTransact()`,calls `boolean Binder.transact(int,Parcel,Parcel,int)`]
codetoanalyze/java/starvation-whole-program/IndirectStarvation.java, IndirectStarvation.postBlockingCallToBackgroundThreadAndLockBad():void, 32, STARVATION, no_bucket, ERROR, [[Trace 1] `void IndirectStarvation.postBlockingCallToBackgroundThreadAndLockBad()`,Method call: `void IndirectStarvation$1.run()`, locks `this.monitorA` in `class IndirectStarvation`,[Trace 2] `void IndirectStarvation.postBlockingCallToBackgroundThreadAndLockBad()`,Method call: `void IndirectStarvation$2.run()`, locks `this.monitorA` in `class IndirectStarvation`,Method call: `void IndirectStarvation.doTransact()`,calls `boolean Binder.transact(int,Parcel,Parcel,int)`] codetoanalyze/java/starvation-whole-program/IndirectStarvation.java, IndirectStarvation.postBlockingCallToBackgroundThreadAndLockBad():void, 32, STARVATION, no_bucket, ERROR, [[Trace 1] `void IndirectStarvation.postBlockingCallToBackgroundThreadAndLockBad()`,Method call: `void IndirectStarvation$1.run()`, locks `this.monitorA` in `class IndirectStarvation`,[Trace 2] `void IndirectStarvation.postBlockingCallToBackgroundThreadAndLockBad()`,Method call: `void IndirectStarvation$2.run()`, locks `this.monitorA` in `class IndirectStarvation`,Method call: `void IndirectStarvation.doTransact()`,calls `boolean Binder.transact(int,Parcel,Parcel,int)`]
codetoanalyze/java/starvation-whole-program/ModeledExecutors.java, ModeledExecutors.postBlockingCallToUIThreadBad():void, 25, STARVATION, no_bucket, ERROR, [`void ModeledExecutors.postBlockingCallToUIThreadBad()`,Method call: `void ModeledExecutors$1.run()`,Method call: `void ModeledExecutors.doTransact()`,calls `boolean Binder.transact(int,Parcel,Parcel,int)`] codetoanalyze/java/starvation-whole-program/ModeledExecutors.java, ModeledExecutors.postBlockingCallToUIThreadBad():void, 24, STARVATION, no_bucket, ERROR, [`void ModeledExecutors.postBlockingCallToUIThreadBad()`,Method call: `void ModeledExecutors$1.run()`,Method call: `void ModeledExecutors.doTransact()`,calls `boolean Binder.transact(int,Parcel,Parcel,int)`]
codetoanalyze/java/starvation-whole-program/ModeledExecutors.java, ModeledExecutors.scheduleGuaranteedDelayedDeadlockBad():void, 94, DEADLOCK, no_bucket, ERROR, [[Trace 1] `void ModeledExecutors.scheduleGuaranteedDelayedDeadlockBad()`,Method call: `void ModeledExecutors$7.run()`, locks `this.monitorA` in `class ModeledExecutors`, locks `this.monitorB` in `class ModeledExecutors`,[Trace 2] `void ModeledExecutors.scheduleGuaranteedDelayedDeadlockBad()`,Method call: `void ModeledExecutors$8.run()`, locks `this.monitorB` in `class ModeledExecutors`, locks `this.monitorA` in `class ModeledExecutors`] codetoanalyze/java/starvation-whole-program/ModeledExecutors.java, ModeledExecutors.scheduleGuaranteedDelayedDeadlockBad():void, 93, DEADLOCK, no_bucket, ERROR, [[Trace 1] `void ModeledExecutors.scheduleGuaranteedDelayedDeadlockBad()`,Method call: `void ModeledExecutors$7.run()`, locks `this.monitorA` in `class ModeledExecutors`, locks `this.monitorB` in `class ModeledExecutors`,[Trace 2] `void ModeledExecutors.scheduleGuaranteedDelayedDeadlockBad()`,Method call: `void ModeledExecutors$8.run()`, locks `this.monitorB` in `class ModeledExecutors`, locks `this.monitorA` in `class ModeledExecutors`]
codetoanalyze/java/starvation-whole-program/ModeledExecutors.java, ModeledExecutors.scheduleGuaranteedDelayedDeadlockBad():void, 107, DEADLOCK, no_bucket, ERROR, [[Trace 1] `void ModeledExecutors.scheduleGuaranteedDelayedDeadlockBad()`,Method call: `void ModeledExecutors$8.run()`, locks `this.monitorB` in `class ModeledExecutors`, locks `this.monitorA` in `class ModeledExecutors`,[Trace 2] `void ModeledExecutors.scheduleGuaranteedDelayedDeadlockBad()`,Method call: `void ModeledExecutors$7.run()`, locks `this.monitorA` in `class ModeledExecutors`, locks `this.monitorB` in `class ModeledExecutors`] codetoanalyze/java/starvation-whole-program/ModeledExecutors.java, ModeledExecutors.scheduleGuaranteedDelayedDeadlockBad():void, 106, DEADLOCK, no_bucket, ERROR, [[Trace 1] `void ModeledExecutors.scheduleGuaranteedDelayedDeadlockBad()`,Method call: `void ModeledExecutors$8.run()`, locks `this.monitorB` in `class ModeledExecutors`, locks `this.monitorA` in `class ModeledExecutors`,[Trace 2] `void ModeledExecutors.scheduleGuaranteedDelayedDeadlockBad()`,Method call: `void ModeledExecutors$7.run()`, locks `this.monitorA` in `class ModeledExecutors`, locks `this.monitorB` in `class ModeledExecutors`]
codetoanalyze/java/starvation-whole-program/ModeledExecutors.java, ModeledExecutors.staticPostBlockingCallToUIThreadBad():void, 47, STARVATION, no_bucket, ERROR, [`void ModeledExecutors.staticPostBlockingCallToUIThreadBad()`,Method call: `void ModeledExecutors$3.run()`,Method call: `void ModeledExecutors.doTransact()`,calls `boolean Binder.transact(int,Parcel,Parcel,int)`] codetoanalyze/java/starvation-whole-program/ModeledExecutors.java, ModeledExecutors.staticPostBlockingCallToUIThreadBad():void, 46, STARVATION, no_bucket, ERROR, [`void ModeledExecutors.staticPostBlockingCallToUIThreadBad()`,Method call: `void ModeledExecutors$3.run()`,Method call: `void ModeledExecutors.doTransact()`,calls `boolean Binder.transact(int,Parcel,Parcel,int)`]
codetoanalyze/java/starvation-whole-program/ModeledExecutors.java, ModeledExecutors.staticPostDelayedBlockingCallToUIThreadBad():void, 69, STARVATION, no_bucket, ERROR, [`void ModeledExecutors.staticPostDelayedBlockingCallToUIThreadBad()`,Method call: `void ModeledExecutors$5.run()`,Method call: `void ModeledExecutors.doTransact()`,calls `boolean Binder.transact(int,Parcel,Parcel,int)`] codetoanalyze/java/starvation-whole-program/ModeledExecutors.java, ModeledExecutors.staticPostDelayedBlockingCallToUIThreadBad():void, 68, STARVATION, no_bucket, ERROR, [`void ModeledExecutors.staticPostDelayedBlockingCallToUIThreadBad()`,Method call: `void ModeledExecutors$5.run()`,Method call: `void ModeledExecutors.doTransact()`,calls `boolean Binder.transact(int,Parcel,Parcel,int)`]
codetoanalyze/java/starvation-whole-program/ModeledExecutors.java, ModeledExecutors.staticRunBlockingCallToUIThreadBad():void, 58, STARVATION, no_bucket, ERROR, [`void ModeledExecutors.staticRunBlockingCallToUIThreadBad()`,Method call: `void ModeledExecutors$4.run()`,Method call: `void ModeledExecutors.doTransact()`,calls `boolean Binder.transact(int,Parcel,Parcel,int)`] codetoanalyze/java/starvation-whole-program/ModeledExecutors.java, ModeledExecutors.staticRunBlockingCallToUIThreadBad():void, 57, STARVATION, no_bucket, ERROR, [`void ModeledExecutors.staticRunBlockingCallToUIThreadBad()`,Method call: `void ModeledExecutors$4.run()`,Method call: `void ModeledExecutors.doTransact()`,calls `boolean Binder.transact(int,Parcel,Parcel,int)`]
codetoanalyze/java/starvation-whole-program/MyActivity.java, MyActivity.onCreate(android.os.Bundle):void, 28, STARVATION, no_bucket, ERROR, [`void MyActivity.onCreate(Bundle)`,Method call: `void MyActivity.bad()`,calls `boolean Binder.transact(int,Parcel,Parcel,int)`] codetoanalyze/java/starvation-whole-program/MyActivity.java, MyActivity.onCreate(android.os.Bundle):void, 28, STARVATION, no_bucket, ERROR, [`void MyActivity.onCreate(Bundle)`,Method call: `void MyActivity.bad()`,calls `boolean Binder.transact(int,Parcel,Parcel,int)`]
codetoanalyze/java/starvation-whole-program/MyActivity.java, MyActivity.onDestroy():void, 68, DEADLOCK, no_bucket, ERROR, [[Trace 1] `void MyActivity.onDestroy()`, locks `this.monitorC` in `class MyActivity`, locks `this.monitorB` in `class MyActivity`,[Trace 2] `void MyActivity.onPause()`,Method call: `void MyActivity$2.run()`, locks `this.monitorB` in `class MyActivity`, locks `this.monitorC` in `class MyActivity`] codetoanalyze/java/starvation-whole-program/MyActivity.java, MyActivity.onDestroy():void, 68, DEADLOCK, no_bucket, ERROR, [[Trace 1] `void MyActivity.onDestroy()`, locks `this.monitorC` in `class MyActivity`, locks `this.monitorB` in `class MyActivity`,[Trace 2] `void MyActivity.onPause()`,Method call: `void MyActivity$2.run()`, locks `this.monitorB` in `class MyActivity`, locks `this.monitorC` in `class MyActivity`]
codetoanalyze/java/starvation-whole-program/MyActivity.java, MyActivity.onPause():void, 76, DEADLOCK, no_bucket, ERROR, [[Trace 1] `void MyActivity.onPause()`,Method call: `void MyActivity$2.run()`, locks `this.monitorB` in `class MyActivity`, locks `this.monitorC` in `class MyActivity`,[Trace 2] `void MyActivity.onDestroy()`, locks `this.monitorC` in `class MyActivity`, locks `this.monitorB` in `class MyActivity`] codetoanalyze/java/starvation-whole-program/MyActivity.java, MyActivity.onPause():void, 76, DEADLOCK, no_bucket, ERROR, [[Trace 1] `void MyActivity.onPause()`,Method call: `void MyActivity$2.run()`, locks `this.monitorB` in `class MyActivity`, locks `this.monitorC` in `class MyActivity`,[Trace 2] `void MyActivity.onDestroy()`, locks `this.monitorC` in `class MyActivity`, locks `this.monitorB` in `class MyActivity`]

Loading…
Cancel
Save