diff options
author | Eric Holk <eholk@google.com> | 2019-03-06 20:15:03 -0800 |
---|---|---|
committer | Eric Holk <eholk@google.com> | 2019-03-07 10:21:37 -0800 |
commit | 78fc9b1c2d27161f3c9ff943a8249a4219dcba4b (patch) | |
tree | a794a270a9c327a2b8489f624578e3deead5a1e6 | |
parent | acfb58a5f7ec3bb11d13b14755400028ba2663de (diff) | |
download | trebuchet-78fc9b1c2d27161f3c9ff943a8249a4219dcba4b.tar.gz |
Teach StartupAnalyzer to handle threads with no slices
Sometimes we end up with threads that have no slices to them, which was crashing
the startup analyzer. This change uses POSITIVE_INFINITY as the start time for
threads with no slices.
This change also includes a regression test that was generated by reducing a
trace that exercised this bug. Further regression tests should now be easier to
add.
Test: atest
Change-Id: Ibc82abb6406689b2e1f8c15558458d16e9f3f65d
-rw-r--r-- | Android.bp | 16 | ||||
-rw-r--r-- | TEST_MAPPING | 4 | ||||
-rw-r--r-- | trebuchet/startup-common/src/StartupCommon.kt | 2 | ||||
-rw-r--r-- | trebuchet/startup-common/test/StartupCommonTests.kt | 69 |
4 files changed, 90 insertions, 1 deletions
@@ -61,6 +61,22 @@ java_test_host { test_suites: ["general-tests"], } +java_test_host { + name: "trebuchet-startup-common-tests", + defaults: ["trebuchet-defaults"], + srcs: [ + "trebuchet/startup-common/test/**/*.kt", + ], + static_libs: [ + "trebuchet-core", + "trebuchet-startup-common", + ], + libs: [ + "junit", + ], + test_suites: ["general-tests"], +} + java_binary_host { name: "AnalyzerKt", defaults: ["trebuchet-defaults"], diff --git a/TEST_MAPPING b/TEST_MAPPING index 97a5fde..5be5baf 100644 --- a/TEST_MAPPING +++ b/TEST_MAPPING @@ -8,6 +8,10 @@ "exclude-annotation": "trebuchet.testutils.NeedsSampleData" } ] + }, + { + "name": "trebuchet-startup-common-tests", + "host": true } ] } diff --git a/trebuchet/startup-common/src/StartupCommon.kt b/trebuchet/startup-common/src/StartupCommon.kt index da6c378..13345c5 100644 --- a/trebuchet/startup-common/src/StartupCommon.kt +++ b/trebuchet/startup-common/src/StartupCommon.kt @@ -210,7 +210,7 @@ fun Model.getStartupEvents() : List<StartupEvent> { val newProc = this.findProcess(newProcName, systemServerSlice.startTime, systemServerSlice.endTime) val startProcSlice = systemServerProc.findFirstSlice(SLICE_NAME_PROC_START, newProcName, systemServerSlice.startTime, systemServerSlice.endTime) val rfdSlice = systemServerProc.findFirstSliceOrNull(SLICE_NAME_REPORT_FULLY_DRAWN, newProcName, systemServerSlice.startTime) - val firstSliceTime = newProc.threads.map { it.slices.first().startTime }.min()!! + val firstSliceTime = newProc.threads.map { it.slices.firstOrNull()?.startTime ?: Double.POSITIVE_INFINITY }.min()!! val schedSliceInfo : MutableMap<SchedulingState, Double> = mutableMapOf() newProc.threads.first().schedSlices.forEach schedLoop@ { schedSlice -> diff --git a/trebuchet/startup-common/test/StartupCommonTests.kt b/trebuchet/startup-common/test/StartupCommonTests.kt new file mode 100644 index 0000000..0a73d70 --- /dev/null +++ b/trebuchet/startup-common/test/StartupCommonTests.kt @@ -0,0 +1,69 @@ +/* + * Copyright 2019 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package trebuchet.analyzer.startup + +import getStartupEvents +import org.junit.Test +import trebuchet.io.BufferProducer +import trebuchet.io.DataSlice +import trebuchet.io.asSlice +import trebuchet.model.Model +import trebuchet.task.ImportTask +import trebuchet.util.PrintlnImportFeedback + +fun parseString(source: String): Model { + val task = ImportTask(PrintlnImportFeedback()) + return task.import(object : BufferProducer { + var hasRead = false + override fun next(): DataSlice? { + if (hasRead) return null + hasRead = true + return source.asSlice() + } + }) +} + +class StartupCommonTest { + @Test + fun testEmptyThreadStart() { + // This is a reduced trace based on one seen in the wild that used to + // crash because it creates a thread with no slices assigned to it. + val test = """ +TRACE: +# tracer: nop +# +# entries-in-buffer/entries-written: 289801/289801 #P:4 +# +# _-----=> irqs-off +# / _----=> need-resched +# | / _---=> hardirq/softirq +# || / _--=> preempt-depth +# ||| / delay +# TASK-PID TGID CPU# |||| TIMESTAMP FUNCTION +# | | | | |||| | | + Binder:980_13-3951 ( 980) [002] ...1 1628.269667: tracing_mark_write: E|980 + Binder:980_13-3951 ( 980) [002] ...2 1628.270068: tracing_mark_write: S|980|launching: com.example.eholk.myfirstapp|0 + <...>-1104 (-----) [000] ...1 1628.292040: tracing_mark_write: B|980|Start proc: com.example.eholk.myfirstapp + <...>-1103 (-----) [002] d..4 1628.300748: sched_waking: comm=system_server pid=980 prio=118 target_cpu=001 + RenderThread-9817 ( 9793) [003] d..3 1628.457017: sched_waking: comm=holk.myfirstapp pid=9793 prio=110 target_cpu=002 + RenderThread-9817 ( 9793) [003] ...1 1628.457191: tracing_mark_write: B|9793|Thread birth + """ + + val trace = parseString(test) + trace.getStartupEvents() + } +} |