diff --git a/infer/src/concurrency/RacerDConfig.ml b/infer/src/concurrency/RacerDConfig.ml index f924f0de6..2a126cce9 100644 --- a/infer/src/concurrency/RacerDConfig.ml +++ b/infer/src/concurrency/RacerDConfig.ml @@ -700,17 +700,20 @@ module Models = struct ["android.os.AsyncTask"] "get" + (* at most one function is allowed to be true *) let may_block = + let open StarvationDomain.LockEvent in let matchers = - [ is_blocking_java_io - ; is_countdownlatch_await - ; is_two_way_binder_transact - ; is_getWindowVisibleDisplayFrame - ; is_future_get - ; is_accountManager_setUserData - ; is_asyncTask_get ] + [ (is_blocking_java_io, Low) + ; (is_countdownlatch_await, Medium) + ; (is_two_way_binder_transact, High) + ; (is_getWindowVisibleDisplayFrame, Low) + ; (is_future_get, High) + ; (is_accountManager_setUserData, High) + ; (is_asyncTask_get, High) ] in - fun tenv pn actuals -> List.exists matchers ~f:(fun matcher -> matcher tenv pn actuals) + fun tenv pn actuals -> + List.find_map matchers ~f:(fun (matcher, sev) -> Option.some_if (matcher tenv pn actuals) sev) let is_synchronized_library_call = diff --git a/infer/src/concurrency/RacerDConfig.mli b/infer/src/concurrency/RacerDConfig.mli index 21a765b49..109d63f98 100644 --- a/infer/src/concurrency/RacerDConfig.mli +++ b/infer/src/concurrency/RacerDConfig.mli @@ -81,7 +81,8 @@ module Models : sig (** return true if procedure is at an abstraction boundary or reporting has been explicitly requested via @ThreadSafe *) - val may_block : Tenv.t -> Typ.Procname.t -> HilExp.t list -> bool + val may_block : + Tenv.t -> Typ.Procname.t -> HilExp.t list -> StarvationDomain.LockEvent.severity_t option (** is the method call potentially blocking, given the actuals passed? *) val is_synchronized_library_call : Tenv.t -> Typ.Procname.t -> bool diff --git a/infer/src/concurrency/starvation.ml b/infer/src/concurrency/starvation.ml index 37b683295..4bf2c560b 100644 --- a/infer/src/concurrency/starvation.ml +++ b/infer/src/concurrency/starvation.ml @@ -78,15 +78,17 @@ module TransferFunctions (CFG : ProcCfg.S) = struct | NoEffect when Models.is_synchronized_library_call tenv callee -> (* model a synchronized call without visible internal behaviour *) do_lock actuals loc astate |> do_unlock actuals - | NoEffect when Models.may_block tenv callee actuals -> - let caller = Procdesc.get_proc_name pdesc in - Domain.blocking_call ~caller ~callee loc astate | NoEffect when is_on_ui_thread callee -> - let explanation = F.asprintf "calls %a" (MF.wrap_monospaced Typ.Procname.pp) callee in + let explanation = F.asprintf "it calls %a" (MF.wrap_monospaced Typ.Procname.pp) callee in Domain.set_on_ui_thread astate explanation - | _ -> - Payload.read pdesc callee - |> Option.value_map ~default:astate ~f:(Domain.integrate_summary astate callee loc) ) + | NoEffect -> + match Models.may_block tenv callee actuals with + | Some sev -> + let caller = Procdesc.get_proc_name pdesc in + Domain.blocking_call ~caller ~callee sev loc astate + | None -> + Payload.read pdesc callee + |> Option.value_map ~default:astate ~f:(Domain.integrate_summary astate callee loc) ) | _ -> astate @@ -159,9 +161,64 @@ let get_summaries_of_methods_in_class tenv clazz = (Summary.get_proc_name sum, p) ) ) -let log_issue current_pname current_loc ltr exn = - Reporting.log_issue_external current_pname Exceptions.Kerror ~loc:current_loc ~ltr exn +(** per-procedure report map, which takes care of deduplication *) +module ReportMap = struct + type issue_t = Starvation of StarvationDomain.LockEvent.severity_t | Deadlock + [@@deriving compare] + + type report_t = + { issue: issue_t + ; pname: Typ.Procname.t [@compare.ignore] + ; ltr: Errlog.loc_trace [@compare.ignore] + ; message: string [@compare.ignore] } + [@@deriving compare] + + module LocMap = PrettyPrintable.MakePPMap (Location) + + let empty : report_t list LocMap.t = LocMap.empty + + let add issue pname loc ltr message map = + let rep = {issue; pname; ltr; message} in + let preexisting = try LocMap.find loc map with Caml.Not_found -> [] in + LocMap.add loc (rep :: preexisting) map + + let add_deadlock pname loc ltr exn map = add Deadlock pname loc ltr exn map + + let add_starvation sev pname loc ltr exn map = add (Starvation sev) pname loc ltr exn map + + let log map = + let log_report loc {issue; pname; ltr; message} = + let issue_type = + match issue with Deadlock -> IssueType.deadlock | Starvation _ -> IssueType.starvation + in + let exn = Exceptions.Checkers (issue_type, Localise.verbatim_desc message) in + Reporting.log_issue_external pname Exceptions.Kerror ~loc ~ltr exn + in + let mk_deduped_report num_of_reports ({message} as report) = + { report with + message= + Printf.sprintf "%s %d more starvation report(s) on the same line suppressed." message + (num_of_reports - 1) } + in + let log_location loc reports = + let deadlocks, starvations = + List.partition_tf ~f:(function {issue= Deadlock} -> true | _ -> false) reports + in + (* report all deadlocks *) + List.iter ~f:(log_report loc) deadlocks ; + match starvations with + | [] -> + () + | [report] -> + log_report loc report + | _ -> + List.max_elt ~compare:compare_report_t starvations + |> Option.iter ~f:(fun rep -> + mk_deduped_report (List.length starvations) rep |> log_report loc ) + in + LocMap.iter log_location map +end let should_report_deadlock_on_current_proc current_elem endpoint_elem = let open StarvationDomain in @@ -200,15 +257,16 @@ let should_report_deadlock_on_current_proc current_elem endpoint_elem = 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 tenv current_pdesc (summary, current_main) = +let report_deadlocks tenv current_pdesc (summary, current_main) report_map' = let open StarvationDomain in - 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 elem = + let report_endpoint_elem current_elem endpoint_pname elem report_map = if - LockOrder.may_deadlock current_elem elem - && should_report_deadlock_on_current_proc current_elem elem - then + not + ( LockOrder.may_deadlock current_elem elem + && should_report_deadlock_on_current_proc current_elem elem ) + then report_map + else 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 _} -> @@ -220,104 +278,99 @@ let report_deadlocks tenv current_pdesc (summary, current_main) = (MF.wrap_monospaced Typ.Procname.pp) endpoint_pname LockOrder.pp elem in - let exn = - Exceptions.Checkers (IssueType.deadlock, Localise.verbatim_desc error_message) - in let first_trace = List.rev (make_loc_trace current_pname 1 current_elem) in let second_trace = make_loc_trace endpoint_pname 2 elem in let ltr = List.rev_append first_trace second_trace in - log_issue current_pname current_loc ltr exn + let loc = LockOrder.get_loc current_elem in + ReportMap.add_deadlock current_pname loc ltr error_message report_map | _, _ -> - () + report_map in - let report_on_current_elem elem = + let report_on_current_elem elem report_map = match elem with | {LockOrder.first= None} | {LockOrder.eventually= {LockEvent.event= LockEvent.MayBlock _}} -> - () + report_map | {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 + LockIdentity.owner_class endpoint_lock + |> Option.value_map ~default:report_map ~f:(fun 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_summaries = get_summaries_of_methods_in_class tenv endpoint_class in - (* for each summary related to the endpoint, analyse and report on its pairs *) - List.iter endpoint_summaries ~f:(fun (endpoint_pname, (summary, endpoint_main)) -> - if UIThreadDomain.is_empty current_main || UIThreadDomain.is_empty endpoint_main then - LockOrderDomain.iter (report_endpoint_elem elem endpoint_pname) summary ) + let endpoint_summaries = get_summaries_of_methods_in_class tenv endpoint_class in + (* for each summary related to the endpoint, analyse and report on its pairs *) + List.fold endpoint_summaries ~init:report_map ~f: + (fun acc (endp_pname, (endp_summary, endp_ui)) -> + if UIThreadDomain.is_empty current_main || UIThreadDomain.is_empty endp_ui then + LockOrderDomain.fold (report_endpoint_elem elem endp_pname) endp_summary acc + else acc ) ) in - LockOrderDomain.iter report_on_current_elem summary + LockOrderDomain.fold report_on_current_elem summary report_map' -let report_blocks_on_main_thread tenv current_pdesc order ui_explain = +let report_blocks_on_main_thread tenv current_pdesc (order, ui) report_map' = let open StarvationDomain in - let current_loc = Procdesc.get_loc current_pdesc in let current_pname = Procdesc.get_proc_name current_pdesc in - let report_remote_block current_elem current_lock endpoint_pname endpoint_elem = + let report_remote_block ui_explain current_elem current_lock endpoint_pname endpoint_elem + report_map = match endpoint_elem with | { LockOrder.first= Some {LockEvent.event= LockEvent.LockAcquire lock} - ; eventually= {LockEvent.event= LockEvent.MayBlock block_descr} } + ; eventually= {LockEvent.event= LockEvent.MayBlock (block_descr, sev)} } when LockIdentity.equal current_lock lock -> let error_message = Format.asprintf "Method %a runs on UI thread (because %s) and %a, which may be held by another thread \ - which %s" + which %s." (MF.wrap_monospaced Typ.Procname.pp) current_pname ui_explain LockIdentity.pp lock block_descr in - let exn = - Exceptions.Checkers (IssueType.starvation, Localise.verbatim_desc error_message) - in let first_trace = List.rev (make_loc_trace current_pname 1 current_elem) in let second_trace = make_loc_trace endpoint_pname 2 endpoint_elem in let ltr = List.rev_append first_trace second_trace in - log_issue current_pname current_loc ltr exn + let loc = LockOrder.get_loc current_elem in + ReportMap.add_starvation sev current_pname loc ltr error_message report_map | _ -> - () + report_map in - let report_on_current_elem ({LockOrder.eventually} as elem) = + let report_on_current_elem ui_explain ({LockOrder.eventually} as elem) report_map = match eventually with - | {LockEvent.event= LockEvent.MayBlock _} -> + | {LockEvent.event= LockEvent.MayBlock (_, sev)} -> let error_message = - Format.asprintf "Method %a runs on UI thread (because %s), and may block; %a" + Format.asprintf "Method %a runs on UI thread (because %s), and may block; %a." (MF.wrap_monospaced Typ.Procname.pp) current_pname ui_explain LockEvent.pp_event eventually.LockEvent.event in - let exn = - Exceptions.Checkers (IssueType.starvation, Localise.verbatim_desc error_message) - in + let loc = LockOrder.get_loc elem in let ltr = make_trace_with_header elem current_pname in - log_issue current_pname current_loc ltr exn + ReportMap.add_starvation sev current_pname loc ltr error_message report_map | {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 + LockIdentity.owner_class endpoint_lock + |> Option.value_map ~default:report_map ~f:(fun 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_summaries = get_summaries_of_methods_in_class tenv endpoint_class in - (* for each summary related to the endpoint, analyse and report on its pairs *) - List.iter endpoint_summaries ~f:(fun (endpoint_pname, (order, ui)) -> - (* skip methods known to run on ui thread, as they cannot run in parallel to us *) - if UIThreadDomain.is_empty ui then - LockOrderDomain.iter (report_remote_block elem endpoint_lock endpoint_pname) order - ) + let endpoint_summaries = get_summaries_of_methods_in_class tenv endpoint_class in + (* for each summary related to the endpoint, analyse and report on its pairs *) + List.fold endpoint_summaries ~init:report_map ~f: + (fun acc (endpoint_pname, (order, ui)) -> + (* skip methods known to run on ui thread, as they cannot run in parallel to us *) + if UIThreadDomain.is_empty ui then + LockOrderDomain.fold + (report_remote_block ui_explain elem endpoint_lock endpoint_pname) + order acc + else acc ) ) in - LockOrderDomain.iter report_on_current_elem order + match ui with + | AbstractDomain.Types.Bottom -> + report_map' + | AbstractDomain.Types.NonBottom ui_explain -> + LockOrderDomain.fold (report_on_current_elem ui_explain) order report_map' let reporting {Callbacks.procedures; exe_env} = let report_procedure (tenv, proc_desc) = die_if_not_java proc_desc ; Payload.read proc_desc (Procdesc.get_proc_name proc_desc) - |> Option.iter ~f:(fun ((order, ui) as summary) -> - report_deadlocks tenv proc_desc summary ; - match ui with - | AbstractDomain.Types.Bottom -> - () - | AbstractDomain.Types.NonBottom on_ui -> - report_blocks_on_main_thread tenv proc_desc order on_ui ) + |> Option.iter ~f:(fun summary -> + report_deadlocks tenv proc_desc summary ReportMap.empty + |> report_blocks_on_main_thread tenv proc_desc summary |> ReportMap.log ) in List.iter procedures ~f:report_procedure ; let sourcefile = exe_env.Exe_env.source_file in diff --git a/infer/src/concurrency/starvationDomain.ml b/infer/src/concurrency/starvationDomain.ml index f0815449f..a72ef51a0 100644 --- a/infer/src/concurrency/starvationDomain.ml +++ b/infer/src/concurrency/starvationDomain.ml @@ -53,12 +53,15 @@ module LockIdentity = struct end module LockEvent = struct - type event_t = LockAcquire of LockIdentity.t | MayBlock of string [@@deriving compare] + type severity_t = Low | Medium | High [@@deriving compare] + + type event_t = LockAcquire of LockIdentity.t | MayBlock of (string * severity_t) + [@@deriving compare] let pp_event fmt = function | LockAcquire lock -> LockIdentity.pp fmt lock - | MayBlock msg -> + | MayBlock (msg, _) -> F.pp_print_string fmt msg @@ -102,9 +105,9 @@ module LockEvent = struct let make_acquire lock loc = {event= LockAcquire lock; loc; trace= []} - let make_blocks msg loc = {event= MayBlock msg; loc; trace= []} + let make_blocks msg sev loc = {event= MayBlock (msg, sev); loc; trace= []} - let make_blocking_call ~caller ~callee loc = + let make_blocking_call ~caller ~callee sev loc = let descr = F.asprintf "calls %a from %a" (MF.wrap_monospaced Typ.Procname.pp) @@ -112,8 +115,10 @@ module LockEvent = struct (MF.wrap_monospaced Typ.Procname.pp) caller in - make_blocks descr loc + make_blocks descr sev loc + + let get_loc {loc; trace} = List.hd trace |> Option.value_map ~default:loc ~f:CallSite.loc let make_loc_trace ?(reverse= false) e = let call_trace, nesting = @@ -165,6 +170,10 @@ module LockOrder = struct eventually= {o.eventually with LockEvent.trace= callsite :: o.eventually.LockEvent.trace} } + let get_loc {first; eventually} = + match first with Some event -> LockEvent.get_loc event | None -> LockEvent.get_loc eventually + + let make_loc_trace o = let first_trace = Option.value_map o.first ~default:[] ~f:(LockEvent.make_loc_trace ~reverse:true) @@ -265,8 +274,8 @@ let acquire ((ls, lo), main) loc lockid = ((ls', lo'), main) -let blocking_call ~caller ~callee loc ((ls, lo), main) = - let newlock_event = LockEvent.make_blocking_call ~caller ~callee loc in +let blocking_call ~caller ~callee sev loc ((ls, lo), main) = + let newlock_event = LockEvent.make_blocking_call ~caller ~callee sev loc in let lo' = add_order_pairs ls newlock_event lo in ((ls, lo'), main) diff --git a/infer/src/concurrency/starvationDomain.mli b/infer/src/concurrency/starvationDomain.mli index 4baa91523..f92923cdb 100644 --- a/infer/src/concurrency/starvationDomain.mli +++ b/infer/src/concurrency/starvationDomain.mli @@ -23,7 +23,10 @@ end (** A lock event. Equality/comparison disregards the call trace but includes location. *) module LockEvent : sig - type event_t = private LockAcquire of LockIdentity.t | MayBlock of string + type severity_t = Low | Medium | High [@@deriving compare] + + type event_t = LockAcquire of LockIdentity.t | MayBlock of (string * severity_t) + [@@deriving compare] val pp_event : F.formatter -> event_t -> unit @@ -47,6 +50,8 @@ module LockOrder : sig variable name at the root of each path. *) val make_loc_trace : t -> Errlog.loc_trace + + val get_loc : t -> Location.t end module LockOrderDomain : sig @@ -65,7 +70,8 @@ val acquire : astate -> Location.t -> LockIdentity.t -> astate val release : astate -> LockIdentity.t -> astate val blocking_call : - caller:Typ.Procname.t -> callee:Typ.Procname.t -> Location.t -> astate -> astate + caller:Typ.Procname.t -> callee:Typ.Procname.t -> LockEvent.severity_t -> Location.t -> astate + -> astate val set_on_ui_thread : astate -> string -> astate (** set the property "runs on UI thread" to true by attaching the given explanation string as to diff --git a/infer/tests/codetoanalyze/java/starvation/Dedup.java b/infer/tests/codetoanalyze/java/starvation/Dedup.java new file mode 100644 index 000000000..8e3152b9d --- /dev/null +++ b/infer/tests/codetoanalyze/java/starvation/Dedup.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018 - present Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +import java.io.FileReader; +import java.util.concurrent.CountDownLatch; +import android.support.annotation.UiThread; +import java.io.IOException; + +class Dedup { + FileReader reader; + CountDownLatch latch; + + // only one report should be seen + @UiThread + void onUiThreadBad() throws InterruptedException, IOException { + callMethodWithMultipleBlocksBad(); + } + + // three reports are expected + @UiThread + void callMethodWithMultipleBlocksBad() throws InterruptedException, IOException { + reader.read(); + latch.await(); + reader.read(); + } +} diff --git a/infer/tests/codetoanalyze/java/starvation/issues.exp b/infer/tests/codetoanalyze/java/starvation/issues.exp index cd6db3418..0dbaee1dc 100644 --- a/infer/tests/codetoanalyze/java/starvation/issues.exp +++ b/infer/tests/codetoanalyze/java/starvation/issues.exp @@ -1,29 +1,33 @@ -codetoanalyze/java/starvation/AccMgr.java, void AccMgr.lockOnUiThreadBad(), 26, STARVATION, ERROR, [[Trace 1] `void AccMgr.lockOnUiThreadBad()`,locks `this.AccMgr.lock` in class `AccMgr*`,[Trace 2] `void AccMgr.setUserDataUnderLock()`,locks `this.AccMgr.lock` in class `AccMgr*`,calls `void AccountManager.setUserData(Account,String,String)` from `void AccMgr.setUserDataUnderLock()`] -codetoanalyze/java/starvation/AccMgr.java, void AccMgr.onUiThreadBad(), 21, STARVATION, ERROR, [ `void AccMgr.onUiThreadBad()`,calls `void AccountManager.setUserData(Account,String,String)` from `void AccMgr.onUiThreadBad()`] -codetoanalyze/java/starvation/AsyncTaskGet.java, void AsyncTaskGet.lockOnUiThreadBad(), 32, STARVATION, ERROR, [[Trace 1] `void AsyncTaskGet.lockOnUiThreadBad()`,locks `this.AsyncTaskGet.lock` in class `AsyncTaskGet*`,[Trace 2] `void AsyncTaskGet.taskGetUnderLock()`,locks `this.AsyncTaskGet.lock` in class `AsyncTaskGet*`,calls `Object AsyncTask.get()` from `void AsyncTaskGet.taskGetUnderLock()`] -codetoanalyze/java/starvation/AsyncTaskGet.java, void AsyncTaskGet.taskGetOnUiThreadBad(), 21, STARVATION, ERROR, [ `void AsyncTaskGet.taskGetOnUiThreadBad()`,calls `Object AsyncTask.get()` from `void AsyncTaskGet.taskGetOnUiThreadBad()`] -codetoanalyze/java/starvation/Binders.java, void Binders.annotationBad(), 36, STARVATION, ERROR, [ `void Binders.annotationBad()`,Method call: `void Binders.doTransact()`,calls `boolean Binder.transact(int,Parcel,Parcel,int)` from `void Binders.doTransact()`] -codetoanalyze/java/starvation/Binders.java, void Binders.interBad(), 25, STARVATION, ERROR, [ `void Binders.interBad()`,calls `boolean Binder.transact(int,Parcel,Parcel,int)` from `void Binders.interBad()`] -codetoanalyze/java/starvation/Binders.java, void Binders.intraBad(), 30, STARVATION, ERROR, [ `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(), 22, STARVATION, ERROR, [ `void Countdwn.awaitOnMainByAnnotBad()`,calls `void CountDownLatch.await()` from `void Countdwn.awaitOnMainByAnnotBad()`] -codetoanalyze/java/starvation/Countdwn.java, void Countdwn.awaitOnMainByCallBad(), 16, STARVATION, ERROR, [ `void Countdwn.awaitOnMainByCallBad()`,calls `void CountDownLatch.await()` from `void Countdwn.awaitOnMainByCallBad()`] -codetoanalyze/java/starvation/FutureGet.java, void FutureGet.getDirectBad(), 21, STARVATION, ERROR, [ `void FutureGet.getDirectBad()`,calls `Object Future.get()` from `void FutureGet.getDirectBad()`] -codetoanalyze/java/starvation/FutureGet.java, void FutureGet.getIndirectBad(), 26, STARVATION, ERROR, [[Trace 1] `void FutureGet.getIndirectBad()`,locks `this.FutureGet.lock` in class `FutureGet*`,[Trace 2] `void FutureGet.getUnderLock()`,locks `this.FutureGet.lock` in class `FutureGet*`,calls `Object Future.get()` from `void FutureGet.getUnderLock()`] -codetoanalyze/java/starvation/FutureGet.java, void FutureGet.getTimeout50000001MicroSecondsBad(), 76, STARVATION, ERROR, [ `void FutureGet.getTimeout50000001MicroSecondsBad()`,calls `Object Future.get(long,TimeUnit)` from `void FutureGet.getTimeout50000001MicroSecondsBad()`] -codetoanalyze/java/starvation/FutureGet.java, void FutureGet.getTimeoutOneDayBad(), 41, STARVATION, ERROR, [ `void FutureGet.getTimeoutOneDayBad()`,calls `Object Future.get(long,TimeUnit)` from `void FutureGet.getTimeoutOneDayBad()`] -codetoanalyze/java/starvation/FutureGet.java, void FutureGet.getTimeoutOneHourBad(), 55, STARVATION, ERROR, [ `void FutureGet.getTimeoutOneHourBad()`,calls `Object Future.get(long,TimeUnit)` from `void FutureGet.getTimeoutOneHourBad()`] -codetoanalyze/java/starvation/IndirectBlock.java, void IndirectBlock.takeExpensiveLockOnUiThreadBad(), 23, STARVATION, ERROR, [[Trace 1] `void IndirectBlock.takeExpensiveLockOnUiThreadBad()`,locks `this.IndirectBlock.expensiveLock` in class `IndirectBlock*`,[Trace 2] `void IndirectBlock.doTransactUnderLock()`,locks `this.IndirectBlock.expensiveLock` in class `IndirectBlock*`,calls `boolean Binder.transact(int,Parcel,Parcel,int)` from `void IndirectBlock.doTransactUnderLock()`] -codetoanalyze/java/starvation/IndirectBlock.java, void IndirectBlock.takeRemoteExpensiveLockOnUiThreadBad(IndirectInterproc), 34, STARVATION, ERROR, [[Trace 1] `void IndirectBlock.takeRemoteExpensiveLockOnUiThreadBad(IndirectInterproc)`,Method call: `void IndirectInterproc.takeLock()`,locks `this` in class `IndirectInterproc*`,[Trace 2] `void IndirectInterproc.doTransactUnderLock(Binder)`,locks `this` in class `IndirectInterproc*`,calls `boolean Binder.transact(int,Parcel,Parcel,int)` from `void IndirectInterproc.doTransactUnderLock(Binder)`] -codetoanalyze/java/starvation/InnerClass.java, InnerClass$InnerClassA.(InnerClass,Object), 49, DEADLOCK, ERROR, [[Trace 1] `InnerClass$InnerClassA.(InnerClass,Object)`,locks `this` in class `InnerClass$InnerClassA*`,Method call: `void InnerClass.bar()`,locks `this` in class `InnerClass*`,[Trace 2] `void InnerClass.outerInnerBad(InnerClass$InnerClassA)`,locks `this` in class `InnerClass*`,Method call: `void InnerClass$InnerClassA.baz()`,locks `this` in class `InnerClass$InnerClassA*`] +codetoanalyze/java/starvation/AccMgr.java, void AccMgr.lockOnUiThreadBad(), 27, STARVATION, ERROR, [[Trace 1] `void AccMgr.lockOnUiThreadBad()`,locks `this.AccMgr.lock` in class `AccMgr*`,[Trace 2] `void AccMgr.setUserDataUnderLock()`,locks `this.AccMgr.lock` in class `AccMgr*`,calls `void AccountManager.setUserData(Account,String,String)` from `void AccMgr.setUserDataUnderLock()`] +codetoanalyze/java/starvation/AccMgr.java, void AccMgr.onUiThreadBad(), 22, STARVATION, ERROR, [ `void AccMgr.onUiThreadBad()`,calls `void AccountManager.setUserData(Account,String,String)` from `void AccMgr.onUiThreadBad()`] +codetoanalyze/java/starvation/AsyncTaskGet.java, void AsyncTaskGet.lockOnUiThreadBad(), 33, STARVATION, ERROR, [[Trace 1] `void AsyncTaskGet.lockOnUiThreadBad()`,locks `this.AsyncTaskGet.lock` in class `AsyncTaskGet*`,[Trace 2] `void AsyncTaskGet.taskGetUnderLock()`,locks `this.AsyncTaskGet.lock` in class `AsyncTaskGet*`,calls `Object AsyncTask.get()` from `void AsyncTaskGet.taskGetUnderLock()`] +codetoanalyze/java/starvation/AsyncTaskGet.java, void AsyncTaskGet.taskGetOnUiThreadBad(), 22, STARVATION, ERROR, [ `void AsyncTaskGet.taskGetOnUiThreadBad()`,calls `Object AsyncTask.get()` from `void AsyncTaskGet.taskGetOnUiThreadBad()`] +codetoanalyze/java/starvation/Binders.java, void Binders.annotationBad(), 37, STARVATION, ERROR, [ `void Binders.annotationBad()`,Method call: `void Binders.doTransact()`,calls `boolean Binder.transact(int,Parcel,Parcel,int)` from `void Binders.doTransact()`] +codetoanalyze/java/starvation/Binders.java, void Binders.interBad(), 26, STARVATION, ERROR, [ `void Binders.interBad()`,calls `boolean Binder.transact(int,Parcel,Parcel,int)` from `void Binders.interBad()`] +codetoanalyze/java/starvation/Binders.java, void Binders.intraBad(), 32, STARVATION, ERROR, [ `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(), 23, STARVATION, ERROR, [ `void Countdwn.awaitOnMainByAnnotBad()`,calls `void CountDownLatch.await()` from `void Countdwn.awaitOnMainByAnnotBad()`] +codetoanalyze/java/starvation/Countdwn.java, void Countdwn.awaitOnMainByCallBad(), 18, STARVATION, ERROR, [ `void Countdwn.awaitOnMainByCallBad()`,calls `void CountDownLatch.await()` from `void Countdwn.awaitOnMainByCallBad()`] +codetoanalyze/java/starvation/Dedup.java, void Dedup.callMethodWithMultipleBlocksBad(), 28, STARVATION, ERROR, [ `void Dedup.callMethodWithMultipleBlocksBad()`,calls `int InputStreamReader.read()` from `void Dedup.callMethodWithMultipleBlocksBad()`] +codetoanalyze/java/starvation/Dedup.java, void Dedup.callMethodWithMultipleBlocksBad(), 29, STARVATION, ERROR, [ `void Dedup.callMethodWithMultipleBlocksBad()`,calls `void CountDownLatch.await()` from `void Dedup.callMethodWithMultipleBlocksBad()`] +codetoanalyze/java/starvation/Dedup.java, void Dedup.callMethodWithMultipleBlocksBad(), 30, STARVATION, ERROR, [ `void Dedup.callMethodWithMultipleBlocksBad()`,calls `int InputStreamReader.read()` from `void Dedup.callMethodWithMultipleBlocksBad()`] +codetoanalyze/java/starvation/Dedup.java, void Dedup.onUiThreadBad(), 22, STARVATION, ERROR, [ `void Dedup.onUiThreadBad()`,Method call: `void Dedup.callMethodWithMultipleBlocksBad()`,calls `void CountDownLatch.await()` from `void Dedup.callMethodWithMultipleBlocksBad()`] +codetoanalyze/java/starvation/FutureGet.java, void FutureGet.getDirectBad(), 22, STARVATION, ERROR, [ `void FutureGet.getDirectBad()`,calls `Object Future.get()` from `void FutureGet.getDirectBad()`] +codetoanalyze/java/starvation/FutureGet.java, void FutureGet.getIndirectBad(), 27, STARVATION, ERROR, [[Trace 1] `void FutureGet.getIndirectBad()`,locks `this.FutureGet.lock` in class `FutureGet*`,[Trace 2] `void FutureGet.getUnderLock()`,locks `this.FutureGet.lock` in class `FutureGet*`,calls `Object Future.get()` from `void FutureGet.getUnderLock()`] +codetoanalyze/java/starvation/FutureGet.java, void FutureGet.getTimeout50000001MicroSecondsBad(), 78, STARVATION, ERROR, [ `void FutureGet.getTimeout50000001MicroSecondsBad()`,calls `Object Future.get(long,TimeUnit)` from `void FutureGet.getTimeout50000001MicroSecondsBad()`] +codetoanalyze/java/starvation/FutureGet.java, void FutureGet.getTimeoutOneDayBad(), 43, STARVATION, ERROR, [ `void FutureGet.getTimeoutOneDayBad()`,calls `Object Future.get(long,TimeUnit)` from `void FutureGet.getTimeoutOneDayBad()`] +codetoanalyze/java/starvation/FutureGet.java, void FutureGet.getTimeoutOneHourBad(), 57, STARVATION, ERROR, [ `void FutureGet.getTimeoutOneHourBad()`,calls `Object Future.get(long,TimeUnit)` from `void FutureGet.getTimeoutOneHourBad()`] +codetoanalyze/java/starvation/IndirectBlock.java, void IndirectBlock.takeExpensiveLockOnUiThreadBad(), 24, STARVATION, ERROR, [[Trace 1] `void IndirectBlock.takeExpensiveLockOnUiThreadBad()`,locks `this.IndirectBlock.expensiveLock` in class `IndirectBlock*`,[Trace 2] `void IndirectBlock.doTransactUnderLock()`,locks `this.IndirectBlock.expensiveLock` in class `IndirectBlock*`,calls `boolean Binder.transact(int,Parcel,Parcel,int)` from `void IndirectBlock.doTransactUnderLock()`] +codetoanalyze/java/starvation/IndirectBlock.java, void IndirectBlock.takeRemoteExpensiveLockOnUiThreadBad(IndirectInterproc), 35, STARVATION, ERROR, [[Trace 1] `void IndirectBlock.takeRemoteExpensiveLockOnUiThreadBad(IndirectInterproc)`,Method call: `void IndirectInterproc.takeLock()`,locks `this` in class `IndirectInterproc*`,[Trace 2] `void IndirectInterproc.doTransactUnderLock(Binder)`,locks `this` in class `IndirectInterproc*`,calls `boolean Binder.transact(int,Parcel,Parcel,int)` from `void IndirectInterproc.doTransactUnderLock(Binder)`] +codetoanalyze/java/starvation/InnerClass.java, InnerClass$InnerClassA.(InnerClass,Object), 50, DEADLOCK, ERROR, [[Trace 1] `InnerClass$InnerClassA.(InnerClass,Object)`,locks `this` in class `InnerClass$InnerClassA*`,Method call: `void InnerClass.bar()`,locks `this` in class `InnerClass*`,[Trace 2] `void InnerClass.outerInnerBad(InnerClass$InnerClassA)`,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(), 36, DEADLOCK, ERROR, [[Trace 1] `void InnerClass$InnerClassA.innerOuterBad()`,locks `this` in class `InnerClass$InnerClassA*`,Method call: `void InnerClass.bar()`,locks `this` in class `InnerClass*`,[Trace 2] `void InnerClass.outerInnerBad(InnerClass$InnerClassA)`,locks `this` in class `InnerClass*`,Method call: `void InnerClass$InnerClassA.baz()`,locks `this` in class `InnerClass$InnerClassA*`] codetoanalyze/java/starvation/Interclass.java, void Interclass.interclass1Bad(InterclassA), 12, DEADLOCK, ERROR, [[Trace 1] `void Interclass.interclass1Bad(InterclassA)`,locks `this` in class `Interclass*`,Method call: `void InterclassA.interclass1Bad()`,locks `this` in class `InterclassA*`,[Trace 2] `void InterclassA.interclass2Bad(Interclass)`,locks `this` in class `InterclassA*`,Method call: `void Interclass.interclass2Bad()`,locks `this` in class `Interclass*`] codetoanalyze/java/starvation/Interproc.java, void Interproc.interproc1Bad(InterprocA), 11, DEADLOCK, ERROR, [[Trace 1] `void Interproc.interproc1Bad(InterprocA)`,locks `this` in class `Interproc*`,Method call: `void Interproc.interproc2Bad(InterprocA)`,locks `b` in class `InterprocA*`,[Trace 2] `void InterprocA.interproc1Bad(Interproc)`,locks `this` in class `InterprocA*`,Method call: `void InterprocA.interproc2Bad(Interproc)`,locks `d` in class `Interproc*`] -codetoanalyze/java/starvation/Intraproc.java, void Intraproc.intraBad(IntraprocA), 11, DEADLOCK, ERROR, [[Trace 1] `void Intraproc.intraBad(IntraprocA)`,locks `this` in class `Intraproc*`,locks `o` in class `IntraprocA*`,[Trace 2] `void IntraprocA.intraBad(Intraproc)`,locks `this` in class `IntraprocA*`,locks `o` in class `Intraproc*`] -codetoanalyze/java/starvation/JavaIO.java, void JavaIO.fileReadBad(), 32, STARVATION, ERROR, [ `void JavaIO.fileReadBad()`,Method call: `int JavaIO.doFileRead()`,calls `int InputStreamReader.read()` from `int JavaIO.doFileRead()`] -codetoanalyze/java/starvation/JavaIO.java, void JavaIO.streamReadBad(), 37, STARVATION, ERROR, [ `void JavaIO.streamReadBad()`,Method call: `String JavaIO.doStreamRead()`,calls `String DataInputStream.readUTF()` from `String JavaIO.doStreamRead()`] -codetoanalyze/java/starvation/LegacySync.java, Object LegacySync.onUiThreadOpBad(), 26, STARVATION, ERROR, [[Trace 1] `Object LegacySync.onUiThreadOpBad()`,locks `this.LegacySync.table` in class `LegacySync*`,[Trace 2] `void LegacySync.notOnUiThreadSyncedBad()`,locks `this.LegacySync.table` in class `LegacySync*`,calls `Object Future.get()` from `void LegacySync.notOnUiThreadSyncedBad()`] -codetoanalyze/java/starvation/ServiceOnUIThread.java, IBinder ServiceOnUIThread.onBind(Intent), 20, STARVATION, ERROR, [ `IBinder ServiceOnUIThread.onBind(Intent)`,Method call: `void ServiceOnUIThread.transactBad()`,calls `boolean IBinder.transact(int,Parcel,Parcel,int)` from `void ServiceOnUIThread.transactBad()`] -codetoanalyze/java/starvation/ServiceOnUIThread.java, void ServiceOnUIThread.transactBad(), 25, STARVATION, ERROR, [ `void ServiceOnUIThread.transactBad()`,calls `boolean IBinder.transact(int,Parcel,Parcel,int)` from `void ServiceOnUIThread.transactBad()`] -codetoanalyze/java/starvation/StaticLock.java, void StaticLock.lockOtherClassOneWayBad(), 24, DEADLOCK, ERROR, [[Trace 1] `void StaticLock.lockOtherClassOneWayBad()`,locks `StaticLock$0` in class `java.lang.Class*`,locks `this` in class `StaticLock*`,[Trace 2] `void StaticLock.lockOtherClassAnotherWayNad()`,locks `this` in class `StaticLock*`,Method call: `void StaticLock.staticSynced()`,locks `StaticLock$0` in class `java.lang.Class*`] +codetoanalyze/java/starvation/Intraproc.java, void Intraproc.intraBad(IntraprocA), 12, DEADLOCK, ERROR, [[Trace 1] `void Intraproc.intraBad(IntraprocA)`,locks `this` in class `Intraproc*`,locks `o` in class `IntraprocA*`,[Trace 2] `void IntraprocA.intraBad(Intraproc)`,locks `this` in class `IntraprocA*`,locks `o` in class `Intraproc*`] +codetoanalyze/java/starvation/JavaIO.java, void JavaIO.fileReadBad(), 33, STARVATION, ERROR, [ `void JavaIO.fileReadBad()`,Method call: `int JavaIO.doFileRead()`,calls `int InputStreamReader.read()` from `int JavaIO.doFileRead()`] +codetoanalyze/java/starvation/JavaIO.java, void JavaIO.streamReadBad(), 38, STARVATION, ERROR, [ `void JavaIO.streamReadBad()`,Method call: `String JavaIO.doStreamRead()`,calls `String DataInputStream.readUTF()` from `String JavaIO.doStreamRead()`] +codetoanalyze/java/starvation/LegacySync.java, Object LegacySync.onUiThreadOpBad(), 27, STARVATION, ERROR, [[Trace 1] `Object LegacySync.onUiThreadOpBad()`,locks `this.LegacySync.table` in class `LegacySync*`,[Trace 2] `void LegacySync.notOnUiThreadSyncedBad()`,locks `this.LegacySync.table` in class `LegacySync*`,calls `Object Future.get()` from `void LegacySync.notOnUiThreadSyncedBad()`] +codetoanalyze/java/starvation/ServiceOnUIThread.java, IBinder ServiceOnUIThread.onBind(Intent), 21, STARVATION, ERROR, [ `IBinder ServiceOnUIThread.onBind(Intent)`,Method call: `void ServiceOnUIThread.transactBad()`,calls `boolean IBinder.transact(int,Parcel,Parcel,int)` from `void ServiceOnUIThread.transactBad()`] +codetoanalyze/java/starvation/ServiceOnUIThread.java, void ServiceOnUIThread.transactBad(), 27, STARVATION, ERROR, [ `void ServiceOnUIThread.transactBad()`,calls `boolean IBinder.transact(int,Parcel,Parcel,int)` from `void ServiceOnUIThread.transactBad()`] +codetoanalyze/java/starvation/StaticLock.java, void StaticLock.lockOtherClassOneWayBad(), 25, DEADLOCK, ERROR, [[Trace 1] `void StaticLock.lockOtherClassOneWayBad()`,locks `StaticLock$0` in class `java.lang.Class*`,locks `this` in class `StaticLock*`,[Trace 2] `void StaticLock.lockOtherClassAnotherWayNad()`,locks `this` in class `StaticLock*`,Method call: `void StaticLock.staticSynced()`,locks `StaticLock$0` in class `java.lang.Class*`] codetoanalyze/java/starvation/UIDeadlock.java, void UIDeadlock.onUIThreadBad(), 28, DEADLOCK, ERROR, [[Trace 1] `void UIDeadlock.onUIThreadBad()`,locks `this` in class `UIDeadlock*`,locks `this.UIDeadlock.lockB` in class `UIDeadlock*`,[Trace 2] `void UIDeadlock.notOnUIThreadBad()`,locks `this.UIDeadlock.lockB` in class `UIDeadlock*`,locks `this` in class `UIDeadlock*`] -codetoanalyze/java/starvation/VisDispFrame.java, void VisDispFrame.callsGetVisibleDisplayFrameOnUiThreadBad(), 19, STARVATION, ERROR, [ `void VisDispFrame.callsGetVisibleDisplayFrameOnUiThreadBad()`,calls `void View.getWindowVisibleDisplayFrame(Rect)` from `void VisDispFrame.callsGetVisibleDisplayFrameOnUiThreadBad()`] +codetoanalyze/java/starvation/VisDispFrame.java, void VisDispFrame.callsGetVisibleDisplayFrameOnUiThreadBad(), 20, STARVATION, ERROR, [ `void VisDispFrame.callsGetVisibleDisplayFrameOnUiThreadBad()`,calls `void View.getWindowVisibleDisplayFrame(Rect)` from `void VisDispFrame.callsGetVisibleDisplayFrameOnUiThreadBad()`]