Add an observer to log blocked threads.
This commit is contained in:
parent
f65de84c19
commit
0c8b6f8ef8
10 changed files with 194 additions and 15 deletions
|
@ -191,7 +191,7 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
|
|||
long startTime = System.currentTimeMillis();
|
||||
Log.i(TAG, "App is now visible.");
|
||||
|
||||
ApplicationDependencies.getFrameRateTracker().begin();
|
||||
ApplicationDependencies.getFrameRateTracker().start();
|
||||
ApplicationDependencies.getMegaphoneRepository().onAppForegrounded();
|
||||
|
||||
SignalExecutors.BOUNDED.execute(() -> {
|
||||
|
@ -203,6 +203,7 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
|
|||
KeyCachingService.onAppForegrounded(this);
|
||||
ApplicationDependencies.getShakeToReport().enable();
|
||||
checkBuildExpiration();
|
||||
ApplicationDependencies.getDeadlockDetector().start();
|
||||
});
|
||||
|
||||
Log.d(TAG, "onStart() took " + (System.currentTimeMillis() - startTime) + " ms");
|
||||
|
@ -213,8 +214,9 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
|
|||
Log.i(TAG, "App is no longer visible.");
|
||||
KeyCachingService.onAppBackgrounded(this);
|
||||
ApplicationDependencies.getMessageNotifier().clearVisibleThread();
|
||||
ApplicationDependencies.getFrameRateTracker().end();
|
||||
ApplicationDependencies.getFrameRateTracker().stop();
|
||||
ApplicationDependencies.getShakeToReport().disable();
|
||||
ApplicationDependencies.getDeadlockDetector().stop();
|
||||
}
|
||||
|
||||
public PersistentLogger getPersistentLogger() {
|
||||
|
|
|
@ -5,6 +5,7 @@ import android.app.Application;
|
|||
import androidx.annotation.MainThread;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.signal.core.util.concurrent.DeadlockDetector;
|
||||
import org.thoughtcrime.securesms.KbsEnclave;
|
||||
import org.thoughtcrime.securesms.components.TypingStatusRepository;
|
||||
import org.thoughtcrime.securesms.components.TypingStatusSender;
|
||||
|
@ -106,6 +107,7 @@ public class ApplicationDependencies {
|
|||
private static volatile SimpleExoPlayerPool exoPlayerPool;
|
||||
private static volatile AudioManagerCompat audioManagerCompat;
|
||||
private static volatile DonationsService donationsService;
|
||||
private static volatile DeadlockDetector deadlockDetector;
|
||||
|
||||
@MainThread
|
||||
public static void init(@NonNull Application application, @NonNull Provider provider) {
|
||||
|
@ -603,6 +605,17 @@ public class ApplicationDependencies {
|
|||
return donationsService;
|
||||
}
|
||||
|
||||
public static @NonNull DeadlockDetector getDeadlockDetector() {
|
||||
if (deadlockDetector == null) {
|
||||
synchronized (LOCK) {
|
||||
if (deadlockDetector == null) {
|
||||
deadlockDetector = provider.provideDeadlockDetector();
|
||||
}
|
||||
}
|
||||
}
|
||||
return deadlockDetector;
|
||||
}
|
||||
|
||||
public interface Provider {
|
||||
@NonNull GroupsV2Operations provideGroupsV2Operations();
|
||||
@NonNull SignalServiceAccountManager provideSignalServiceAccountManager();
|
||||
|
@ -639,5 +652,6 @@ public class ApplicationDependencies {
|
|||
@NonNull SimpleExoPlayerPool provideExoPlayerPool();
|
||||
@NonNull AudioManagerCompat provideAndroidCallAudioManager();
|
||||
@NonNull DonationsService provideDonationsService();
|
||||
@NonNull DeadlockDetector provideDeadlockDetector();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,12 @@ package org.thoughtcrime.securesms.dependencies;
|
|||
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.signal.core.util.concurrent.DeadlockDetector;
|
||||
import org.signal.core.util.concurrent.SignalExecutors;
|
||||
import org.thoughtcrime.securesms.BuildConfig;
|
||||
import org.thoughtcrime.securesms.components.TypingStatusRepository;
|
||||
|
@ -76,6 +79,7 @@ import org.whispersystems.signalservice.api.websocket.WebSocketFactory;
|
|||
import org.whispersystems.signalservice.internal.websocket.WebSocketConnection;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Implementation of {@link ApplicationDependencies.Provider} that provides real app dependencies.
|
||||
|
@ -311,6 +315,13 @@ public class ApplicationDependencyProvider implements ApplicationDependencies.Pr
|
|||
FeatureFlags.okHttpAutomaticRetry());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull DeadlockDetector provideDeadlockDetector() {
|
||||
HandlerThread handlerThread = new HandlerThread("signal-DeadlockDetector");
|
||||
handlerThread.start();
|
||||
return new DeadlockDetector(new Handler(handlerThread.getLooper()), TimeUnit.SECONDS.toMillis(5));
|
||||
}
|
||||
|
||||
private @NonNull WebSocketFactory provideWebSocketFactory(@NonNull SignalWebSocketHealthMonitor healthMonitor) {
|
||||
return new WebSocketFactory() {
|
||||
@Override
|
||||
|
|
|
@ -40,7 +40,7 @@ public class FrameRateTracker {
|
|||
updateRefreshRate();
|
||||
}
|
||||
|
||||
public void begin() {
|
||||
public void start() {
|
||||
Log.d(TAG, String.format(Locale.ENGLISH, "Beginning frame rate tracking. Screen refresh rate: %.2f hz, or %.2f ms per frame.", refreshRate, idealTimePerFrameNanos / (float) 1_000_000));
|
||||
|
||||
lastFrameTimeNanos = System.nanoTime();
|
||||
|
@ -48,7 +48,7 @@ public class FrameRateTracker {
|
|||
Choreographer.getInstance().postFrameCallback(calculator);
|
||||
}
|
||||
|
||||
public void end() {
|
||||
public void stop() {
|
||||
Choreographer.getInstance().removeFrameCallback(calculator);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'com.google.protobuf'
|
||||
apply plugin: 'witness'
|
||||
plugins {
|
||||
id 'com.android.library'
|
||||
id 'com.google.protobuf'
|
||||
id 'witness'
|
||||
id 'kotlin-android'
|
||||
id 'kotlin-kapt'
|
||||
}
|
||||
apply from: 'witness-verifications.gradle'
|
||||
|
||||
android {
|
||||
|
@ -18,6 +22,9 @@ android {
|
|||
sourceCompatibility JAVA_VERSION
|
||||
targetCompatibility JAVA_VERSION
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
}
|
||||
|
||||
dependencyVerification {
|
||||
|
@ -46,6 +53,7 @@ dependencies {
|
|||
|
||||
api libs.androidx.annotation
|
||||
|
||||
implementation libs.androidx.core.ktx
|
||||
implementation libs.google.protobuf.javalite
|
||||
|
||||
testImplementation testLibs.junit.junit
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
package org.signal.core.util.concurrent
|
||||
|
||||
import android.os.Handler
|
||||
import android.os.SystemClock
|
||||
import org.signal.core.util.logging.Log
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
/**
|
||||
* A class that polls active threads at a set interval and logs when multiple threads are BLOCKED.
|
||||
*/
|
||||
class DeadlockDetector(private val handler: Handler, private val pollingInterval: Long) {
|
||||
|
||||
private var running = false
|
||||
|
||||
fun start() {
|
||||
Log.d(TAG, "Beginning deadlock monitoring.");
|
||||
running = true
|
||||
handler.postDelayed(this::poll, pollingInterval)
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
Log.d(TAG, "Ending deadlock monitoring.");
|
||||
running = false
|
||||
handler.removeCallbacksAndMessages(null)
|
||||
}
|
||||
|
||||
private fun poll() {
|
||||
val threads = Thread.getAllStackTraces()
|
||||
val blocked = threads.filterKeys { thread -> thread.state == Thread.State.BLOCKED }
|
||||
|
||||
if (blocked.size > 1) {
|
||||
Log.w(TAG, buildLogString(blocked))
|
||||
}
|
||||
|
||||
if (running) {
|
||||
handler.postDelayed(this::poll, pollingInterval)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = Log.tag(DeadlockDetector::class.java)
|
||||
|
||||
private fun buildLogString(blocked: Map<Thread, Array<StackTraceElement>>): String {
|
||||
val stringBuilder = StringBuilder()
|
||||
stringBuilder.append("Found multiple blocked threads! Possible deadlock.\n")
|
||||
|
||||
for (entry in blocked) {
|
||||
stringBuilder.append("-- [${entry.key.id}] ${entry.key.name}\n")
|
||||
|
||||
for (element in entry.value) {
|
||||
stringBuilder.append("$element\n")
|
||||
}
|
||||
|
||||
stringBuilder.append("\n")
|
||||
}
|
||||
|
||||
return stringBuilder.toString()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,7 +6,37 @@ dependencyVerification {
|
|||
['androidx.annotation:annotation:1.2.0',
|
||||
'9029262bddce116e6d02be499e4afdba21f24c239087b76b3b57d7e98b490a36'],
|
||||
|
||||
['androidx.arch.core:core-common:2.0.0',
|
||||
'4b80b337779b526e64b0ee0ca9e0df43b808344d145f8e9b1c42a134dac57ad8'],
|
||||
|
||||
['androidx.collection:collection:1.0.0',
|
||||
'9c8d117b5c2bc120a1cdfeb857e05b495b16c36013570372a708f7827e3ac9f9'],
|
||||
|
||||
['androidx.core:core-ktx:1.5.0',
|
||||
'5964cfe7a4882da2a00fb6ca3d3a072d04139208186f7bc4b3cb66022764fc42'],
|
||||
|
||||
['androidx.core:core:1.5.0',
|
||||
'2b279712795689069cfb63e48b3ab63c32a5649bdda44c482eb8f81ca1a72161'],
|
||||
|
||||
['androidx.lifecycle:lifecycle-common:2.0.0',
|
||||
'7bad7a188804adea6fa1f35d5ef99b705f20bd93ecadde484760ff86b535fefc'],
|
||||
|
||||
['androidx.lifecycle:lifecycle-runtime:2.0.0',
|
||||
'e4afc9e636183f6f3e0edf1cf46121a492ffd2c673075bb07f55c7a99dd43cfb'],
|
||||
|
||||
['androidx.versionedparcelable:versionedparcelable:1.1.1',
|
||||
'57e8d93260d18d5b9007c9eed3c64ad159de90c8609ebfc74a347cbd514535a4'],
|
||||
|
||||
['com.google.protobuf:protobuf-javalite:3.10.0',
|
||||
'215a94dbe100130295906b531bb72a26965c7ac8fcd9a75bf8054a8ac2abf4b4'],
|
||||
|
||||
['org.jetbrains.kotlin:kotlin-stdlib-common:1.4.32',
|
||||
'e1ff6f55ee9e7591dcc633f7757bac25a7edb1cc7f738b37ec652f10f66a4145'],
|
||||
|
||||
['org.jetbrains.kotlin:kotlin-stdlib:1.4.32',
|
||||
'13e9fd3e69dc7230ce0fc873a92a4e5d521d179bcf1bef75a6705baac3bfecba'],
|
||||
|
||||
['org.jetbrains:annotations:13.0',
|
||||
'ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478'],
|
||||
]
|
||||
}
|
||||
|
|
|
@ -24,8 +24,11 @@ dependencyVerification {
|
|||
['androidx.collection:collection:1.1.0',
|
||||
'632a0e5407461de774409352940e292a291037724207a787820c77daf7d33b72'],
|
||||
|
||||
['androidx.core:core:1.3.0',
|
||||
'1c6b6626f15185d8f4bc7caac759412a1ab6e851ecf7526387d9b9fadcabdb63'],
|
||||
['androidx.core:core-ktx:1.5.0',
|
||||
'5964cfe7a4882da2a00fb6ca3d3a072d04139208186f7bc4b3cb66022764fc42'],
|
||||
|
||||
['androidx.core:core:1.5.0',
|
||||
'2b279712795689069cfb63e48b3ab63c32a5649bdda44c482eb8f81ca1a72161'],
|
||||
|
||||
['androidx.cursoradapter:cursoradapter:1.0.0',
|
||||
'a81c8fe78815fa47df5b749deb52727ad11f9397da58b16017f4eb2c11e28564'],
|
||||
|
@ -69,8 +72,8 @@ dependencyVerification {
|
|||
['androidx.vectordrawable:vectordrawable:1.1.0',
|
||||
'46fd633ac01b49b7fcabc263bf098c5a8b9e9a69774d234edcca04fb02df8e26'],
|
||||
|
||||
['androidx.versionedparcelable:versionedparcelable:1.1.0',
|
||||
'9a1d77140ac222b7866b5054ee7d159bc1800987ed2d46dd6afdd145abb710c1'],
|
||||
['androidx.versionedparcelable:versionedparcelable:1.1.1',
|
||||
'57e8d93260d18d5b9007c9eed3c64ad159de90c8609ebfc74a347cbd514535a4'],
|
||||
|
||||
['androidx.viewpager:viewpager:1.0.0',
|
||||
'147af4e14a1984010d8f155e5e19d781f03c1d70dfed02a8e0d18428b8fc8682'],
|
||||
|
@ -81,6 +84,15 @@ dependencyVerification {
|
|||
['org.greenrobot:eventbus:3.0.0',
|
||||
'180d4212467df06f2fbc9c8d8a2984533ac79c87769ad883bc421612f0b4e17c'],
|
||||
|
||||
['org.jetbrains.kotlin:kotlin-stdlib-common:1.4.32',
|
||||
'e1ff6f55ee9e7591dcc633f7757bac25a7edb1cc7f738b37ec652f10f66a4145'],
|
||||
|
||||
['org.jetbrains.kotlin:kotlin-stdlib:1.4.32',
|
||||
'13e9fd3e69dc7230ce0fc873a92a4e5d521d179bcf1bef75a6705baac3bfecba'],
|
||||
|
||||
['org.jetbrains:annotations:13.0',
|
||||
'ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478'],
|
||||
|
||||
['org.whispersystems:signal-client-java:0.9.6',
|
||||
'f64aeb071eae2e1e2413902da6554c03e22f66d7a59ffdd79f3beeb0248ea054'],
|
||||
]
|
||||
|
|
|
@ -39,8 +39,11 @@ dependencyVerification {
|
|||
['androidx.coordinatorlayout:coordinatorlayout:1.1.0',
|
||||
'44a9e30abf56af1025c52a0af506fee9c4131aa55efda52f9fd9451211c5e8cb'],
|
||||
|
||||
['androidx.core:core:1.3.1',
|
||||
'e92ea65a37d589943d405a6a54d1be9d12a225948f26c4e41e511dd55e81efb6'],
|
||||
['androidx.core:core-ktx:1.5.0',
|
||||
'5964cfe7a4882da2a00fb6ca3d3a072d04139208186f7bc4b3cb66022764fc42'],
|
||||
|
||||
['androidx.core:core:1.5.0',
|
||||
'2b279712795689069cfb63e48b3ab63c32a5649bdda44c482eb8f81ca1a72161'],
|
||||
|
||||
['androidx.cursoradapter:cursoradapter:1.0.0',
|
||||
'a81c8fe78815fa47df5b749deb52727ad11f9397da58b16017f4eb2c11e28564'],
|
||||
|
@ -105,8 +108,8 @@ dependencyVerification {
|
|||
['androidx.vectordrawable:vectordrawable:1.1.0',
|
||||
'46fd633ac01b49b7fcabc263bf098c5a8b9e9a69774d234edcca04fb02df8e26'],
|
||||
|
||||
['androidx.versionedparcelable:versionedparcelable:1.1.0',
|
||||
'9a1d77140ac222b7866b5054ee7d159bc1800987ed2d46dd6afdd145abb710c1'],
|
||||
['androidx.versionedparcelable:versionedparcelable:1.1.1',
|
||||
'57e8d93260d18d5b9007c9eed3c64ad159de90c8609ebfc74a347cbd514535a4'],
|
||||
|
||||
['androidx.viewpager2:viewpager2:1.0.0',
|
||||
'e95c0031d4cc247cd48196c6287e58d2cee54d9c79b85afea7c90920330275af'],
|
||||
|
@ -119,5 +122,14 @@ dependencyVerification {
|
|||
|
||||
['com.google.protobuf:protobuf-javalite:3.10.0',
|
||||
'215a94dbe100130295906b531bb72a26965c7ac8fcd9a75bf8054a8ac2abf4b4'],
|
||||
|
||||
['org.jetbrains.kotlin:kotlin-stdlib-common:1.4.32',
|
||||
'e1ff6f55ee9e7591dcc633f7757bac25a7edb1cc7f738b37ec652f10f66a4145'],
|
||||
|
||||
['org.jetbrains.kotlin:kotlin-stdlib:1.4.32',
|
||||
'13e9fd3e69dc7230ce0fc873a92a4e5d521d179bcf1bef75a6705baac3bfecba'],
|
||||
|
||||
['org.jetbrains:annotations:13.0',
|
||||
'ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478'],
|
||||
]
|
||||
}
|
||||
|
|
|
@ -6,9 +6,39 @@ dependencyVerification {
|
|||
['androidx.annotation:annotation:1.2.0',
|
||||
'9029262bddce116e6d02be499e4afdba21f24c239087b76b3b57d7e98b490a36'],
|
||||
|
||||
['androidx.arch.core:core-common:2.0.0',
|
||||
'4b80b337779b526e64b0ee0ca9e0df43b808344d145f8e9b1c42a134dac57ad8'],
|
||||
|
||||
['androidx.collection:collection:1.0.0',
|
||||
'9c8d117b5c2bc120a1cdfeb857e05b495b16c36013570372a708f7827e3ac9f9'],
|
||||
|
||||
['androidx.core:core-ktx:1.5.0',
|
||||
'5964cfe7a4882da2a00fb6ca3d3a072d04139208186f7bc4b3cb66022764fc42'],
|
||||
|
||||
['androidx.core:core:1.5.0',
|
||||
'2b279712795689069cfb63e48b3ab63c32a5649bdda44c482eb8f81ca1a72161'],
|
||||
|
||||
['androidx.lifecycle:lifecycle-common:2.0.0',
|
||||
'7bad7a188804adea6fa1f35d5ef99b705f20bd93ecadde484760ff86b535fefc'],
|
||||
|
||||
['androidx.lifecycle:lifecycle-runtime:2.0.0',
|
||||
'e4afc9e636183f6f3e0edf1cf46121a492ffd2c673075bb07f55c7a99dd43cfb'],
|
||||
|
||||
['androidx.versionedparcelable:versionedparcelable:1.1.1',
|
||||
'57e8d93260d18d5b9007c9eed3c64ad159de90c8609ebfc74a347cbd514535a4'],
|
||||
|
||||
['com.google.protobuf:protobuf-javalite:3.10.0',
|
||||
'215a94dbe100130295906b531bb72a26965c7ac8fcd9a75bf8054a8ac2abf4b4'],
|
||||
|
||||
['org.jetbrains.kotlin:kotlin-stdlib-common:1.4.32',
|
||||
'e1ff6f55ee9e7591dcc633f7757bac25a7edb1cc7f738b37ec652f10f66a4145'],
|
||||
|
||||
['org.jetbrains.kotlin:kotlin-stdlib:1.4.32',
|
||||
'13e9fd3e69dc7230ce0fc873a92a4e5d521d179bcf1bef75a6705baac3bfecba'],
|
||||
|
||||
['org.jetbrains:annotations:13.0',
|
||||
'ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478'],
|
||||
|
||||
['org.mp4parser:isoparser:1.9.39',
|
||||
'a3a7172648f1ac4b2a369ecca2861317e472179c842a5217b08643ba0a1dfa12'],
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue