Summary: First step towards a global analysis. A new command line flag activates the step in `Driver`. The whole-program analysis is a simple, quadratic (inefficient-as-yet), iteration over all domain elements. However, it is restricted to those elements that are explicitly scheduled to run. Reviewed By: skcho Differential Revision: D17787441 fbshipit-source-id: 9fecd766cmaster
parent
bd1b55ef51
commit
08df37ef76
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"force-delete-results-dir": true
|
||||||
|
}
|
@ -0,0 +1,95 @@
|
|||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
class Deadlock {
|
||||||
|
// executors are injected and annotated as to what thread they schedule to
|
||||||
|
@ForUiThread private final Executor mUiThreadExecutor = null;
|
||||||
|
@ForNonUiThread private final Executor mNonUiThreadExecutor = null;
|
||||||
|
|
||||||
|
Object monitorA, monitorB;
|
||||||
|
|
||||||
|
// text-book deadlock between UI and background thread
|
||||||
|
public void postDeadlockBad() {
|
||||||
|
mUiThreadExecutor.execute(
|
||||||
|
new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
synchronized (monitorA) {
|
||||||
|
synchronized (monitorB) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mNonUiThreadExecutor.execute(
|
||||||
|
new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
synchronized (monitorB) {
|
||||||
|
synchronized (monitorA) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Object monitorC, monitorD;
|
||||||
|
|
||||||
|
// non-deadlock as both work items are scheduled on same thread
|
||||||
|
public void postOnUIThreadOk() {
|
||||||
|
mUiThreadExecutor.execute(
|
||||||
|
new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
synchronized (monitorC) {
|
||||||
|
synchronized (monitorD) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mUiThreadExecutor.execute(
|
||||||
|
new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
synchronized (monitorD) {
|
||||||
|
synchronized (monitorC) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Object monitorE, monitorF;
|
||||||
|
|
||||||
|
// deadlock as both work items are scheduled on background threads
|
||||||
|
public void postOnBGThreadBad() {
|
||||||
|
mNonUiThreadExecutor.execute(
|
||||||
|
new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
synchronized (monitorE) {
|
||||||
|
synchronized (monitorF) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mNonUiThreadExecutor.execute(
|
||||||
|
new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
synchronized (monitorF) {
|
||||||
|
synchronized (monitorE) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* 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 DirectStarvation {
|
||||||
|
static Binder binder;
|
||||||
|
|
||||||
|
// executors are injected and annotated as to what thread they schedule to
|
||||||
|
@ForUiThread private final Executor mUiThreadExecutor = null;
|
||||||
|
@ForNonUiThread private final Executor mNonUiThreadExecutor = null;
|
||||||
|
|
||||||
|
// call which should not happen on UI thread
|
||||||
|
private static void doTransact() {
|
||||||
|
try {
|
||||||
|
binder.transact(0, null, null, 0);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// starvation via scheduling a transaction on UI thread
|
||||||
|
public void postBlockingCallToUIThreadBad() {
|
||||||
|
mUiThreadExecutor.execute(
|
||||||
|
new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
doTransact();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void postBlockingCallToNonUIThreadOk() {
|
||||||
|
mNonUiThreadExecutor.execute(
|
||||||
|
new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
doTransact();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@interface ForNonUiThread {}
|
@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@interface ForUiThread {}
|
@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* 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 IndirectStarvation {
|
||||||
|
static Binder binder;
|
||||||
|
|
||||||
|
// executors are injected and annotated as to what thread they schedule to
|
||||||
|
@ForUiThread private final Executor mUiThreadExecutor = null;
|
||||||
|
@ForNonUiThread private final Executor mNonUiThreadExecutor = null;
|
||||||
|
|
||||||
|
// call which should not happen on UI thread
|
||||||
|
private static void doTransact() {
|
||||||
|
try {
|
||||||
|
binder.transact(0, null, null, 0);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object monitorA;
|
||||||
|
|
||||||
|
// starvation via locking on UI thread and doing a transaction under that lock
|
||||||
|
// in a background thread
|
||||||
|
public void postBlockingCallToBackgroundThreadAndLockBad() {
|
||||||
|
mUiThreadExecutor.execute(
|
||||||
|
new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
synchronized (monitorA) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mNonUiThreadExecutor.execute(
|
||||||
|
new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
synchronized (monitorA) {
|
||||||
|
doTransact();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Object monitorB, monitorC;
|
||||||
|
|
||||||
|
// no starvation, as lock on UI thread is not used for transaction on background thread
|
||||||
|
public void postBlockingCallToBackgroundThreadAndUseOtherLockOk() {
|
||||||
|
mUiThreadExecutor.execute(
|
||||||
|
new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
synchronized (monitorB) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
mNonUiThreadExecutor.execute(
|
||||||
|
new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
synchronized (monitorC) {
|
||||||
|
doTransact();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,12 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
TESTS_DIR = ../../..
|
||||||
|
|
||||||
|
INFER_OPTIONS = --starvation-only --starvation-whole-program --debug-exceptions
|
||||||
|
INFERPRINT_OPTIONS = --issues-tests
|
||||||
|
SOURCES = $(wildcard *.java)
|
||||||
|
|
||||||
|
include $(TESTS_DIR)/javac.make
|
@ -0,0 +1,59 @@
|
|||||||
|
/*
|
||||||
|
* 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 ModeledExecutors {
|
||||||
|
static Binder binder;
|
||||||
|
|
||||||
|
private static void doTransact() {
|
||||||
|
try {
|
||||||
|
binder.transact(0, null, null, 0);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// starvation via scheduling a transaction on UI thread
|
||||||
|
public void postBlockingCallToUIThreadBad() {
|
||||||
|
Executors.getForegroundExecutor()
|
||||||
|
.execute(
|
||||||
|
new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
doTransact();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void postBlockingCallToNonUIThreadOk() {
|
||||||
|
Executors.getBackgroundExecutor()
|
||||||
|
.execute(
|
||||||
|
new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
doTransact();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// modeled executors
|
||||||
|
class Executors {
|
||||||
|
static Executor uiExecutor;
|
||||||
|
|
||||||
|
static Executor getForegroundExecutor() {
|
||||||
|
return uiExecutor;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Executor bgExecutor;
|
||||||
|
|
||||||
|
static Executor getBackgroundExecutor() {
|
||||||
|
return bgExecutor;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
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.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/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/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)`]
|
@ -1,153 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
@interface ForUiThread {}
|
|
||||||
|
|
||||||
@interface ForNonUiThread {}
|
|
||||||
|
|
||||||
class ExecutorRunnable {
|
|
||||||
static Binder binder;
|
|
||||||
@ForUiThread private final Executor mUiThreadExecutor = null;
|
|
||||||
@ForNonUiThread private final Executor mNonUiThreadExecutor = null;
|
|
||||||
|
|
||||||
private static void doTransact() {
|
|
||||||
try {
|
|
||||||
binder.transact(0, null, null, 0);
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void FN_postBlockingCallToUIThreadBad() {
|
|
||||||
mUiThreadExecutor.execute(
|
|
||||||
new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
doTransact();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void postBlockingCallToNonUIThreadOk() {
|
|
||||||
mNonUiThreadExecutor.execute(
|
|
||||||
new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
doTransact();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Object monitorA, monitorB;
|
|
||||||
|
|
||||||
private void lockAB() {
|
|
||||||
synchronized (monitorA) {
|
|
||||||
synchronized (monitorB) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void lockBA() {
|
|
||||||
synchronized (monitorB) {
|
|
||||||
synchronized (monitorA) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void FN_postDeadlockBad() {
|
|
||||||
mUiThreadExecutor.execute(
|
|
||||||
new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
lockAB();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
mNonUiThreadExecutor.execute(
|
|
||||||
new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
lockBA();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void postOnUIThreadOk() {
|
|
||||||
mUiThreadExecutor.execute(
|
|
||||||
new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
lockAB();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
mUiThreadExecutor.execute(
|
|
||||||
new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
lockBA();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void postDeadlockIndirectBad() {
|
|
||||||
Executors.getForegroundExecutor()
|
|
||||||
.execute(
|
|
||||||
new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
lockAB();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Executors.getBackgroundExecutor()
|
|
||||||
.execute(
|
|
||||||
new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
lockBA();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void postOnUIThreadIndirectOk() {
|
|
||||||
Executors.getForegroundExecutor()
|
|
||||||
.execute(
|
|
||||||
new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
lockAB();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Executors.getForegroundExecutor()
|
|
||||||
.execute(
|
|
||||||
new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
lockBA();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Executors {
|
|
||||||
static Executor uiExecutor;
|
|
||||||
|
|
||||||
static Executor getForegroundExecutor() {
|
|
||||||
return uiExecutor;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Executor bgExecutor;
|
|
||||||
|
|
||||||
static Executor getBackgroundExecutor() {
|
|
||||||
return bgExecutor;
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in new issue