Skip to content
Snippets Groups Projects
Commit c034c721 authored by Junyu Lai's avatar Junyu Lai
Browse files

Make thread leak error message more readable

This change only outputs thread count differences instead of
listing all threads. Additionally, it ignores threads with 1
count, which effectively filter out threads created
by the test runner or other system components, such as
hwuiTask*, queued-work-looper, SurfaceSyncGroupTimer,
RenderThread, and Time-limited test.

Sample Output:
[1122/3060] android.net.connectivity.com.android.server.CSLocalAgentCreationTests#ThreadLeakMonitor: FAILED (1ms)

STACKTRACE:
java.lang.IllegalStateException: Unexpected thread changes: removed=[] added=[] updated=[TestAlarmManager=25,CSTestHandler=25]

Test: atest ConnectivityCoverageTests
      (with shouldThreadLeakFailTest set to true)
Bug: 310581973
Bug: 307693729
Change-Id: I085799ebbd69c29f833335684de208105302e012
parent 17f589cf
No related branches found
No related tags found
No related merge requests found
......@@ -17,9 +17,9 @@
package com.android.testutils
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult
import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo
import java.lang.IllegalStateException
import java.lang.reflect.Modifier
import org.junit.runner.Description
import org.junit.runner.Runner
......@@ -110,10 +110,19 @@ class DevSdkIgnoreRunner(private val klass: Class<*>) : Runner(), Filterable, So
notifier.fireTestStarted(leakMonitorDesc)
val threadCountsAfterTest = getAllThreadNameCounts()
if (threadCountsBeforeTest != threadCountsAfterTest) {
// TODO : move CompareOrUpdateResult to its own util instead of LinkProperties.
val threadsDiff = CompareOrUpdateResult(
threadCountsBeforeTest.entries,
threadCountsAfterTest.entries
) { it.key }
// Ignore removed threads, which typically are generated by previous tests.
// Because this is in the threadsDiff.updated member, for sure there is a
// corresponding key in threadCountsBeforeTest.
val increasedThreads = threadsDiff.updated
.filter { threadCountsBeforeTest[it.key]!! < it.value }
if (threadsDiff.added.isNotEmpty() || increasedThreads.isNotEmpty()) {
notifier.fireTestFailure(Failure(leakMonitorDesc,
IllegalStateException("Expected threads: $threadCountsBeforeTest " +
"but got: $threadCountsAfterTest")))
IllegalStateException("Unexpected thread changes: $threadsDiff")))
}
notifier.fireTestFinished(leakMonitorDesc)
}
......@@ -121,9 +130,13 @@ class DevSdkIgnoreRunner(private val klass: Class<*>) : Runner(), Filterable, So
private fun getAllThreadNameCounts(): Map<String, Int> {
// Get the counts of threads in the group per name.
// Filter system thread groups.
// Also ignore threads with 1 count, this effectively filtered out threads created by the
// test runner or other system components. e.g. hwuiTask*, queued-work-looper,
// SurfaceSyncGroupTimer, RenderThread, Time-limited test, etc.
return Thread.getAllStackTraces().keys
.filter { it.threadGroup?.name != "system" }
.groupingBy { it.name }.eachCount()
.filter { it.value != 1 }
}
override fun getDescription(): Description {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment