kopia lustrzana https://github.com/ryukoposting/Signal-Android
Log when executors are full.
rodzic
16ab27084c
commit
fcf62512a7
|
@ -192,6 +192,7 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
|
||||||
|
|
||||||
ApplicationDependencies.getFrameRateTracker().start();
|
ApplicationDependencies.getFrameRateTracker().start();
|
||||||
ApplicationDependencies.getMegaphoneRepository().onAppForegrounded();
|
ApplicationDependencies.getMegaphoneRepository().onAppForegrounded();
|
||||||
|
ApplicationDependencies.getDeadlockDetector().start();
|
||||||
|
|
||||||
SignalExecutors.BOUNDED.execute(() -> {
|
SignalExecutors.BOUNDED.execute(() -> {
|
||||||
FeatureFlags.refreshIfNecessary();
|
FeatureFlags.refreshIfNecessary();
|
||||||
|
@ -202,7 +203,6 @@ public class ApplicationContext extends MultiDexApplication implements AppForegr
|
||||||
KeyCachingService.onAppForegrounded(this);
|
KeyCachingService.onAppForegrounded(this);
|
||||||
ApplicationDependencies.getShakeToReport().enable();
|
ApplicationDependencies.getShakeToReport().enable();
|
||||||
checkBuildExpiration();
|
checkBuildExpiration();
|
||||||
ApplicationDependencies.getDeadlockDetector().start();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Log.d(TAG, "onStart() took " + (System.currentTimeMillis() - startTime) + " ms");
|
Log.d(TAG, "onStart() took " + (System.currentTimeMillis() - startTime) + " ms");
|
||||||
|
|
|
@ -2,6 +2,8 @@ package org.signal.core.util.concurrent
|
||||||
|
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import org.signal.core.util.logging.Log
|
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.
|
* A class that polls active threads at a set interval and logs when multiple threads are BLOCKED.
|
||||||
|
@ -32,13 +34,11 @@ 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") })
|
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 blockedIds: Set<Long> = blocked.keys.map(Thread::getId).toSet()
|
||||||
|
val stillBlocked: Set<Long> = blockedIds.intersect(previouslyBlocked)
|
||||||
|
|
||||||
if (blocked.size > 1) {
|
if (blocked.size > 1) {
|
||||||
Log.w(TAG, buildLogString("Found multiple blocked threads! Possible deadlock.", blocked))
|
Log.w(TAG, buildLogString("Found multiple blocked threads! Possible deadlock.", blocked))
|
||||||
} else {
|
} else if (stillBlocked.isNotEmpty()) {
|
||||||
val stillBlocked: Set<Long> = blockedIds.intersect(previouslyBlocked)
|
|
||||||
|
|
||||||
if (stillBlocked.isNotEmpty()) {
|
|
||||||
val stillBlockedMap: Map<Thread, Array<StackTraceElement>> = stillBlocked
|
val stillBlockedMap: Map<Thread, Array<StackTraceElement>> = stillBlocked
|
||||||
.map { blockedId ->
|
.map { blockedId ->
|
||||||
val key: Thread = blocked.keys.first { it.id == blockedId }
|
val key: Thread = blocked.keys.first { it.id == blockedId }
|
||||||
|
@ -49,6 +49,18 @@ class DeadlockDetector(private val handler: Handler, private val pollingInterval
|
||||||
|
|
||||||
Log.w(TAG, buildLogString("Found a long block! Blocked for at least $pollingInterval ms.", stillBlockedMap))
|
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()
|
||||||
|
|
||||||
|
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))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
previouslyBlocked.clear()
|
previouslyBlocked.clear()
|
||||||
|
@ -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 {
|
companion object {
|
||||||
private val TAG = Log.tag(DeadlockDetector::class.java)
|
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 {
|
private fun buildLogString(description: String, blocked: Map<Thread, Array<StackTraceElement>>): String {
|
||||||
val stringBuilder = StringBuilder()
|
val stringBuilder = StringBuilder()
|
||||||
stringBuilder.append(description).append("\n")
|
stringBuilder.append(description).append("\n")
|
||||||
|
@ -78,5 +102,13 @@ class DeadlockDetector(private val handler: Handler, private val pollingInterval
|
||||||
|
|
||||||
return stringBuilder.toString()
|
return stringBuilder.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun isExecutorFull(executor: ExecutorService): Boolean {
|
||||||
|
return if (executor is ThreadPoolExecutor) {
|
||||||
|
executor.queue.size > CONCERNING_QUEUE_THRESHOLD
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Ładowanie…
Reference in New Issue