Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix native-only thread dump parsing and tracingContext for ANRv2 #2839

Merged
merged 5 commits into from
Jul 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## Unreleased

### Fixes

- Fix ANRv2 thread dump parsing for native-only threads ([#2839](https://github.com/getsentry/sentry-java/pull/2839))
- Derive `TracingContext` values from event for ANRv2 events ([#2839](https://github.com/getsentry/sentry-java/pull/2839))

## 6.25.2

### Fixes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,31 +38,35 @@
public class ThreadDumpParser {
private static final Pattern BEGIN_MANAGED_THREAD_RE =
Pattern.compile("\"(.*)\" (.*) ?prio=(\\d+)\\s+tid=(\\d+)\\s*(.*)");

private static final Pattern BEGIN_UNMANAGED_NATIVE_THREAD_RE =
Pattern.compile("\"(.*)\" (.*) ?sysTid=(\\d+)");

private static final Pattern NATIVE_RE =
Pattern.compile(
" (?:native: )?#\\d+ \\S+ [0-9a-fA-F]+\\s+(.*?)\\s+\\((.*)\\+(\\d+)\\)(?: \\(.*\\))?");
" *(?:native: )?#\\d+ \\S+ [0-9a-fA-F]+\\s+(.*?)\\s+\\((.*)\\+(\\d+)\\)(?: \\(.*\\))?");
private static final Pattern NATIVE_NO_LOC_RE =
Pattern.compile(
" (?:native: )?#\\d+ \\S+ [0-9a-fA-F]+\\s+(.*)\\s*\\(?(.*)\\)?(?: \\(.*\\))?");
" *(?:native: )?#\\d+ \\S+ [0-9a-fA-F]+\\s+(.*)\\s*\\(?(.*)\\)?(?: \\(.*\\))?");
private static final Pattern JAVA_RE =
Pattern.compile(" at (?:(.+)\\.)?([^.]+)\\.([^.]+)\\((.*):([\\d-]+)\\)");
Pattern.compile(" *at (?:(.+)\\.)?([^.]+)\\.([^.]+)\\((.*):([\\d-]+)\\)");
private static final Pattern JNI_RE =
Pattern.compile(" at (?:(.+)\\.)?([^.]+)\\.([^.]+)\\(Native method\\)");
Pattern.compile(" *at (?:(.+)\\.)?([^.]+)\\.([^.]+)\\(Native method\\)");
private static final Pattern LOCKED_RE =
Pattern.compile(" - locked \\<([0x0-9a-fA-F]{1,16})\\> \\(a (?:(.+)\\.)?([^.]+)\\)");
Pattern.compile(" *- locked \\<([0x0-9a-fA-F]{1,16})\\> \\(a (?:(.+)\\.)?([^.]+)\\)");
private static final Pattern SLEEPING_ON_RE =
Pattern.compile(" - sleeping on \\<([0x0-9a-fA-F]{1,16})\\> \\(a (?:(.+)\\.)?([^.]+)\\)");
Pattern.compile(" *- sleeping on \\<([0x0-9a-fA-F]{1,16})\\> \\(a (?:(.+)\\.)?([^.]+)\\)");
private static final Pattern WAITING_ON_RE =
Pattern.compile(" - waiting on \\<([0x0-9a-fA-F]{1,16})\\> \\(a (?:(.+)\\.)?([^.]+)\\)");
Pattern.compile(" *- waiting on \\<([0x0-9a-fA-F]{1,16})\\> \\(a (?:(.+)\\.)?([^.]+)\\)");
private static final Pattern WAITING_TO_LOCK_RE =
Pattern.compile(
" - waiting to lock \\<([0x0-9a-fA-F]{1,16})\\> \\(a (?:(.+)\\.)?([^.]+)\\)");
" *- waiting to lock \\<([0x0-9a-fA-F]{1,16})\\> \\(a (?:(.+)\\.)?([^.]+)\\)");
private static final Pattern WAITING_TO_LOCK_HELD_RE =
Pattern.compile(
" - waiting to lock \\<([0x0-9a-fA-F]{1,16})\\> \\(a (?:(.+)\\.)?([^.]+)\\)"
" *- waiting to lock \\<([0x0-9a-fA-F]{1,16})\\> \\(a (?:(.+)\\.)?([^.]+)\\)"
+ "(?: held by thread (\\d+))");
private static final Pattern WAITING_TO_LOCK_UNKNOWN_RE =
Pattern.compile(" - waiting to lock an unknown object");
Pattern.compile(" *- waiting to lock an unknown object");
private static final Pattern BLANK_RE = Pattern.compile("\\s+");

private final @NotNull SentryOptions options;
Expand All @@ -82,6 +86,7 @@ public List<SentryThread> parse(final @NotNull Lines lines) {
final List<SentryThread> sentryThreads = new ArrayList<>();

final Matcher beginManagedThreadRe = BEGIN_MANAGED_THREAD_RE.matcher("");
final Matcher beginUnmanagedNativeThreadRe = BEGIN_UNMANAGED_NATIVE_THREAD_RE.matcher("");

while (lines.hasNext()) {
final Line line = lines.next();
Expand All @@ -92,7 +97,7 @@ public List<SentryThread> parse(final @NotNull Lines lines) {
final String text = line.text;
// we only handle managed threads, as unmanaged/not attached do not have the thread id and
// our protocol does not support this case
if (matches(beginManagedThreadRe, text)) {
if (matches(beginManagedThreadRe, text) || matches(beginUnmanagedNativeThreadRe, text)) {
lines.rewind();

final SentryThread thread = parseThread(lines);
Expand All @@ -108,6 +113,7 @@ private SentryThread parseThread(final @NotNull Lines lines) {
final SentryThread sentryThread = new SentryThread();

final Matcher beginManagedThreadRe = BEGIN_MANAGED_THREAD_RE.matcher("");
final Matcher beginUnmanagedNativeThreadRe = BEGIN_UNMANAGED_NATIVE_THREAD_RE.matcher("");

// thread attributes
if (!lines.hasNext()) {
Expand Down Expand Up @@ -137,14 +143,24 @@ private SentryThread parseThread(final @NotNull Lines lines) {
sentryThread.setState(state);
}
}
final String threadName = sentryThread.getName();
if (threadName != null) {
final boolean isMain = threadName.equals("main");
sentryThread.setMain(isMain);
// since it's an ANR, the crashed thread will always be main
sentryThread.setCrashed(isMain);
sentryThread.setCurrent(isMain && !isBackground);
} else if (matches(beginUnmanagedNativeThreadRe, line.text)) {
final Long sysTid = getLong(beginUnmanagedNativeThreadRe, 3, null);
if (sysTid == null) {
options.getLogger().log(SentryLevel.DEBUG, "No thread id in the dump, skipping thread.");
// tid is required by our protocol
return null;
}
sentryThread.setId(sysTid);
sentryThread.setName(beginUnmanagedNativeThreadRe.group(1));
}

final String threadName = sentryThread.getName();
if (threadName != null) {
final boolean isMain = threadName.equals("main");
sentryThread.setMain(isMain);
// since it's an ANR, the crashed thread will always be main
sentryThread.setCrashed(isMain);
sentryThread.setCurrent(isMain && !isBackground);
}

// thread stacktrace
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import java.io.File
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull

class ThreadDumpParserTest {

Expand Down Expand Up @@ -72,4 +73,26 @@ class ThreadDumpParserTest {
assertEquals(67, firstFrame.lineno)
assertEquals(null, firstFrame.isInApp)
}

@Test
fun `parses native only thread dump`() {
val lines = Lines.readLines(File("src/test/resources/thread_dump_native_only.txt"))
val parser = ThreadDumpParser(
SentryOptions().apply { addInAppInclude("io.sentry.samples") },
false
)
val threads = parser.parse(lines)
// just verifying a few important threads, as there are many
val thread = threads.find { it.name == "samples.android" }
assertEquals(9955, thread!!.id)
assertNull(thread.state)
assertEquals(false, thread.isCrashed)
assertEquals(false, thread.isMain)
assertEquals(false, thread.isCurrent)
val lastFrame = thread.stacktrace!!.frames!!.last()
assertEquals("/apex/com.android.runtime/lib64/bionic/libc.so", lastFrame.`package`)
assertEquals("syscall", lastFrame.function)
assertEquals(28, lastFrame.lineno)
assertNull(lastFrame.isInApp)
}
}
Loading