From 168481fee5e480c2a5d825092604653eedd748ac Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Wed, 13 Oct 2021 11:00:48 -0400 Subject: [PATCH] Improve observer that logs blocked threads. --- .../core/util/concurrent/DeadlockDetector.kt | 36 +++++++++++++++---- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/core-util/src/main/java/org/signal/core/util/concurrent/DeadlockDetector.kt b/core-util/src/main/java/org/signal/core/util/concurrent/DeadlockDetector.kt index f638ed507f..ec3be348a7 100644 --- a/core-util/src/main/java/org/signal/core/util/concurrent/DeadlockDetector.kt +++ b/core-util/src/main/java/org/signal/core/util/concurrent/DeadlockDetector.kt @@ -1,9 +1,7 @@ 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. @@ -11,6 +9,7 @@ import java.util.concurrent.TimeUnit class DeadlockDetector(private val handler: Handler, private val pollingInterval: Long) { private var running = false + private val previouslyBlocked: MutableSet = mutableSetOf() fun start() { Log.d(TAG, "Beginning deadlock monitoring."); @@ -25,13 +24,36 @@ class DeadlockDetector(private val handler: Handler, private val pollingInterval } private fun poll() { - val threads = Thread.getAllStackTraces() - val blocked = threads.filterKeys { thread -> thread.state == Thread.State.BLOCKED } + val threads: Map> = Thread.getAllStackTraces() + val blocked: Map> = threads.filter { entry -> + val thread: Thread = entry.key + val stack: Array = entry.value + + thread.state == Thread.State.BLOCKED || (thread.state == Thread.State.WAITING && stack.any { it.methodName.startsWith("lock") }) + } + val blockedIds: Set = blocked.keys.map(Thread::getId).toSet() if (blocked.size > 1) { - Log.w(TAG, buildLogString(blocked)) + Log.w(TAG, buildLogString("Found multiple blocked threads! Possible deadlock.", blocked)) + } else { + val stillBlocked: Set = blockedIds.intersect(previouslyBlocked) + + if (stillBlocked.isNotEmpty()) { + val stillBlockedMap: Map> = stillBlocked + .map { blockedId -> + val key: Thread = blocked.keys.first { it.id == blockedId } + val value: Array = blocked[key]!! + Pair(key, value) + } + .toMap() + + Log.w(TAG, buildLogString("Found a long block! Blocked for at least $pollingInterval ms.", stillBlockedMap)) + } } + previouslyBlocked.clear() + previouslyBlocked.addAll(blockedIds) + if (running) { handler.postDelayed(this::poll, pollingInterval) } @@ -40,9 +62,9 @@ class DeadlockDetector(private val handler: Handler, private val pollingInterval companion object { private val TAG = Log.tag(DeadlockDetector::class.java) - private fun buildLogString(blocked: Map>): String { + private fun buildLogString(description: String, blocked: Map>): String { val stringBuilder = StringBuilder() - stringBuilder.append("Found multiple blocked threads! Possible deadlock.\n") + stringBuilder.append(description).append("\n") for (entry in blocked) { stringBuilder.append("-- [${entry.key.id}] ${entry.key.name}\n")