Log when executors are full.

This commit is contained in:
Greyson Parrelli 2021-10-21 16:32:25 -04:00
parent 16ab27084c
commit fcf62512a7
2 changed files with 44 additions and 12 deletions

View file

@ -192,6 +192,7 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
ApplicationDependencies.getFrameRateTracker().start();
ApplicationDependencies.getMegaphoneRepository().onAppForegrounded();
ApplicationDependencies.getDeadlockDetector().start();
SignalExecutors.BOUNDED.execute(() -> {
FeatureFlags.refreshIfNecessary();
@ -202,7 +203,6 @@ 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");

View file

@ -2,6 +2,8 @@ package org.signal.core.util.concurrent
import android.os.Handler
import org.signal.core.util.logging.Log
import java.util.concurrent.ExecutorService
import java.util.concurrent.ThreadPoolExecutor
/**
* A class that polls active threads at a set interval and logs when multiple threads are BLOCKED.
@ -32,22 +34,32 @@ class DeadlockDetector(private val handler: Handler, private val pollingInterval
thread.state == Thread.State.BLOCKED || (thread.state == Thread.State.WAITING && stack.any { it.methodName.startsWith("lock") })
}
val blockedIds: Set<Long> = blocked.keys.map(Thread::getId).toSet()
val stillBlocked: Set<Long> = blockedIds.intersect(previouslyBlocked)
if (blocked.size > 1) {
Log.w(TAG, buildLogString("Found multiple blocked threads! Possible deadlock.", blocked))
} else {
val stillBlocked: Set<Long> = blockedIds.intersect(previouslyBlocked)
} else if (stillBlocked.isNotEmpty()) {
val stillBlockedMap: Map<Thread, Array<StackTraceElement>> = stillBlocked
.map { blockedId ->
val key: Thread = blocked.keys.first { it.id == blockedId }
val value: Array<StackTraceElement> = blocked[key]!!
Pair(key, value)
}
.toMap()
if (stillBlocked.isNotEmpty()) {
val stillBlockedMap: Map<Thread, Array<StackTraceElement>> = stillBlocked
.map { blockedId ->
val key: Thread = blocked.keys.first { it.id == blockedId }
val value: Array<StackTraceElement> = blocked[key]!!
Pair(key, value)
}
Log.w(TAG, buildLogString("Found a long block! Blocked for at least $pollingInterval ms.", stillBlockedMap))
}
val fullExecutors: List<ExecutorInfo> = EXECUTORS.filter { isExecutorFull(it.executor) }
if (fullExecutors.isNotEmpty()) {
fullExecutors.forEach { executorInfo ->
val fullMap: Map<Thread, Array<StackTraceElement>> = threads
.filter { it.key.name.startsWith(executorInfo.namePrefix) }
.toMap()
Log.w(TAG, buildLogString("Found a long block! Blocked for at least $pollingInterval ms.", stillBlockedMap))
val executor: ThreadPoolExecutor = executorInfo.executor as ThreadPoolExecutor
Log.w(TAG, buildLogString("Found a full executor! ${executor.activeCount}/${executor.corePoolSize} threads active with ${executor.queue.size} tasks queued.", fullMap))
}
}
@ -59,9 +71,21 @@ class DeadlockDetector(private val handler: Handler, private val pollingInterval
}
}
private data class ExecutorInfo(
val executor: ExecutorService,
val namePrefix: String
)
companion object {
private val TAG = Log.tag(DeadlockDetector::class.java)
private val EXECUTORS: Set<ExecutorInfo> = setOf(
ExecutorInfo(SignalExecutors.BOUNDED, "signal-bounded-"),
ExecutorInfo(SignalExecutors.BOUNDED_IO, "signal-bounded-io-")
)
private const val CONCERNING_QUEUE_THRESHOLD = 4
private fun buildLogString(description: String, blocked: Map<Thread, Array<StackTraceElement>>): String {
val stringBuilder = StringBuilder()
stringBuilder.append(description).append("\n")
@ -78,5 +102,13 @@ class DeadlockDetector(private val handler: Handler, private val pollingInterval
return stringBuilder.toString()
}
private fun isExecutorFull(executor: ExecutorService): Boolean {
return if (executor is ThreadPoolExecutor) {
executor.queue.size > CONCERNING_QUEUE_THRESHOLD
} else {
false
}
}
}
}
}