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