[starvation] Move to a cluster checker phase for reporting

Reviewed By: jeremydubreil

Differential Revision: D7757783

fbshipit-source-id: 341de51
master
Nikos Gorogiannis 7 years ago committed by Facebook Github Bot
parent 30d7239aff
commit e0a61c0a4a

@ -105,7 +105,9 @@ let all_checkers =
}
; { name= "Starvation analysis"
; active= Config.starvation
; callbacks= [(Procedure Starvation.analyze_procedure, Language.Java)] } ]
; callbacks=
[ (Procedure Starvation.analyze_procedure, Language.Java)
; (Cluster Starvation.reporting, Language.Java) ] } ]
let get_active_checkers () =

@ -100,27 +100,46 @@ end
module Analyzer = LowerHil.MakeAbstractInterpreter (ProcCfg.Normal) (TransferFunctions)
(* To allow on-demand reporting for deadlocks, we look for order pairs of the form (A,B)
where A belongs to the current class and B is potentially another class. To avoid
quadratic/double reporting (ie when we actually analyse B), we allow the check
only if the current class is ordered greater or equal to the callee class. *)
let should_skip_during_deadlock_reporting _ _ = false
(* currently short-circuited until non-determinism in reporting is dealt with *)
(* Typ.Name.compare current_class eventually_class < 0 *)
(* let false_if_none a ~f = Option.value_map a ~default:false ~f *)
(* if same class, report only if the locks order in one of the possible ways *)
let should_report_if_same_class _ = true
(* currently short-circuited until non-determinism in reporting is dealt with *)
(* StarvationDomain.(
LockOrder.get_pair caller_elem
|> false_if_none ~f:(fun (b, a) ->
let b_class_opt, a_class_opt = (LockEvent.owner_class b, LockEvent.owner_class a) in
false_if_none b_class_opt ~f:(fun first_class ->
false_if_none a_class_opt ~f:(fun eventually_class ->
not (Typ.Name.equal first_class eventually_class) || LockEvent.compare b a >= 0
) ) )) *)
let get_class_of_pname = function
| Typ.Procname.Java java_pname ->
Some (Typ.Procname.Java.get_class_type_name java_pname)
| _ ->
None
let analyze_procedure {Callbacks.proc_desc; tenv; summary} =
let pname = Procdesc.get_proc_name proc_desc in
let formals = FormalMap.make proc_desc in
let proc_data = ProcData.make proc_desc tenv formals in
let initial =
if not (Procdesc.is_java_synchronized proc_desc) then StarvationDomain.empty
else
let loc = Procdesc.get_loc proc_desc in
let lock =
if is_java_static pname then
(* this is crafted so as to match synchronized(CLASSNAME.class) constructs *)
get_class_of_pname pname
|> Option.map ~f:(fun tn -> Typ.Name.name tn |> Ident.string_to_name |> lock_of_class)
else FormalMap.get_formal_base 0 formals |> Option.map ~f:(fun base -> (base, []))
in
Option.value_map lock ~default:StarvationDomain.empty
~f:(StarvationDomain.acquire StarvationDomain.empty loc)
in
let initial =
if RacerDConfig.Models.runs_on_ui_thread proc_desc then
StarvationDomain.set_on_main_thread initial
else initial
in
Analyzer.compute_post proc_data ~initial
|> Option.value_map ~default:summary ~f:(fun lock_state ->
let lock_order = StarvationDomain.to_summary lock_state in
Summary.update_summary lock_order summary )
let get_summary caller_pdesc callee_pdesc =
Summary.read_summary caller_pdesc (Procdesc.get_proc_name callee_pdesc)
|> Option.map ~f:(fun summary -> (callee_pdesc, summary))
let make_trace_with_header ?(header= "") elem start_loc pname =
let trace = StarvationDomain.LockOrder.make_loc_trace elem in
@ -138,72 +157,67 @@ let make_loc_trace pname trace_id start_loc elem =
make_trace_with_header ~header elem start_loc pname
let get_summary caller_pdesc callee_pdesc =
Summary.read_summary caller_pdesc (Procdesc.get_proc_name callee_pdesc)
|> Option.map ~f:(fun summary -> (callee_pdesc, summary))
let get_class_of_pname = function
| Typ.Procname.Java java_pname ->
Some (Typ.Procname.Java.get_class_type_name java_pname)
| _ ->
None
let report_deadlocks get_proc_desc tenv pdesc (summary, _) =
(* Note about how many times we report a deadlock: normally twice, at each trace starting point.
Due to the fact we look for deadlocks in the summaries of the class at the root of a path,
this will fail when (a) the lock is of class type (ie as used in static sync methods), because
then the root is an identifier of type java.lang.Class and (b) when the lock belongs to an
inner class but this is no longer obvious in the path, because of nested-class path normalisation.
The net effect of the above issues is that we will only see these locks in conflicting pairs
once, as opposed to twice with all other deadlock pairs. *)
let report_deadlocks get_proc_desc tenv current_pdesc (summary, _) =
let open StarvationDomain in
let process_callee_elem caller_pdesc caller_elem callee_pdesc elem =
if LockOrder.may_deadlock caller_elem elem && should_report_if_same_class caller_elem then (
debug "Possible deadlock:@.%a@.%a@." LockOrder.pp caller_elem LockOrder.pp elem ;
let caller_loc = Procdesc.get_loc caller_pdesc in
let callee_loc = Procdesc.get_loc callee_pdesc in
let caller_pname = Procdesc.get_proc_name caller_pdesc in
let callee_pname = Procdesc.get_proc_name callee_pdesc in
match
( caller_elem.LockOrder.eventually.LockEvent.event
, elem.LockOrder.eventually.LockEvent.event )
with
| LockEvent.LockAcquire lock, LockEvent.LockAcquire lock' ->
let current_loc = Procdesc.get_loc current_pdesc in
let current_pname = Procdesc.get_proc_name current_pdesc in
let report_endpoint_elem current_elem endpoint_pname endpoint_loc elem =
if LockOrder.may_deadlock current_elem elem then
let () = debug "Possible deadlock:@.%a@.%a@." LockOrder.pp current_elem LockOrder.pp elem in
match (current_elem.LockOrder.eventually, elem.LockOrder.eventually) with
| {LockEvent.event= LockAcquire _}, {LockEvent.event= LockAcquire _} ->
let error_message =
Format.asprintf "Potential deadlock (%a ; %a)" LockIdentity.pp lock LockIdentity.pp
lock'
Format.asprintf
"Potential deadlock.@.Trace 1 (starts at %a), %a.@.Trace 2 (starts at %a), %a."
Typ.Procname.pp current_pname LockOrder.pp current_elem Typ.Procname.pp
endpoint_pname LockOrder.pp elem
in
let exn =
Exceptions.Checkers (IssueType.starvation, Localise.verbatim_desc error_message)
in
let first_trace = List.rev (make_loc_trace caller_pname 1 caller_loc caller_elem) in
let second_trace = make_loc_trace callee_pname 2 callee_loc elem in
let first_trace = List.rev (make_loc_trace current_pname 1 current_loc current_elem) in
let second_trace = make_loc_trace endpoint_pname 2 endpoint_loc elem in
let ltr = List.rev_append first_trace second_trace in
Specs.get_summary caller_pname
|> Option.iter ~f:(fun summary -> Reporting.log_error summary ~loc:caller_loc ~ltr exn)
Reporting.log_error_deprecated ~store_summary:true current_pname ~loc:current_loc ~ltr
exn
| _, _ ->
() )
()
in
let report_pair current_class elem =
LockOrder.get_pair elem
|> Option.iter ~f:(fun (_, eventually) ->
LockEvent.owner_class eventually
|> Option.iter ~f:(fun eventually_class ->
if should_skip_during_deadlock_reporting current_class eventually_class then ()
else
(* get the class of the root variable of the lock in the endpoint event
and retrieve all the summaries of the methods of that class *)
let class_of_eventual_lock =
LockEvent.owner_class eventually |> Option.bind ~f:(Tenv.lookup tenv)
in
let methods =
Option.value_map class_of_eventual_lock ~default:[] ~f:(fun tstruct ->
tstruct.Typ.Struct.methods )
in
let proc_descs = List.rev_filter_map methods ~f:get_proc_desc in
let summaries = List.rev_filter_map proc_descs ~f:(get_summary pdesc) in
(* for each summary related to the endpoint, analyse and report on its pairs *)
List.iter summaries ~f:(fun (callee_pdesc, (summary, _)) ->
LockOrderDomain.iter (process_callee_elem pdesc elem callee_pdesc) summary
) ) )
let report_on_current_elem elem =
match elem with
| {LockOrder.first= None} | {LockOrder.eventually= {LockEvent.event= LockEvent.MayBlock _}} ->
()
| {LockOrder.eventually= {LockEvent.event= LockEvent.LockAcquire endpoint_lock}} ->
match LockIdentity.owner_class endpoint_lock with
| None ->
()
| Some endpoint_class ->
(* get the class of the root variable of the lock in the endpoint event
and retrieve all the summaries of the methods of that class *)
let endpoint_tstruct = Tenv.lookup tenv endpoint_class in
let methods =
Option.value_map endpoint_tstruct ~default:[] ~f:(fun tstruct ->
tstruct.Typ.Struct.methods )
in
let endpoint_pdescs = List.rev_filter_map methods ~f:get_proc_desc in
let endpoint_summaries =
List.rev_filter_map endpoint_pdescs ~f:(get_summary current_pdesc)
in
(* for each summary related to the endpoint, analyse and report on its pairs *)
List.iter endpoint_summaries ~f:(fun (endpoint_pdesc, (summary, _)) ->
let endpoint_loc = Procdesc.get_loc endpoint_pdesc in
let endpoint_pname = Procdesc.get_proc_name endpoint_pdesc in
LockOrderDomain.iter (report_endpoint_elem elem endpoint_pname endpoint_loc) summary
)
in
Procdesc.get_proc_name pdesc |> get_class_of_pname
|> Option.iter ~f:(fun curr_class -> LockOrderDomain.iter (report_pair curr_class) summary)
LockOrderDomain.iter report_on_current_elem summary
let report_direct_blocks_on_main_thread proc_desc summary =
@ -211,8 +225,8 @@ let report_direct_blocks_on_main_thread proc_desc summary =
let report_pair ({LockOrder.eventually} as elem) =
match eventually with
| {LockEvent.event= LockEvent.MayBlock _} ->
let caller_loc = Procdesc.get_loc proc_desc in
let caller_pname = Procdesc.get_proc_name proc_desc in
let current_loc = Procdesc.get_loc proc_desc in
let current_pname = Procdesc.get_proc_name proc_desc in
let error_message =
Format.asprintf "UI-thread method may block; %a" LockEvent.pp_event
eventually.LockEvent.event
@ -220,46 +234,19 @@ let report_direct_blocks_on_main_thread proc_desc summary =
let exn =
Exceptions.Checkers (IssueType.starvation, Localise.verbatim_desc error_message)
in
let ltr = make_trace_with_header elem caller_loc caller_pname in
Specs.get_summary caller_pname
|> Option.iter ~f:(fun summary -> Reporting.log_error summary ~loc:caller_loc ~ltr exn)
let ltr = make_trace_with_header elem current_loc current_pname in
Reporting.log_error_deprecated ~store_summary:true current_pname ~loc:current_loc ~ltr exn
| _ ->
()
in
LockOrderDomain.iter report_pair summary
let analyze_procedure {Callbacks.proc_desc; get_proc_desc; tenv; summary} =
let pname = Procdesc.get_proc_name proc_desc in
let formals = FormalMap.make proc_desc in
let proc_data = ProcData.make proc_desc tenv formals in
let initial =
if not (Procdesc.is_java_synchronized proc_desc) then StarvationDomain.empty
else
let loc = Procdesc.get_loc proc_desc in
let lock =
if is_java_static pname then
(* this is crafted so as to match synchronized(CLASSNAME.class) constructs *)
get_class_of_pname pname
|> Option.map ~f:(fun tn -> Typ.Name.name tn |> Ident.string_to_name |> lock_of_class)
else FormalMap.get_formal_base 0 formals |> Option.map ~f:(fun base -> (base, []))
in
Option.value_map lock ~default:StarvationDomain.empty
~f:(StarvationDomain.acquire StarvationDomain.empty loc)
in
let initial =
if RacerDConfig.Models.runs_on_ui_thread proc_desc then
StarvationDomain.set_on_main_thread initial
else initial
let reporting {Callbacks.procedures; get_proc_desc} =
let report_procedure (tenv, proc_desc) =
Summary.read_summary proc_desc (Procdesc.get_proc_name proc_desc)
|> Option.iter ~f:(fun ((s, main) as summary) ->
report_deadlocks get_proc_desc tenv proc_desc summary ;
if main then report_direct_blocks_on_main_thread proc_desc s )
in
match Analyzer.compute_post proc_data ~initial with
| None ->
summary
| Some lock_state ->
let lock_order = StarvationDomain.to_summary lock_state in
let updated_summary = Summary.update_summary lock_order summary in
Option.iter updated_summary.Specs.payload.starvation
~f:(report_deadlocks get_proc_desc tenv proc_desc) ;
Option.iter updated_summary.Specs.payload.starvation ~f:(fun (sum, main) ->
if main then report_direct_blocks_on_main_thread proc_desc sum ) ;
updated_summary
List.iter procedures ~f:report_procedure

@ -10,3 +10,5 @@
open! IStd
val analyze_procedure : Callbacks.proc_callback_t
val reporting : Callbacks.cluster_callback_t

@ -41,7 +41,10 @@ module LockIdentity = struct
let pp fmt (((_, typ), _) as lock) =
F.fprintf fmt "Locks %a in class %a" AccessPath.pp lock (Typ.pp_full Pp.text) typ
F.fprintf fmt "locks `%a` in class `%a`" AccessPath.pp lock (Typ.pp_full Pp.text) typ
let owner_class ((_, typ), _) = Typ.inner_name typ
end
module LockEvent = struct
@ -92,15 +95,6 @@ module LockEvent = struct
F.fprintf fmt "%a at %a%a" pp_event e.event Location.pp e.loc pp_trace e.trace
let owner_class e =
match e.event with
| LockAcquire lock ->
let (_, typ), _ = lock in
Typ.inner_name typ
| _ ->
None
let make_acquire lock loc = {event= LockAcquire lock; loc; trace= []}
let make_blocks msg loc = {event= MayBlock msg; loc; trace= []}
@ -137,8 +131,6 @@ module LockOrder = struct
o.eventually
let get_pair elem = match elem.first with None -> None | Some b -> Some (b, elem.eventually)
let may_deadlock elem elem' =
match (elem.first, elem'.first) with
| Some b, Some b' ->

@ -12,7 +12,12 @@ module F = Format
(** Abstraction of a path that represents a lock, special-casing equality and comparisons
to work over type, base variable modulo this and access list *)
module LockIdentity : PrettyPrintable.PrintableOrderedType with type t = AccessPath.t
module LockIdentity : 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 *)
end
(** A lock event. Equality/comparison disregards the call trace but includes location. *)
module LockEvent : sig
@ -23,9 +28,6 @@ module LockEvent : sig
type t = private {event: event_t; loc: Location.t; trace: CallSite.t list}
include PrettyPrintable.PrintableOrderedType with type t := t
val owner_class : t -> Typ.name option
(** Class of the root variable of the path representing the lock *)
end
module LockState : AbstractDomain.WithBottom
@ -40,9 +42,6 @@ module LockOrder : sig
include PrettyPrintable.PrintableOrderedType with type t := t
val get_pair : t -> (LockEvent.t * LockEvent.t) option
(** return the pair (b, eventually) if first is Some b *)
val may_deadlock : t -> t -> bool
(** check if two pairs are symmetric in terms of locks, where locks are compared modulo the
variable name at the root of each path. *)

@ -3,12 +3,17 @@ codetoanalyze/java/starvation/Binders.java, void Binders.interBad(), 0, STARVATI
codetoanalyze/java/starvation/Binders.java, void Binders.intraBad(), 0, STARVATION, ERROR, [Method start: void Binders.intraBad(),Method call: void Binders.doTransact(),calls boolean Binder.transact(int,Parcel,Parcel,int) from void Binders.doTransact()]
codetoanalyze/java/starvation/Countdwn.java, void Countdwn.awaitOnMainByAnnotBad(), 0, STARVATION, ERROR, [Method start: void Countdwn.awaitOnMainByAnnotBad(),calls void CountDownLatch.await() from void Countdwn.awaitOnMainByAnnotBad()]
codetoanalyze/java/starvation/Countdwn.java, void Countdwn.awaitOnMainByCallBad(), 0, STARVATION, ERROR, [Method start: void Countdwn.awaitOnMainByCallBad(),calls void CountDownLatch.await() from void Countdwn.awaitOnMainByCallBad()]
codetoanalyze/java/starvation/InnerClass.java, void InnerClass$InnerClassA.innerOuterBad(), 0, STARVATION, ERROR, [[Trace 1] Locks this in class InnerClass$InnerClassA*,Method call: void InnerClass.bar(),Locks this in class InnerClass*,[Trace 2] Locks this in class InnerClass*,Method call: void InnerClass$InnerClassA.baz(),Locks this in class InnerClass$InnerClassA*]
codetoanalyze/java/starvation/InnerClass.java, void InnerClass.outerInnerBad(InnerClass$InnerClassA), 0, STARVATION, ERROR, [[Trace 1] Locks this in class InnerClass*,Method call: void InnerClass$InnerClassA.baz(),Locks this in class InnerClass$InnerClassA*,[Trace 2] Method start: InnerClass$InnerClassA.<init>(InnerClass,Object),Locks this in class InnerClass$InnerClassA*,Method call: void InnerClass.bar(),Locks this in class InnerClass*]
codetoanalyze/java/starvation/Interclass.java, void InterclassA.interclass2Bad(Interclass), 0, STARVATION, ERROR, [[Trace 1] Locks this in class InterclassA*,Method call: void Interclass.interclass2Bad(),Locks this in class Interclass*,[Trace 2] Locks this in class Interclass*,Method call: void InterclassA.interclass1Bad(),Locks this in class InterclassA*]
codetoanalyze/java/starvation/Interproc.java, void Interproc.interproc1Bad(InterprocA), 0, STARVATION, ERROR, [[Trace 1] Locks this in class Interproc*,Method call: void Interproc.interproc2Bad(InterprocA),Locks b in class InterprocA*,[Trace 2] Locks this in class InterprocA*,Method call: void InterprocA.interproc2Bad(Interproc),Locks d in class Interproc*]
codetoanalyze/java/starvation/Intraproc.java, void IntraprocA.intraBad(Intraproc), 0, STARVATION, ERROR, [[Trace 1] Method start: void IntraprocA.intraBad(Intraproc),Locks this in class IntraprocA*,Locks o in class Intraproc*,[Trace 2] Method start: void Intraproc.intraBad(IntraprocA),Locks this in class Intraproc*,Locks o in class IntraprocA*]
codetoanalyze/java/starvation/InnerClass.java, InnerClass$InnerClassA.<init>(InnerClass,Object), 0, STARVATION, ERROR, [[Trace 1] Method start: InnerClass$InnerClassA.<init>(InnerClass,Object),locks `this` in class `InnerClass$InnerClassA*`,Method call: void InnerClass.bar(),locks `this` in class `InnerClass*`,[Trace 2] locks `this` in class `InnerClass*`,Method call: void InnerClass$InnerClassA.baz(),locks `this` in class `InnerClass$InnerClassA*`]
codetoanalyze/java/starvation/InnerClass.java, void InnerClass$InnerClassA.innerOuterBad(), 0, STARVATION, ERROR, [[Trace 1] locks `this` in class `InnerClass$InnerClassA*`,Method call: void InnerClass.bar(),locks `this` in class `InnerClass*`,[Trace 2] locks `this` in class `InnerClass*`,Method call: void InnerClass$InnerClassA.baz(),locks `this` in class `InnerClass$InnerClassA*`]
codetoanalyze/java/starvation/InnerClass.java, void InnerClass.outerInnerBad(InnerClass$InnerClassA), 0, STARVATION, ERROR, [[Trace 1] locks `this` in class `InnerClass*`,Method call: void InnerClass$InnerClassA.baz(),locks `this` in class `InnerClass$InnerClassA*`,[Trace 2] locks `this` in class `InnerClass$InnerClassA*`,Method call: void InnerClass.bar(),locks `this` in class `InnerClass*`]
codetoanalyze/java/starvation/InnerClass.java, void InnerClass.outerInnerBad(InnerClass$InnerClassA), 0, STARVATION, ERROR, [[Trace 1] locks `this` in class `InnerClass*`,Method call: void InnerClass$InnerClassA.baz(),locks `this` in class `InnerClass$InnerClassA*`,[Trace 2] Method start: InnerClass$InnerClassA.<init>(InnerClass,Object),locks `this` in class `InnerClass$InnerClassA*`,Method call: void InnerClass.bar(),locks `this` in class `InnerClass*`]
codetoanalyze/java/starvation/Interclass.java, void Interclass.interclass1Bad(InterclassA), 0, STARVATION, ERROR, [[Trace 1] locks `this` in class `Interclass*`,Method call: void InterclassA.interclass1Bad(),locks `this` in class `InterclassA*`,[Trace 2] locks `this` in class `InterclassA*`,Method call: void Interclass.interclass2Bad(),locks `this` in class `Interclass*`]
codetoanalyze/java/starvation/Interclass.java, void InterclassA.interclass2Bad(Interclass), 0, STARVATION, ERROR, [[Trace 1] locks `this` in class `InterclassA*`,Method call: void Interclass.interclass2Bad(),locks `this` in class `Interclass*`,[Trace 2] locks `this` in class `Interclass*`,Method call: void InterclassA.interclass1Bad(),locks `this` in class `InterclassA*`]
codetoanalyze/java/starvation/Interproc.java, void Interproc.interproc1Bad(InterprocA), 0, STARVATION, ERROR, [[Trace 1] locks `this` in class `Interproc*`,Method call: void Interproc.interproc2Bad(InterprocA),locks `b` in class `InterprocA*`,[Trace 2] locks `this` in class `InterprocA*`,Method call: void InterprocA.interproc2Bad(Interproc),locks `d` in class `Interproc*`]
codetoanalyze/java/starvation/Interproc.java, void InterprocA.interproc1Bad(Interproc), 0, STARVATION, ERROR, [[Trace 1] locks `this` in class `InterprocA*`,Method call: void InterprocA.interproc2Bad(Interproc),locks `d` in class `Interproc*`,[Trace 2] locks `this` in class `Interproc*`,Method call: void Interproc.interproc2Bad(InterprocA),locks `b` in class `InterprocA*`]
codetoanalyze/java/starvation/Intraproc.java, void Intraproc.intraBad(IntraprocA), 0, STARVATION, ERROR, [[Trace 1] Method start: void Intraproc.intraBad(IntraprocA),locks `this` in class `Intraproc*`,locks `o` in class `IntraprocA*`,[Trace 2] Method start: void IntraprocA.intraBad(Intraproc),locks `this` in class `IntraprocA*`,locks `o` in class `Intraproc*`]
codetoanalyze/java/starvation/Intraproc.java, void IntraprocA.intraBad(Intraproc), 0, STARVATION, ERROR, [[Trace 1] Method start: void IntraprocA.intraBad(Intraproc),locks `this` in class `IntraprocA*`,locks `o` in class `Intraproc*`,[Trace 2] Method start: void Intraproc.intraBad(IntraprocA),locks `this` in class `Intraproc*`,locks `o` in class `IntraprocA*`]
codetoanalyze/java/starvation/JavaIO.java, void JavaIO.fileReadBad(), 0, STARVATION, ERROR, [Method start: void JavaIO.fileReadBad(),Method call: int JavaIO.doFileRead(),calls int InputStreamReader.read() from int JavaIO.doFileRead()]
codetoanalyze/java/starvation/JavaIO.java, void JavaIO.streamReadBad(), 0, STARVATION, ERROR, [Method start: void JavaIO.streamReadBad(),Method call: String JavaIO.doStreamRead(),calls String DataInputStream.readUTF() from String JavaIO.doStreamRead()]
codetoanalyze/java/starvation/StaticLock.java, void StaticLock.lockOtherClassOneWayBad(), 0, STARVATION, ERROR, [[Trace 1] Method start: void StaticLock.lockOtherClassOneWayBad(),Locks StaticLock$0 in class java.lang.Class*,Locks this in class StaticLock*,[Trace 2] Locks this in class StaticLock*,Method call: void StaticLock.staticSynced(),Locks StaticLock$0 in class java.lang.Class*]
codetoanalyze/java/starvation/StaticLock.java, void StaticLock.lockOtherClassOneWayBad(), 0, STARVATION, ERROR, [[Trace 1] Method start: void StaticLock.lockOtherClassOneWayBad(),locks `StaticLock$0` in class `java.lang.Class*`,locks `this` in class `StaticLock*`,[Trace 2] locks `this` in class `StaticLock*`,Method call: void StaticLock.staticSynced(),locks `StaticLock$0` in class `java.lang.Class*`]
codetoanalyze/java/starvation/VisDispFrame.java, void VisDispFrame.callsGetVisibleDisplayFrameOnUiThreadBad(), 0, STARVATION, ERROR, [Method start: void VisDispFrame.callsGetVisibleDisplayFrameOnUiThreadBad(),calls void View.getWindowVisibleDisplayFrame(Rect) from void VisDispFrame.callsGetVisibleDisplayFrameOnUiThreadBad()]

Loading…
Cancel
Save