Add log section for the last thread dump during a possible deadlock.

This commit is contained in:
Greyson Parrelli
2022-04-27 10:30:30 -04:00
parent 8cb74fb776
commit ebf2ef65e2
4 changed files with 69 additions and 41 deletions

View File

@@ -12,6 +12,13 @@ class DeadlockDetector(private val handler: Handler, private val pollingInterval
private var running = false
private val previouslyBlocked: MutableSet<Long> = mutableSetOf()
private val waitingStates: Set<Thread.State> = setOf(Thread.State.WAITING, Thread.State.TIMED_WAITING)
@Volatile
var lastThreadDump: Map<Thread, Array<StackTraceElement>>? = null
@Volatile
var lastThreadDumpTime: Long = -1
fun start() {
Log.d(TAG, "Beginning deadlock monitoring.");
@@ -26,21 +33,24 @@ class DeadlockDetector(private val handler: Handler, private val pollingInterval
}
private fun poll() {
val time: Long = System.currentTimeMillis()
val threads: Map<Thread, Array<StackTraceElement>> = Thread.getAllStackTraces()
val blocked: Map<Thread, Array<StackTraceElement>> = threads
.filter { entry ->
val thread: Thread = entry.key
val stack: Array<StackTraceElement> = entry.value
thread.state == Thread.State.BLOCKED || (thread.state == Thread.State.WAITING && stack.any { it.methodName.startsWith("lock") })
thread.state == Thread.State.BLOCKED || (waitingStates.contains(thread.state) && stack.any { it.methodName.startsWith("lock") || it.methodName.startsWith("waitForConnection") })
}
.filter { entry -> !BLOCK_BLACKLIST.contains(entry.key.name) }
.filter { entry -> !BLOCK_BLOCKLIST.contains(entry.key.name) }
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))
lastThreadDump = threads
lastThreadDumpTime = time
} else if (stillBlocked.isNotEmpty()) {
val stillBlockedMap: Map<Thread, Array<StackTraceElement>> = stillBlocked
.map { blockedId ->
@@ -51,6 +61,8 @@ class DeadlockDetector(private val handler: Handler, private val pollingInterval
.toMap()
Log.w(TAG, buildLogString("Found a long block! Blocked for at least $pollingInterval ms.", stillBlockedMap))
lastThreadDump = threads
lastThreadDumpTime = time
}
val fullExecutors: List<ExecutorInfo> = CHECK_FULLNESS_EXECUTORS.filter { isExecutorFull(it.executor) }
@@ -64,6 +76,8 @@ class DeadlockDetector(private val handler: Handler, private val pollingInterval
val executor: TracingExecutorService = executorInfo.executor as TracingExecutorService
Log.w(TAG, buildLogString("Found a full executor! ${executor.activeCount}/${executor.maximumPoolSize} threads active with ${executor.queue.size} tasks queued.", fullMap))
}
lastThreadDump = threads
lastThreadDumpTime = time
}
previouslyBlocked.clear()
@@ -89,7 +103,7 @@ class DeadlockDetector(private val handler: Handler, private val pollingInterval
private const val CONCERNING_QUEUE_THRESHOLD = 4
private val BLOCK_BLACKLIST = setOf("HeapTaskDaemon")
private val BLOCK_BLOCKLIST = setOf("HeapTaskDaemon")
private fun buildLogString(description: String, blocked: Map<Thread, Array<StackTraceElement>>): String {
val stringBuilder = StringBuilder()