Skip to content

Commit 14dea02

Browse files
committed
fix(anr): handle source lines without method, file, or line
1 parent 94dca1d commit 14dea02

File tree

2 files changed

+64
-16
lines changed

2 files changed

+64
-16
lines changed

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

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@ internal object AppExitAnrTraceProcessor {
2828

2929
// matcher for lines like ` at some.pkg.ClassName.doSomething(ClassName.kt:732)`
3030
// * class - fully-qualified class
31-
// * method - method within class
31+
// * method - method, starting with a match for Character.isJavaIdentifierStart() (java spec 3.8)
3232
// * file - class source file
3333
// * line - line number
3434
private val ANR_STACK_TRACE_REGEX =
3535
Regex(
36-
"^\\s+at\\s+(?<class>.+)\\.(?<method>[^.]+)\\((?<file>[^:]+):(?<line>\\d+)\\)\$",
36+
"^\\s+at\\s+(?<class>[.\\w]+?)(?:\\.(?<method>[a-z_\$][\\w\$]*))?(?:\\((?<file>[^:]+)(?::(?<line>\\d+))?\\))?\$",
3737
)
3838
private val mainStackTraceFrames = mutableListOf<Int>()
3939
private var isProcessingMainThreadTrace = false
@@ -96,20 +96,23 @@ internal object AppExitAnrTraceProcessor {
9696
if (!isProcessingMainThreadTrace) {
9797
return
9898
}
99-
val path = builder.createString(fileName)
10099
val sourceFile =
101-
SourceFile.createSourceFile(
102-
builder,
103-
path,
104-
lineNumber.toLongOrNull() ?: 0,
105-
0,
106-
)
100+
if (fileName.isEmpty()) {
101+
0
102+
} else {
103+
SourceFile.createSourceFile(
104+
builder,
105+
builder.createString(fileName),
106+
lineNumber.toLongOrNull() ?: 0,
107+
0,
108+
)
109+
}
107110
val frame =
108111
Frame.createFrame(
109112
builder,
110113
FrameType.JVM,
111114
builder.createString(className),
112-
builder.createString(symbolName),
115+
if (symbolName.isEmpty()) 0 else builder.createString(symbolName),
113116
sourceFile,
114117
0,
115118
0u,

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

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -279,12 +279,57 @@ class FatalIssueReporterProcessorTest {
279279
assertThat(report.errors(0)).isNotNull
280280
report.errors(0)?.let { error ->
281281
assertThat(error.name).isEqualTo(expectedMessage)
282-
val frame = error.stackTrace(0)
283-
assertThat(frame).isNotNull
284-
assertThat(frame!!.sourceFile!!.line).isEqualTo(106)
285-
assertThat(frame.sourceFile!!.path).isEqualTo("FatalIssueGenerator.kt")
286-
assertThat(frame.symbolName).isEqualTo("startProcessing")
287-
assertThat(frame.className).isEqualTo("io.bitdrift.capture.FatalIssueGenerator")
282+
val firstFrame = error.stackTrace(0)
283+
assertThat(firstFrame).isNotNull
284+
firstFrame?.let { frame ->
285+
assertThat(frame.sourceFile!!.line).isEqualTo(106)
286+
assertThat(frame.sourceFile!!.path).isEqualTo("FatalIssueGenerator.kt")
287+
assertThat(frame.symbolName).isEqualTo("startProcessing")
288+
assertThat(frame.className).isEqualTo("io.bitdrift.capture.FatalIssueGenerator")
289+
}
290+
291+
val secondFrame = error.stackTrace(1)
292+
assertThat(secondFrame).isNotNull
293+
secondFrame?.let { frame ->
294+
assertThat(frame.sourceFile!!.line).isEqualTo(35)
295+
assertThat(frame.sourceFile!!.path).isEqualTo("FatalIssueGenerator.kt")
296+
assertThat(frame.symbolName).isEqualTo("forceDeadlockAnr\$")
297+
assertThat(frame.className).isEqualTo("io.bitdrift.capture.FatalIssueGenerator")
298+
}
299+
300+
val unavailableFrame = error.stackTrace(2)
301+
assertThat(unavailableFrame).isNotNull
302+
unavailableFrame?.let { frame ->
303+
assertThat(frame.sourceFile!!.line).isEqualTo(0)
304+
assertThat(frame.sourceFile!!.path).isEqualTo("unavailable")
305+
assertThat(frame.symbolName).isNull()
306+
assertThat(frame.className).isEqualTo("io.bitdrift.capture.FatalIssueGenerator")
307+
}
308+
309+
val noMethodNoSourceFrame = error.stackTrace(3)
310+
assertThat(noMethodNoSourceFrame).isNotNull
311+
noMethodNoSourceFrame?.let { frame ->
312+
assertThat(frame.sourceFile).isNull()
313+
assertThat(frame.symbolName).isNull()
314+
assertThat(frame.className).isEqualTo("io.bitdrift.capture.FatalIssueGenerator")
315+
}
316+
317+
val unsourcedFrame = error.stackTrace(4)
318+
assertThat(unsourcedFrame).isNotNull
319+
unsourcedFrame?.let { frame ->
320+
assertThat(frame.sourceFile).isNull()
321+
assertThat(frame.symbolName).isEqualTo("callOnMainThread")
322+
assertThat(frame.className).isEqualTo("io.bitdrift.capture.FatalIssueGenerator")
323+
}
324+
325+
val nativeFrame = error.stackTrace(12)
326+
assertThat(nativeFrame).isNotNull
327+
nativeFrame?.let { frame ->
328+
assertThat(frame.sourceFile!!.line).isEqualTo(0)
329+
assertThat(frame.sourceFile!!.path).isEqualTo("Native method")
330+
assertThat(frame.symbolName).isEqualTo("invoke")
331+
assertThat(frame.className).isEqualTo("java.lang.reflect.Method")
332+
}
288333
}
289334
}
290335

0 commit comments

Comments
 (0)