[starvation] add static timeout recognition and 5 second android limit

Reviewed By: jvillard

Differential Revision: D7953454

fbshipit-source-id: 76a72f0
master
Nikos Gorogiannis 7 years ago committed by Facebook Github Bot
parent f22bb53aa8
commit e3f0754335

@ -586,6 +586,38 @@ module Models = struct
false false
(** magical value from https://developer.android.com/topic/performance/vitals/anr *)
let android_anr_time_limit = 5.0
(** can this timeout cause an anr? *)
let is_excessive_timeout =
let time_units =
String.Map.of_alist_exn
[ ("NANOSECONDS", 0.000_000_001)
; ("MICROSECONDS", 0.000_001)
; ("MILLISECONDS", 0.001)
; ("SECONDS", 1.0)
; ("MINUTES", 60.0)
; ("HOURS", 3_600.0)
; ("DAYS", 86_400.0) ]
in
fun duration_exp timeunit_exp ->
match (duration_exp, timeunit_exp) with
| HilExp.Constant (Const.Cint duration_lit), HilExp.AccessExpression timeunit_acc_exp -> (
match AccessExpression.to_access_path timeunit_acc_exp with
| _, [AccessPath.FieldAccess field]
when String.equal "java.util.concurrent.TimeUnit" (Typ.Fieldname.Java.get_class field) ->
let fieldname = Typ.Fieldname.Java.get_field field in
let duration = float_of_int (IntLit.to_int duration_lit) in
String.Map.find time_units fieldname
|> Option.value_map ~default:false ~f:(fun unit_in_secs ->
unit_in_secs *. duration >. android_anr_time_limit )
| _ ->
false )
| _ ->
false
(** It's surprisingly difficult making any sense of the official documentation on java.io classes (** It's surprisingly difficult making any sense of the official documentation on java.io classes
as to whether methods may block. We approximate these by calls in traditionally blocking as to whether methods may block. We approximate these by calls in traditionally blocking
classes (non-channel based I/O), to methods `(Reader|InputStream).read*`. classes (non-channel based I/O), to methods `(Reader|InputStream).read*`.
@ -596,9 +628,19 @@ module Models = struct
"read" "read"
let actuals_are_empty_or_timeout = function
| [_] ->
true
| [_; duration; timeunit] ->
is_excessive_timeout duration timeunit
| _ ->
false
(** is the method called CountDownLath.await or on subclass? *) (** is the method called CountDownLath.await or on subclass? *)
let is_countdownlatch_await = let is_countdownlatch_await =
is_call_of_class_or_superclass ["java.util.concurrent.CountDownLatch"] "await" is_call_of_class_or_superclass ~actuals_pred:actuals_are_empty_or_timeout
["java.util.concurrent.CountDownLatch"] "await"
(** an IBinder.transact call is an RPC. If the 4th argument (5th counting `this` as the first) (** an IBinder.transact call is an RPC. If the 4th argument (5th counting `this` as the first)
@ -618,15 +660,18 @@ module Models = struct
(** is it a call to Future.get() or on sublass? *) (** is it a call to Future.get() or on sublass? *)
let is_future_get = is_call_of_class_or_superclass ["java.util.concurrent.Future"] "get" let is_future_get =
is_call_of_class_or_superclass ~actuals_pred:actuals_are_empty_or_timeout
["java.util.concurrent.Future"] "get"
let is_accountManager_setUserData = let is_accountManager_setUserData =
is_call_of_class_or_superclass ["android.accounts.AccountManager"] "setUserData" is_call_of_class_or_superclass ["android.accounts.AccountManager"] "setUserData"
let is_asyncTask_get_without_timeout = let is_asyncTask_get =
let actuals_pred actuals = Int.equal 1 (List.length actuals) in is_call_of_class_or_superclass ~actuals_pred:actuals_are_empty_or_timeout
is_call_of_class_or_superclass ~actuals_pred ["android.os.AsyncTask"] "get" ["android.os.AsyncTask"] "get"
let may_block = let may_block =
@ -637,7 +682,7 @@ module Models = struct
; is_getWindowVisibleDisplayFrame ; is_getWindowVisibleDisplayFrame
; is_future_get ; is_future_get
; is_accountManager_setUserData ; is_accountManager_setUserData
; is_asyncTask_get_without_timeout ] ; is_asyncTask_get ]
in in
fun tenv pn actuals -> List.exists matchers ~f:(fun matcher -> matcher tenv pn actuals) fun tenv pn actuals -> List.exists matchers ~f:(fun matcher -> matcher tenv pn actuals)
end end

@ -9,7 +9,9 @@
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import java.util.concurrent.TimeUnit;
class FutureGet { class FutureGet {
Future future; Future future;
@ -34,4 +36,46 @@ class FutureGet {
void getOnOtherThreadOk() throws InterruptedException, ExecutionException { void getOnOtherThreadOk() throws InterruptedException, ExecutionException {
future.get(); future.get();
} }
@UiThread
void getTimeoutOneDayBad() throws InterruptedException, ExecutionException {
try {
future.get(1L, TimeUnit.DAYS);
} catch (TimeoutException e) {}
}
@UiThread
void getTimeoutOneSecondOk() throws InterruptedException, ExecutionException {
try {
future.get(1L, TimeUnit.SECONDS);
} catch (TimeoutException e) {}
}
@UiThread
void getTimeoutOneHourBad() throws InterruptedException, ExecutionException {
try {
future.get(1L, TimeUnit.HOURS);
} catch (TimeoutException e) {}
}
@UiThread
void getTimeoutFourSecondsOk() throws InterruptedException, ExecutionException {
try {
future.get(4L, TimeUnit.SECONDS);
} catch (TimeoutException e) {}
}
@UiThread
void getTimeout4999MilliSecondsOk() throws InterruptedException, ExecutionException {
try {
future.get(4999L, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {}
}
@UiThread
void getTimeout50000001MicroSecondsBad() throws InterruptedException, ExecutionException {
try {
future.get(5000001L, TimeUnit.MICROSECONDS);
} catch (TimeoutException e) {}
}
} }

@ -7,8 +7,11 @@ codetoanalyze/java/starvation/Binders.java, void Binders.interBad(), 25, STARVAT
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/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.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/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(), 19, STARVATION, ERROR, [ void FutureGet.getDirectBad(),calls Object Future.get() from void FutureGet.getDirectBad()] 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(), 24, 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.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.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/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.<init>(InnerClass,Object), 49, DEADLOCK, ERROR, [[Trace 1] InnerClass$InnerClassA.<init>(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, InnerClass$InnerClassA.<init>(InnerClass,Object), 49, DEADLOCK, ERROR, [[Trace 1] InnerClass$InnerClassA.<init>(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*`]

Loading…
Cancel
Save