From 3870ebb747f5f6ce7a28a6079833f55daca06572 Mon Sep 17 00:00:00 2001 From: Nikos Gorogiannis Date: Wed, 1 Aug 2018 07:45:00 -0700 Subject: [PATCH] [starvation] catch calls to Thread.sleep Summary: Treat calls to Thread.sleep as blocking, even when the timeouts are less than the ANR limit. Reviewed By: da319 Differential Revision: D9027950 fbshipit-source-id: 001409896 --- infer/src/concurrency/RacerDConfig.ml | 4 +++ .../java/starvation/ThreadSleep.java | 34 +++++++++++++++++++ .../codetoanalyze/java/starvation/issues.exp | 2 ++ 3 files changed, 40 insertions(+) create mode 100644 infer/tests/codetoanalyze/java/starvation/ThreadSleep.java diff --git a/infer/src/concurrency/RacerDConfig.ml b/infer/src/concurrency/RacerDConfig.ml index 9c242b2f7..aa2c921f7 100644 --- a/infer/src/concurrency/RacerDConfig.ml +++ b/infer/src/concurrency/RacerDConfig.ml @@ -735,6 +735,9 @@ module Models = struct |> Staged.unstage + (* consider any call to sleep as bad, even with timeouts lower than the anr limit *) + let is_thread_sleep = is_call_of_class ["java.lang.Thread"] "sleep" |> Staged.unstage + (* at most one function is allowed to be true, sort from High to Low *) let may_block = let open StarvationDomain.Event in @@ -742,6 +745,7 @@ module Models = struct [ (is_accountManager_setUserData, High) ; (is_two_way_binder_transact, High) ; (is_countdownlatch_await, High) + ; (is_thread_sleep, High) ; (is_object_wait, High) ; (is_getWindowVisibleDisplayFrame, Medium) ; (is_asyncTask_get, Low) diff --git a/infer/tests/codetoanalyze/java/starvation/ThreadSleep.java b/infer/tests/codetoanalyze/java/starvation/ThreadSleep.java new file mode 100644 index 000000000..45c0665e8 --- /dev/null +++ b/infer/tests/codetoanalyze/java/starvation/ThreadSleep.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import android.support.annotation.UiThread; +import java.lang.Thread; + +class ThreadSleep { + void sleepOnAnyThreadOk() throws InterruptedException { + Thread.sleep(60); + } + + @UiThread + void sleepOnUIThreadBad() throws InterruptedException { + Thread.sleep(60); + } + + Object lock; + + @UiThread + void indirectSleepOnUIThreadBad() { + synchronized(lock) {} + } + + void lockAndSleepOnNonUIThread() throws InterruptedException { + synchronized(lock) { + sleepOnAnyThreadOk(); + } + } + +} diff --git a/infer/tests/codetoanalyze/java/starvation/issues.exp b/infer/tests/codetoanalyze/java/starvation/issues.exp index 98ef38991..4b65ae931 100644 --- a/infer/tests/codetoanalyze/java/starvation/issues.exp +++ b/infer/tests/codetoanalyze/java/starvation/issues.exp @@ -38,5 +38,7 @@ codetoanalyze/java/starvation/ServiceOnUIThread.java, IBinder ServiceOnUIThread. codetoanalyze/java/starvation/ServiceOnUIThread.java, void ServiceOnUIThread.transactBad(), 25, STARVATION, no_bucket, ERROR, [`void ServiceOnUIThread.transactBad()`,calls `boolean IBinder.transact(int,Parcel,Parcel,int)` from `void ServiceOnUIThread.transactBad()`] codetoanalyze/java/starvation/StaticLock.java, void StaticLock.lockOtherClassOneWayBad(), 23, DEADLOCK, no_bucket, 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/SuppLint.java, void SuppLint.onUiThreadBad(), 26, STARVATION, no_bucket, ERROR, [`void SuppLint.onUiThreadBad()`,calls `Object Future.get()` from `void SuppLint.onUiThreadBad()`] +codetoanalyze/java/starvation/ThreadSleep.java, void ThreadSleep.indirectSleepOnUIThreadBad(), 25, STARVATION, no_bucket, ERROR, [[Trace 1] `void ThreadSleep.indirectSleepOnUIThreadBad()`,locks `this.ThreadSleep.lock` in class `ThreadSleep*`,[Trace 2] `void ThreadSleep.lockAndSleepOnNonUIThread()`,locks `this.ThreadSleep.lock` in class `ThreadSleep*`,Method call: `void ThreadSleep.sleepOnAnyThreadOk()`,calls `void Thread.sleep(long)` from `void ThreadSleep.sleepOnAnyThreadOk()`] +codetoanalyze/java/starvation/ThreadSleep.java, void ThreadSleep.sleepOnUIThreadBad(), 18, STARVATION, no_bucket, ERROR, [`void ThreadSleep.sleepOnUIThreadBad()`,calls `void Thread.sleep(long)` from `void ThreadSleep.sleepOnUIThreadBad()`] codetoanalyze/java/starvation/UIDeadlock.java, void UIDeadlock.onUIThreadBad(), 26, DEADLOCK, no_bucket, 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(), 18, STARVATION, no_bucket, ERROR, [`void VisDispFrame.callsGetVisibleDisplayFrameOnUiThreadBad()`,calls `void View.getWindowVisibleDisplayFrame(Rect)` from `void VisDispFrame.callsGetVisibleDisplayFrameOnUiThreadBad()`]