Skip to content

Commit 172f674

Browse files
authored
[Android] Frame processing improvements (#435)
1 parent c37854f commit 172f674

File tree

4 files changed

+130
-65
lines changed

4 files changed

+130
-65
lines changed

platform/jvm/capture/src/main/kotlin/io/bitdrift/capture/reports/processor/AppExitAnrTraceProcessor.kt

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,9 @@ package io.bitdrift.capture.reports.processor
1010
import com.google.flatbuffers.FlatBufferBuilder
1111
import io.bitdrift.capture.reports.binformat.v1.Error
1212
import io.bitdrift.capture.reports.binformat.v1.ErrorRelation
13-
import io.bitdrift.capture.reports.binformat.v1.Frame
1413
import io.bitdrift.capture.reports.binformat.v1.FrameType
1514
import io.bitdrift.capture.reports.binformat.v1.Report
1615
import io.bitdrift.capture.reports.binformat.v1.ReportType
17-
import io.bitdrift.capture.reports.binformat.v1.SourceFile
1816
import java.io.BufferedReader
1917
import java.io.InputStream
2018
import java.io.InputStreamReader
@@ -26,6 +24,7 @@ import java.io.InputStreamReader
2624
internal object AppExitAnrTraceProcessor {
2725
private const val ANR_MAIN_THREAD_IDENTIFIED = "\"main\""
2826
private const val ANR_STACKTRACE_PREFIX = "at "
27+
private const val ADDITIONAL_THREAD_INFO_IDENTIFIER = "|"
2928
private val ANR_STACK_TRACE_REGEX = Regex("^\\s+at\\s+(.*)\\.(.*)\\((.*):(\\d+)\\)$")
3029
private val mainStackTraceFrames = mutableListOf<Int>()
3130
private var isProcessingMainThreadTrace = false
@@ -85,41 +84,50 @@ internal object AppExitAnrTraceProcessor {
8584
currentLine: String,
8685
) {
8786
setIsMainThreadStackTrace(currentLine)
88-
if (!isStackTraceLine(currentLine)) return
8987

90-
ANR_STACK_TRACE_REGEX.find(currentLine)?.destructured?.let { (className, symbolName, fileName, lineNumber) ->
91-
if (!isProcessingMainThreadTrace) {
92-
return
93-
}
94-
val path = builder.createString(fileName)
95-
val sourceFile =
96-
SourceFile.createSourceFile(
97-
builder,
98-
path,
99-
lineNumber.toLongOrNull() ?: 0,
100-
0,
101-
)
102-
val frame =
103-
Frame.createFrame(
104-
builder,
105-
FrameType.JVM,
106-
builder.createString(className),
107-
builder.createString(symbolName),
108-
sourceFile,
109-
0,
110-
0u,
111-
0u,
112-
0,
113-
0,
114-
0,
115-
0u,
116-
false,
117-
0,
118-
)
119-
mainStackTraceFrames.add(frame)
88+
if (!isProcessingMainThreadTrace) {
89+
return
12090
}
91+
92+
val frame =
93+
when {
94+
isStackTraceLine(currentLine) -> {
95+
ANR_STACK_TRACE_REGEX
96+
.find(currentLine)
97+
?.destructured
98+
?.let { (className, symbolName, fileName, lineNumber) ->
99+
val frameData =
100+
FrameData(
101+
className = className,
102+
symbolName = symbolName,
103+
fileName = fileName,
104+
lineNumber = lineNumber.toLongOrNull(),
105+
)
106+
ReportFrameBuilder.build(
107+
FrameType.JVM,
108+
builder,
109+
frameData,
110+
)
111+
}
112+
}
113+
114+
isAdditionalStackTraceInfo(currentLine) -> {
115+
// TODO(FranAguilera): BIT-5576. To update underlying Report model to support a Frame Entry for
116+
// details (e..g. sleeping on <0x07849c2b> (a java.lang.Object))
117+
ReportFrameBuilder.build(
118+
FrameType.JVM,
119+
builder,
120+
FrameData(className = currentLine),
121+
)
122+
}
123+
124+
else -> null
125+
}
126+
frame?.let { mainStackTraceFrames.add(it) }
121127
}
122128

129+
private fun isAdditionalStackTraceInfo(currentLine: String): Boolean = !currentLine.contains(ADDITIONAL_THREAD_INFO_IDENTIFIER)
130+
123131
/**
124132
* Based on android source (see frameworks/base/core/java/com/android/internal/os/TimeoutRecord.java)
125133
*/

platform/jvm/capture/src/main/kotlin/io/bitdrift/capture/reports/processor/JvmCrashProcessor.kt

Lines changed: 10 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,9 @@ package io.bitdrift.capture.reports.processor
1010
import com.google.flatbuffers.FlatBufferBuilder
1111
import io.bitdrift.capture.reports.binformat.v1.Error
1212
import io.bitdrift.capture.reports.binformat.v1.ErrorRelation
13-
import io.bitdrift.capture.reports.binformat.v1.Frame
1413
import io.bitdrift.capture.reports.binformat.v1.FrameType
1514
import io.bitdrift.capture.reports.binformat.v1.Report
1615
import io.bitdrift.capture.reports.binformat.v1.ReportType
17-
import io.bitdrift.capture.reports.binformat.v1.SourceFile
1816
import io.bitdrift.capture.reports.binformat.v1.ThreadDetails
1917

2018
/**
@@ -78,32 +76,17 @@ internal object JvmCrashProcessor {
7876
builder: FlatBufferBuilder,
7977
element: StackTraceElement,
8078
): Int {
81-
val sourceFile =
82-
if (element.fileName != null) {
83-
SourceFile.createSourceFile(
84-
builder,
85-
builder.createString(element.fileName),
86-
element.lineNumber.toLong(),
87-
0,
88-
)
89-
} else {
90-
0
91-
}
92-
return Frame.createFrame(
93-
builder,
79+
val frameData =
80+
FrameData(
81+
className = element.className,
82+
symbolName = element.methodName,
83+
fileName = element.fileName,
84+
lineNumber = element.lineNumber.toLong(),
85+
)
86+
return ReportFrameBuilder.build(
9487
FrameType.JVM,
95-
builder.createString(element.className),
96-
builder.createString(element.methodName),
97-
sourceFile,
98-
0,
99-
0u,
100-
0u,
101-
0,
102-
0,
103-
0,
104-
0u,
105-
false,
106-
0,
88+
builder,
89+
frameData,
10790
)
10891
}
10992
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// capture-sdk - bitdrift's client SDK
2+
// Copyright Bitdrift, Inc. All rights reserved.
3+
//
4+
// Use of this source code is governed by a source available license that can be found in the
5+
// LICENSE file or at:
6+
// https://polyformproject.org/wp-content/uploads/2020/06/PolyForm-Shield-1.0.0.txt
7+
8+
package io.bitdrift.capture.reports.processor
9+
10+
import com.google.flatbuffers.FlatBufferBuilder
11+
import io.bitdrift.capture.reports.binformat.v1.Frame
12+
import io.bitdrift.capture.reports.binformat.v1.SourceFile
13+
14+
/**
15+
* A builder that allows you to create a [io.bitdrift.capture.reports.binformat.v1.Frame] with
16+
* the main relevant fields
17+
*/
18+
internal object ReportFrameBuilder {
19+
/**
20+
* Builds a [io.bitdrift.capture.reports.binformat.v1.Frame]
21+
*/
22+
fun build(
23+
frameType: Byte, // e.g. FrameType.JVM
24+
builder: FlatBufferBuilder,
25+
frameData: FrameData,
26+
): Int =
27+
Frame.createFrame(
28+
builder,
29+
frameType,
30+
builder.createString(frameData.className),
31+
frameData.symbolName?.let { builder.createString(it) } ?: 0,
32+
buildSourceFile(builder, frameData.fileName, frameData.lineNumber),
33+
0,
34+
0u,
35+
0u,
36+
0,
37+
0,
38+
0,
39+
0u,
40+
false,
41+
0,
42+
)
43+
44+
private fun buildSourceFile(
45+
builder: FlatBufferBuilder,
46+
fileName: String?,
47+
lineNumber: Long?,
48+
): Int =
49+
if (fileName != null) {
50+
val path = builder.createString(fileName)
51+
SourceFile.createSourceFile(
52+
builder,
53+
path,
54+
lineNumber ?: 0,
55+
0,
56+
)
57+
} else {
58+
0
59+
}
60+
}
61+
62+
internal data class FrameData(
63+
val className: String,
64+
val symbolName: String? = null,
65+
val fileName: String? = null,
66+
val lineNumber: Long? = null,
67+
)

platform/jvm/capture/src/test/kotlin/io/bitdrift/capture/FatalIssueReporterProcessorTest.kt

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,13 +109,20 @@ class FatalIssueReporterProcessorTest {
109109
val error = report.errors(0)!!
110110
assertThat(error.name).isEqualTo("User Perceived ANR")
111111
assertThat(error.reason).isEqualTo(APP_EXIT_DESCRIPTION_ANR)
112+
113+
// Entries below corresponds to sample `app_exit_anr_deadlock_anr.txt`
112114
assertThat(error.stackTrace(0)!!.type).isEqualTo(1)
113115
assertThat(error.stackTrace(0)!!.stateLength).isEqualTo(0)
114-
assertThat(error.stackTrace(0)!!.className).isEqualTo("io.bitdrift.capture.FatalIssueGenerator")
115-
assertThat(error.stackTrace(0)!!.symbolName).isEqualTo("startProcessing")
116-
assertThat(error.stackTrace(0)!!.sourceFile!!.path).isEqualTo("FatalIssueGenerator.kt")
117-
assertThat(error.stackTrace(0)!!.sourceFile!!.line).isEqualTo(106)
118-
assertThat(error.stackTrace(0)!!.sourceFile!!.column).isEqualTo(0)
116+
assertThat(error.stackTrace(0)!!.className).isEqualTo("\"main\" prio=5 tid=1 Blocked")
117+
118+
assertThat(error.stackTrace(1)!!.className).isEqualTo("io.bitdrift.capture.FatalIssueGenerator")
119+
assertThat(error.stackTrace(1)!!.symbolName).isEqualTo("startProcessing")
120+
assertThat(error.stackTrace(1)!!.sourceFile!!.path).isEqualTo("FatalIssueGenerator.kt")
121+
assertThat(error.stackTrace(1)!!.sourceFile!!.line).isEqualTo(106)
122+
assertThat(error.stackTrace(1)!!.sourceFile!!.column).isEqualTo(0)
123+
124+
assertThat(error.stackTrace(2)!!.className)
125+
.isEqualTo(" - waiting to lock <0x0481d03d> (a java.lang.String) held by thread 4")
119126
}
120127

121128
@Test

0 commit comments

Comments
 (0)