diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AnrV2EventProcessor.java b/sentry-android-core/src/main/java/io/sentry/android/core/AnrV2EventProcessor.java index 4a070786a49..7f338af54ce 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AnrV2EventProcessor.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AnrV2EventProcessor.java @@ -36,15 +36,19 @@ import io.sentry.android.core.internal.util.CpuInfoUtils; import io.sentry.cache.PersistingOptionsObserver; import io.sentry.cache.PersistingScopeObserver; +import io.sentry.hints.AbnormalExit; import io.sentry.hints.Backfillable; import io.sentry.protocol.App; import io.sentry.protocol.Contexts; import io.sentry.protocol.DebugImage; import io.sentry.protocol.DebugMeta; import io.sentry.protocol.Device; +import io.sentry.protocol.Mechanism; import io.sentry.protocol.OperatingSystem; import io.sentry.protocol.Request; import io.sentry.protocol.SdkVersion; +import io.sentry.protocol.SentryStackTrace; +import io.sentry.protocol.SentryThread; import io.sentry.protocol.User; import io.sentry.util.HintUtils; import java.util.ArrayList; @@ -88,8 +92,7 @@ public AnrV2EventProcessor( this.buildInfoProvider = buildInfoProvider; final SentryStackTraceFactory sentryStackTraceFactory = - new SentryStackTraceFactory( - this.options.getInAppExcludes(), this.options.getInAppIncludes()); + new SentryStackTraceFactory(this.options); sentryExceptionFactory = new SentryExceptionFactory(sentryStackTraceFactory); } @@ -109,7 +112,7 @@ public AnrV2EventProcessor( // we always set exception values, platform, os and device even if the ANR is not enrich-able // even though the OS context may change in the meantime (OS update), we consider this an // edge-case - setExceptions(event); + setExceptions(event, unwrappedHint); setPlatform(event); mergeOS(event); setDevice(event); @@ -125,7 +128,7 @@ public AnrV2EventProcessor( backfillScope(event); - backfillOptions(event); + backfillOptions(event, unwrappedHint); setStaticValues(event); @@ -264,22 +267,26 @@ private void setRequest(final @NotNull SentryBaseEvent event) { // endregion // region options persisted values - private void backfillOptions(final @NotNull SentryEvent event) { + private void backfillOptions(final @NotNull SentryEvent event, final @NotNull Object hint) { setRelease(event); setEnvironment(event); setDist(event); setDebugMeta(event); setSdk(event); - setApp(event); + setApp(event, hint); setOptionsTags(event); } - private void setApp(final @NotNull SentryBaseEvent event) { + private void setApp(final @NotNull SentryBaseEvent event, final @NotNull Object hint) { App app = event.getContexts().getApp(); if (app == null) { app = new App(); } app.setAppName(ContextUtils.getApplicationName(context, options.getLogger())); + // TODO: not entirely correct, because we define background ANRs as not the ones of + // IMPORTANCE_FOREGROUND, but this doesn't mean the app was in foreground when an ANR happened + // but it's our best effort for now. We could serialize AppState in theory. + app.setInForeground(!isBackgroundAnr(hint)); final PackageInfo packageInfo = ContextUtils.getPackageInfo(context, options.getLogger(), buildInfoProvider); @@ -339,10 +346,12 @@ private void setDebugMeta(final @NotNull SentryBaseEvent event) { final String proguardUuid = PersistingOptionsObserver.read(options, PROGUARD_UUID_FILENAME, String.class); - final DebugImage debugImage = new DebugImage(); - debugImage.setType(DebugImage.PROGUARD); - debugImage.setUuid(proguardUuid); - images.add(debugImage); + if (proguardUuid != null) { + final DebugImage debugImage = new DebugImage(); + debugImage.setType(DebugImage.PROGUARD); + debugImage.setUuid(proguardUuid); + images.add(debugImage); + } event.setDebugMeta(debugMeta); } } @@ -411,11 +420,51 @@ private void setPlatform(final @NotNull SentryBaseEvent event) { } } - private void setExceptions(final @NotNull SentryEvent event) { - final Throwable throwable = event.getThrowableMechanism(); - if (throwable != null) { - event.setExceptions(sentryExceptionFactory.getSentryExceptions(throwable)); + @Nullable + private SentryThread findMainThread(final @Nullable List threads) { + if (threads != null) { + for (SentryThread thread : threads) { + final String name = thread.getName(); + if (name != null && name.equals("main")) { + return thread; + } + } + } + return null; + } + + // by default we assume that the ANR is foreground, unless abnormalMechanism is "anr_background" + private boolean isBackgroundAnr(final @NotNull Object hint) { + if (hint instanceof AbnormalExit) { + final String abnormalMechanism = ((AbnormalExit) hint).mechanism(); + return "anr_background".equals(abnormalMechanism); + } + return false; + } + + private void setExceptions(final @NotNull SentryEvent event, final @NotNull Object hint) { + // AnrV2 threads contain a thread dump from the OS, so we just search for the main thread dump + // and make an exception out of its stacktrace + final Mechanism mechanism = new Mechanism(); + mechanism.setType("AppExitInfo"); + + final boolean isBackgroundAnr = isBackgroundAnr(hint); + String message = "ANR"; + if (isBackgroundAnr) { + message = "Background " + message; + } + final ApplicationNotResponding anr = + new ApplicationNotResponding(message, Thread.currentThread()); + + SentryThread mainThread = findMainThread(event.getThreads()); + if (mainThread == null) { + // if there's no main thread in the event threads, we just create a dummy thread so the + // exception is properly created as well, but without stacktrace + mainThread = new SentryThread(); + mainThread.setStacktrace(new SentryStackTrace()); } + event.setExceptions( + sentryExceptionFactory.getSentryExceptionsFromThread(mainThread, mechanism, anr)); } private void mergeUser(final @NotNull SentryBaseEvent event) { diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/AnrV2Integration.java b/sentry-android-core/src/main/java/io/sentry/android/core/AnrV2Integration.java index f56c6a26cd5..afbcfc6c0ca 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/AnrV2Integration.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/AnrV2Integration.java @@ -4,7 +4,6 @@ import android.app.ActivityManager; import android.app.ApplicationExitInfo; import android.content.Context; -import android.os.Looper; import io.sentry.DateUtils; import io.sentry.Hint; import io.sentry.IHub; @@ -14,20 +13,23 @@ import io.sentry.SentryLevel; import io.sentry.SentryOptions; import io.sentry.android.core.cache.AndroidEnvelopeCache; +import io.sentry.android.core.internal.threaddump.Lines; +import io.sentry.android.core.internal.threaddump.ThreadDumpParser; import io.sentry.cache.EnvelopeCache; import io.sentry.cache.IEnvelopeCache; -import io.sentry.exception.ExceptionMechanismException; import io.sentry.hints.AbnormalExit; import io.sentry.hints.Backfillable; import io.sentry.hints.BlockingFlushHint; -import io.sentry.protocol.Mechanism; import io.sentry.protocol.SentryId; +import io.sentry.protocol.SentryThread; import io.sentry.transport.CurrentDateProvider; import io.sentry.transport.ICurrentDateProvider; import io.sentry.util.HintUtils; import io.sentry.util.Objects; +import java.io.BufferedReader; import java.io.Closeable; import java.io.IOException; +import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -221,7 +223,8 @@ private void reportAsSentryEvent( final long anrTimestamp = exitInfo.getTimestamp(); final boolean isBackground = exitInfo.getImportance() != ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; - final Throwable anrThrowable = buildAnrThrowable(exitInfo, isBackground); + + final List threads = parseThreadDump(exitInfo, isBackground); final AnrV2Hint anrHint = new AnrV2Hint( options.getFlushTimeoutMillis(), @@ -232,7 +235,8 @@ private void reportAsSentryEvent( final Hint hint = HintUtils.createWithTypeCheckHint(anrHint); - final SentryEvent event = new SentryEvent(anrThrowable); + final SentryEvent event = new SentryEvent(); + event.setThreads(threads); event.setTimestamp(DateUtils.getDateTime(anrTimestamp)); event.setLevel(SentryLevel.FATAL); @@ -251,20 +255,20 @@ private void reportAsSentryEvent( } } - private @NotNull Throwable buildAnrThrowable( + private @Nullable List parseThreadDump( final @NotNull ApplicationExitInfo exitInfo, final boolean isBackground) { - String message = "ANR"; - if (isBackground) { - message = "Background " + message; + List threads = null; + try (final BufferedReader reader = + new BufferedReader(new InputStreamReader(exitInfo.getTraceInputStream()))) { + final Lines lines = Lines.readLines(reader); + + final ThreadDumpParser threadDumpParser = new ThreadDumpParser(options, isBackground); + threads = threadDumpParser.parse(lines); + } catch (Throwable e) { + options.getLogger().log(SentryLevel.WARNING, "Failed to parse ANR thread dump", e); } - // TODO: here we should actually parse the trace file and extract the thread dump from there - // and then we could properly get the main thread stracktrace and construct a proper exception - final ApplicationNotResponding error = - new ApplicationNotResponding(message, Looper.getMainLooper().getThread()); - final Mechanism mechanism = new Mechanism(); - mechanism.setType("ANRv2"); - return new ExceptionMechanismException(mechanism, error, error.getThread(), true); + return threads; } } diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/threaddump/Line.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/threaddump/Line.java new file mode 100644 index 00000000000..452c481ddc3 --- /dev/null +++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/threaddump/Line.java @@ -0,0 +1,33 @@ +/* + * Adapted from https://cs.android.com/android/platform/superproject/+/master:development/tools/bugreport/src/com/android/bugreport/util/Line.java + * + * Copyright (C) 2016 The Android Open Source Project + * + * 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 + * + * http://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 io.sentry.android.core.internal.threaddump; + +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; + +@ApiStatus.Internal +public final class Line { + public int lineno; + public @NotNull String text; + + public Line(final int lineno, final @NotNull String text) { + this.lineno = lineno; + this.text = text; + } +} diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/threaddump/Lines.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/threaddump/Lines.java new file mode 100644 index 00000000000..5c314e1b23f --- /dev/null +++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/threaddump/Lines.java @@ -0,0 +1,92 @@ +/* + * Adapted from https://cs.android.com/android/platform/superproject/+/master:development/tools/bugreport/src/com/android/bugreport/util/Lines.java + * + * Copyright (C) 2016 The Android Open Source Project + * + * 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 + * + * http://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 io.sentry.android.core.internal.threaddump; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.util.ArrayList; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A stream of parsed lines. Can be rewound, and sub-regions cloned for recursive descent parsing. + */ +@ApiStatus.Internal +public final class Lines { + private final @NotNull ArrayList mList; + private final int mMin; + private final int mMax; + + /** The read position inside the list. */ + public int pos; + + /** Read the whole file into a Lines object. */ + public static Lines readLines(final @NotNull File file) throws IOException { + try (BufferedReader reader = new BufferedReader(new FileReader(file))) { + return Lines.readLines(reader); + } + } + + /** Read the whole file into a Lines object. */ + public static Lines readLines(final @NotNull BufferedReader in) throws IOException { + final ArrayList list = new ArrayList<>(); + + int lineno = 0; + String text; + while ((text = in.readLine()) != null) { + lineno++; + list.add(new Line(lineno, text)); + } + + return new Lines(list); + } + + /** Construct with a list of lines. */ + public Lines(final @NotNull ArrayList list) { + this.mList = list; + mMin = 0; + mMax = mList.size(); + } + + /** If there are more lines to read within the current range. */ + public boolean hasNext() { + return pos < mMax; + } + + /** + * Return the next line, or null if there are no more lines to read. Also returns null in the + * error condition where pos is before the beginning. + */ + @Nullable + public Line next() { + if (pos >= mMin && pos < mMax) { + return this.mList.get(pos++); + } else { + return null; + } + } + + /** Move the read position back by one line. */ + public void rewind() { + pos--; + } +} diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/internal/threaddump/ThreadDumpParser.java b/sentry-android-core/src/main/java/io/sentry/android/core/internal/threaddump/ThreadDumpParser.java new file mode 100644 index 00000000000..1f4b82d0e35 --- /dev/null +++ b/sentry-android-core/src/main/java/io/sentry/android/core/internal/threaddump/ThreadDumpParser.java @@ -0,0 +1,333 @@ +/* + * Adapted from https://cs.android.com/android/platform/superproject/+/master:development/tools/bugreport/src/com/android/bugreport/stacks/ThreadSnapshotParser.java + * + * Copyright (C) 2016 The Android Open Source Project + * + * 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 + * + * http://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 io.sentry.android.core.internal.threaddump; + +import io.sentry.SentryLevel; +import io.sentry.SentryLockReason; +import io.sentry.SentryOptions; +import io.sentry.SentryStackTraceFactory; +import io.sentry.protocol.SentryStackFrame; +import io.sentry.protocol.SentryStackTrace; +import io.sentry.protocol.SentryThread; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class ThreadDumpParser { + private static final Pattern BEGIN_MANAGED_THREAD_RE = + Pattern.compile("\"(.*)\" (.*) ?prio=(\\d+)\\s+tid=(\\d+)\\s*(.*)"); + private static final Pattern NATIVE_RE = + Pattern.compile(" (?: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*\\(?(.*)\\)?"); + private static final Pattern JAVA_RE = + Pattern.compile(" at (?:(.+)\\.)?([^.]+)\\.([^.]+)\\((.*):([\\d-]+)\\)"); + private static final Pattern JNI_RE = + Pattern.compile(" at (?:(.+)\\.)?([^.]+)\\.([^.]+)\\(Native method\\)"); + private static final Pattern LOCKED_RE = + 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 (?:(.+)\\.)?([^.]+)\\)"); + private static final Pattern WAITING_ON_RE = + 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 (?:(.+)\\.)?([^.]+)\\)"); + private static final Pattern WAITING_TO_LOCK_HELD_RE = + Pattern.compile( + " - 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"); + private static final Pattern BLANK_RE = Pattern.compile("\\s+"); + + private final @NotNull SentryOptions options; + + private final boolean isBackground; + + private final @NotNull SentryStackTraceFactory stackTraceFactory; + + public ThreadDumpParser(final @NotNull SentryOptions options, final boolean isBackground) { + this.options = options; + this.isBackground = isBackground; + this.stackTraceFactory = new SentryStackTraceFactory(options); + } + + @NotNull + public List parse(final @NotNull Lines lines) { + final List sentryThreads = new ArrayList<>(); + + final Matcher beginManagedThreadRe = BEGIN_MANAGED_THREAD_RE.matcher(""); + + while (lines.hasNext()) { + final Line line = lines.next(); + if (line == null) { + options.getLogger().log(SentryLevel.WARNING, "Internal error while parsing thread dump."); + return sentryThreads; + } + 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)) { + lines.rewind(); + + final SentryThread thread = parseThread(lines); + if (thread != null) { + sentryThreads.add(thread); + } + } + } + return sentryThreads; + } + + private SentryThread parseThread(final @NotNull Lines lines) { + final SentryThread sentryThread = new SentryThread(); + + final Matcher beginManagedThreadRe = BEGIN_MANAGED_THREAD_RE.matcher(""); + + // thread attributes + if (!lines.hasNext()) { + return null; + } + final Line line = lines.next(); + if (line == null) { + options.getLogger().log(SentryLevel.WARNING, "Internal error while parsing thread dump."); + return null; + } + if (matches(beginManagedThreadRe, line.text)) { + final Long tid = getLong(beginManagedThreadRe, 4, null); + if (tid == null) { + options.getLogger().log(SentryLevel.DEBUG, "No thread id in the dump, skipping thread."); + // tid is required by our protocol + return null; + } + sentryThread.setId(tid); + sentryThread.setName(beginManagedThreadRe.group(1)); + final String state = beginManagedThreadRe.group(5); + // sanitizing thread that have more details after their actual state, e.g. + // "Native (still starting up)" <- we just need "Native" here + if (state != null) { + if (state.contains(" ")) { + sentryThread.setState(state.substring(0, state.indexOf(' '))); + } else { + 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); + } + } + + // thread stacktrace + final SentryStackTrace stackTrace = parseStacktrace(lines, sentryThread); + sentryThread.setStacktrace(stackTrace); + return sentryThread; + } + + @NotNull + private SentryStackTrace parseStacktrace( + final @NotNull Lines lines, final @NotNull SentryThread thread) { + final List frames = new ArrayList<>(); + boolean isLastFrameJava = false; + + final Matcher nativeRe = NATIVE_RE.matcher(""); + final Matcher nativeNoLocRe = NATIVE_NO_LOC_RE.matcher(""); + final Matcher javaRe = JAVA_RE.matcher(""); + final Matcher jniRe = JNI_RE.matcher(""); + final Matcher lockedRe = LOCKED_RE.matcher(""); + final Matcher waitingOnRe = WAITING_ON_RE.matcher(""); + final Matcher sleepingOnRe = SLEEPING_ON_RE.matcher(""); + final Matcher waitingToLockHeldRe = WAITING_TO_LOCK_HELD_RE.matcher(""); + final Matcher waitingToLockRe = WAITING_TO_LOCK_RE.matcher(""); + final Matcher waitingToLockUnknownRe = WAITING_TO_LOCK_UNKNOWN_RE.matcher(""); + final Matcher blankRe = BLANK_RE.matcher(""); + + while (lines.hasNext()) { + final Line line = lines.next(); + if (line == null) { + options.getLogger().log(SentryLevel.WARNING, "Internal error while parsing thread dump."); + break; + } + final String text = line.text; + if (matches(nativeRe, text)) { + final SentryStackFrame frame = new SentryStackFrame(); + frame.setPackage(nativeRe.group(1)); + frame.setSymbol(nativeRe.group(2)); + frame.setLineno(getInteger(nativeRe, 3, null)); + frames.add(frame); + isLastFrameJava = false; + } else if (matches(nativeNoLocRe, text)) { + final SentryStackFrame frame = new SentryStackFrame(); + frame.setPackage(nativeNoLocRe.group(1)); + frame.setSymbol(nativeNoLocRe.group(2)); + frames.add(frame); + isLastFrameJava = false; + } else if (matches(javaRe, text)) { + final SentryStackFrame frame = new SentryStackFrame(); + final String packageName = javaRe.group(1); + final String className = javaRe.group(2); + final String module = String.format("%s.%s", packageName, className); + frame.setModule(module); + frame.setFunction(javaRe.group(3)); + frame.setFilename(javaRe.group(4)); + frame.setLineno(getUInteger(javaRe, 5, null)); + frame.setInApp(stackTraceFactory.isInApp(module)); + frames.add(frame); + isLastFrameJava = true; + } else if (matches(jniRe, text)) { + final SentryStackFrame frame = new SentryStackFrame(); + final String packageName = jniRe.group(1); + final String className = jniRe.group(2); + final String module = String.format("%s.%s", packageName, className); + frame.setModule(module); + frame.setFunction(jniRe.group(3)); + frame.setInApp(stackTraceFactory.isInApp(module)); + frames.add(frame); + isLastFrameJava = true; + } else if (matches(lockedRe, text)) { + if (isLastFrameJava) { + final SentryLockReason lock = new SentryLockReason(); + lock.setType(SentryLockReason.LOCKED); + lock.setAddress(lockedRe.group(1)); + lock.setPackageName(lockedRe.group(2)); + lock.setClassName(lockedRe.group(3)); + combineThreadLocks(thread, lock); + } + } else if (matches(waitingOnRe, text)) { + if (isLastFrameJava) { + final SentryLockReason lock = new SentryLockReason(); + lock.setType(SentryLockReason.WAITING); + lock.setAddress(waitingOnRe.group(1)); + lock.setPackageName(waitingOnRe.group(2)); + lock.setClassName(waitingOnRe.group(3)); + combineThreadLocks(thread, lock); + } + } else if (matches(sleepingOnRe, text)) { + if (isLastFrameJava) { + final SentryLockReason lock = new SentryLockReason(); + lock.setType(SentryLockReason.SLEEPING); + lock.setAddress(sleepingOnRe.group(1)); + lock.setPackageName(sleepingOnRe.group(2)); + lock.setClassName(sleepingOnRe.group(3)); + combineThreadLocks(thread, lock); + } + } else if (matches(waitingToLockHeldRe, text)) { + if (isLastFrameJava) { + final SentryLockReason lock = new SentryLockReason(); + lock.setType(SentryLockReason.BLOCKED); + lock.setAddress(waitingToLockHeldRe.group(1)); + lock.setPackageName(waitingToLockHeldRe.group(2)); + lock.setClassName(waitingToLockHeldRe.group(3)); + lock.setThreadId(getLong(waitingToLockHeldRe, 4, null)); + combineThreadLocks(thread, lock); + } + } else if (matches(waitingToLockRe, text)) { + if (isLastFrameJava) { + final SentryLockReason lock = new SentryLockReason(); + lock.setType(SentryLockReason.BLOCKED); + lock.setAddress(waitingToLockRe.group(1)); + lock.setPackageName(waitingToLockRe.group(2)); + lock.setClassName(waitingToLockRe.group(3)); + combineThreadLocks(thread, lock); + } + } else if (matches(waitingToLockUnknownRe, text)) { + if (isLastFrameJava) { + final SentryLockReason lock = new SentryLockReason(); + lock.setType(SentryLockReason.BLOCKED); + combineThreadLocks(thread, lock); + } + } else if (text.length() == 0 || matches(blankRe, text)) { + break; + } + } + + // Sentry expects frames to be in reverse order + Collections.reverse(frames); + final SentryStackTrace stackTrace = new SentryStackTrace(frames); + // it's a thread dump + stackTrace.setSnapshot(true); + return stackTrace; + } + + private boolean matches(final @NotNull Matcher matcher, final @NotNull String text) { + matcher.reset(text); + return matcher.matches(); + } + + private void combineThreadLocks( + final @NotNull SentryThread thread, final @NotNull SentryLockReason lockReason) { + Map heldLocks = thread.getHeldLocks(); + if (heldLocks == null) { + heldLocks = new HashMap<>(); + } + final SentryLockReason prev = heldLocks.get(lockReason.getAddress()); + if (prev != null) { + // higher type prevails as we are tagging thread with the most severe lock reason + prev.setType(Math.max(prev.getType(), lockReason.getType())); + } else { + heldLocks.put(lockReason.getAddress(), new SentryLockReason(lockReason)); + } + thread.setHeldLocks(heldLocks); + } + + @Nullable + private Long getLong( + final @NotNull Matcher matcher, final int group, final @Nullable Long defaultValue) { + final String str = matcher.group(group); + if (str == null || str.length() == 0) { + return defaultValue; + } else { + return Long.parseLong(str); + } + } + + @Nullable + private Integer getInteger( + final @NotNull Matcher matcher, final int group, final @Nullable Integer defaultValue) { + final String str = matcher.group(group); + if (str == null || str.length() == 0) { + return defaultValue; + } else { + return Integer.parseInt(str); + } + } + + @Nullable + private Integer getUInteger( + final @NotNull Matcher matcher, final int group, final @Nullable Integer defaultValue) { + final String str = matcher.group(group); + if (str == null || str.length() == 0) { + return defaultValue; + } else { + final Integer parsed = Integer.parseInt(str); + return parsed >= 0 ? parsed : defaultValue; + } + } +} diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/AnrV2EventProcessorTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AnrV2EventProcessorTest.kt index 96cbba43fe9..a52aee527a6 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/AnrV2EventProcessorTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/AnrV2EventProcessorTest.kt @@ -32,6 +32,7 @@ import io.sentry.cache.PersistingScopeObserver.TAGS_FILENAME import io.sentry.cache.PersistingScopeObserver.TRACE_FILENAME import io.sentry.cache.PersistingScopeObserver.TRANSACTION_FILENAME import io.sentry.cache.PersistingScopeObserver.USER_FILENAME +import io.sentry.hints.AbnormalExit import io.sentry.hints.Backfillable import io.sentry.protocol.Browser import io.sentry.protocol.Contexts @@ -42,6 +43,9 @@ import io.sentry.protocol.OperatingSystem import io.sentry.protocol.Request import io.sentry.protocol.Response import io.sentry.protocol.SdkVersion +import io.sentry.protocol.SentryStackFrame +import io.sentry.protocol.SentryStackTrace +import io.sentry.protocol.SentryThread import io.sentry.protocol.User import io.sentry.util.HintUtils import org.junit.Rule @@ -61,6 +65,7 @@ import kotlin.test.assertFalse import kotlin.test.assertNotNull import kotlin.test.assertNull import kotlin.test.assertSame +import kotlin.test.assertTrue @RunWith(AndroidJUnit4::class) class AnrV2EventProcessorTest { @@ -301,6 +306,7 @@ class AnrV2EventProcessorTest { assertEquals("io.sentry.android.core.test", processed.contexts.app!!.appName) assertEquals("1.2.0", processed.contexts.app!!.appVersion) assertEquals("232", processed.contexts.app!!.appBuild) + assertEquals(true, processed.contexts.app!!.inForeground) // tags assertEquals("tag", processed.tags!!["option"]) } @@ -316,8 +322,8 @@ class AnrV2EventProcessorTest { val processed = processor.process(original, hint) assertEquals("io.sentry.samples", processed!!.release) - assertNull(processed!!.contexts.app!!.appVersion) - assertNull(processed!!.contexts.app!!.appBuild) + assertNull(processed.contexts.app!!.appVersion) + assertNull(processed.contexts.app!!.appBuild) } @Test @@ -412,6 +418,102 @@ class AnrV2EventProcessorTest { assertEquals("uuid", processed.debugMeta!!.images!![1].uuid) } + @Test + fun `when proguard uuid is not persisted, does not add to debug meta`() { + val hint = HintUtils.createWithTypeCheckHint(BackfillableHint()) + + val processed = processEvent(hint, populateOptionsCache = false) + + // proguard uuid + assertTrue(processed.debugMeta!!.images!!.isEmpty()) + } + + @Test + fun `populates exception from main thread`() { + val hint = HintUtils.createWithTypeCheckHint(AbnormalExitHint()) + val stacktrace = SentryStackTrace().apply { + frames = listOf( + SentryStackFrame().apply { + lineno = 777 + module = "io.sentry.samples.MainActivity" + function = "run" + } + ) + } + + val processed = processEvent(hint) { + threads = listOf( + SentryThread().apply { + name = "main" + id = 13 + this.stacktrace = stacktrace + } + ) + } + + val exception = processed.exceptions!!.first() + assertEquals(13, exception.threadId) + assertEquals("AppExitInfo", exception.mechanism!!.type) + assertEquals("ANR", exception.value) + assertEquals("ApplicationNotResponding", exception.type) + assertEquals("io.sentry.android.core", exception.module) + assertEquals(true, exception.stacktrace!!.snapshot) + val frame = exception.stacktrace!!.frames!!.first() + assertEquals(777, frame.lineno) + assertEquals("run", frame.function) + assertEquals("io.sentry.samples.MainActivity", frame.module) + } + + @Test + fun `populates exception without stacktrace when there is no main thread in threads`() { + val hint = HintUtils.createWithTypeCheckHint(AbnormalExitHint()) + + val processed = processEvent(hint) { + threads = listOf(SentryThread()) + } + + val exception = processed.exceptions!!.first() + assertEquals("AppExitInfo", exception.mechanism!!.type) + assertEquals("ANR", exception.value) + assertEquals("ApplicationNotResponding", exception.type) + assertEquals("io.sentry.android.core", exception.module) + assertNull(exception.stacktrace) + } + + @Test + fun `adds Background to the message when mechanism is anr_background`() { + val hint = HintUtils.createWithTypeCheckHint(AbnormalExitHint(mechanism = "anr_background")) + + val processed = processEvent(hint) { + threads = listOf( + SentryThread().apply { + name = "main" + stacktrace = SentryStackTrace() + } + ) + } + + val exception = processed.exceptions!!.first() + assertEquals("Background ANR", exception.value) + } + + @Test + fun `does not add Background to the message when mechanism is anr_foreground`() { + val hint = HintUtils.createWithTypeCheckHint(AbnormalExitHint(mechanism = "anr_foreground")) + + val processed = processEvent(hint) { + threads = listOf( + SentryThread().apply { + name = "main" + stacktrace = SentryStackTrace() + } + ) + } + + val exception = processed.exceptions!!.first() + assertEquals("ANR", exception.value) + } + private fun processEvent( hint: Hint, populateScopeCache: Boolean = false, @@ -428,6 +530,11 @@ class AnrV2EventProcessorTest { return processor.process(original, hint)!! } + internal class AbnormalExitHint(val mechanism: String? = null) : AbnormalExit, Backfillable { + override fun mechanism(): String? = mechanism + override fun shouldEnrich(): Boolean = true + } + internal class BackfillableHint(private val shouldEnrich: Boolean = true) : Backfillable { override fun shouldEnrich(): Boolean = shouldEnrich } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/AnrV2IntegrationTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/AnrV2IntegrationTest.kt index b09abe9e143..4db0d425c47 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/AnrV2IntegrationTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/AnrV2IntegrationTest.kt @@ -13,7 +13,6 @@ import io.sentry.SentryLevel import io.sentry.android.core.AnrV2Integration.AnrV2Hint import io.sentry.android.core.cache.AndroidEnvelopeCache import io.sentry.cache.EnvelopeCache -import io.sentry.exception.ExceptionMechanismException import io.sentry.hints.DiskFlushNotification import io.sentry.hints.SessionStartHint import io.sentry.protocol.SentryId @@ -29,6 +28,7 @@ import org.mockito.kotlin.check import org.mockito.kotlin.inOrder import org.mockito.kotlin.mock import org.mockito.kotlin.never +import org.mockito.kotlin.spy import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @@ -78,6 +78,7 @@ class AnrV2IntegrationTest { this.isAnrEnabled = isAnrEnabled this.flushTimeoutMillis = flushTimeoutMillis this.isEnableAutoSessionTracking = sessionTrackingEnabled + addInAppInclude("io.sentry.samples") setEnvelopeDiskCache(EnvelopeCache.create(this)) } options.cacheDirPath?.let { cacheDir -> @@ -104,7 +105,41 @@ class AnrV2IntegrationTest { if (importance != null) { builder.setImportance(importance) } - shadowActivityManager.addApplicationExitInfo(builder.build()) + val exitInfo = spy(builder.build()) { + whenever(mock.traceInputStream).thenReturn( + """ +"main" prio=5 tid=1 Blocked + | group="main" sCount=1 ucsCount=0 flags=1 obj=0x72a985e0 self=0xb400007cabc57380 + | sysTid=28941 nice=-10 cgrp=top-app sched=0/0 handle=0x7deceb74f8 + | state=S schedstat=( 324804784 183300334 997 ) utm=23 stm=8 core=3 HZ=100 + | stack=0x7ff93a9000-0x7ff93ab000 stackSize=8188KB + | held mutexes= + at io.sentry.samples.android.MainActivity${'$'}2.run(MainActivity.java:177) + - waiting to lock <0x0d3a2f0a> (a java.lang.Object) held by thread 5 + at android.os.Handler.handleCallback(Handler.java:942) + at android.os.Handler.dispatchMessage(Handler.java:99) + at android.os.Looper.loopOnce(Looper.java:201) + at android.os.Looper.loop(Looper.java:288) + at android.app.ActivityThread.main(ActivityThread.java:7872) + at java.lang.reflect.Method.invoke(Native method) + at com.android.internal.os.RuntimeInit${'$'}MethodAndArgsCaller.run(RuntimeInit.java:548) + at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936) + +"perfetto_hprof_listener" prio=10 tid=7 Native (still starting up) + | group="" sCount=1 ucsCount=0 flags=1 obj=0x0 self=0xb400007cabc5ab20 + | sysTid=28959 nice=-20 cgrp=top-app sched=0/0 handle=0x7b2021bcb0 + | state=S schedstat=( 72750 1679167 1 ) utm=0 stm=0 core=3 HZ=100 + | stack=0x7b20124000-0x7b20126000 stackSize=991KB + | held mutexes= + native: #00 pc 00000000000a20f4 /apex/com.android.runtime/lib64/bionic/libc.so (read+4) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #01 pc 000000000001d840 /apex/com.android.art/lib64/libperfetto_hprof.so (void* std::__1::__thread_proxy >, ArtPlugin_Initialize::${'$'}_34> >(void*)+260) (BuildId: 525cc92a7dc49130157aeb74f6870364) + native: #02 pc 00000000000b63b0 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+208) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #03 pc 00000000000530b8 /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + (no managed stack frames) + """.trimIndent().byteInputStream() + ) + } + shadowActivityManager.addApplicationExitInfo(exitInfo) } } @@ -202,13 +237,31 @@ class AnrV2IntegrationTest { check { assertEquals(newTimestamp, it.timestamp.time) assertEquals(SentryLevel.FATAL, it.level) - assertTrue { - it.throwable is ApplicationNotResponding && - it.throwable!!.message == "Background ANR" - } - assertTrue { - (it.throwableMechanism as ExceptionMechanismException).exceptionMechanism.type == "ANRv2" - } + val mainThread = it.threads!!.first() + assertEquals("main", mainThread.name) + assertEquals(1, mainThread.id) + assertEquals("Blocked", mainThread.state) + assertEquals(true, mainThread.isCrashed) + assertEquals(true, mainThread.isMain) + assertEquals("0x0d3a2f0a", mainThread.heldLocks!!.values.first().address) + assertEquals(5, mainThread.heldLocks!!.values.first().threadId) + val lastFrame = mainThread.stacktrace!!.frames!!.last() + assertEquals("io.sentry.samples.android.MainActivity$2", lastFrame.module) + assertEquals("MainActivity.java", lastFrame.filename) + assertEquals("run", lastFrame.function) + assertEquals(177, lastFrame.lineno) + assertEquals(true, lastFrame.isInApp) + val otherThread = it.threads!![1] + assertEquals("perfetto_hprof_listener", otherThread.name) + assertEquals(7, otherThread.id) + assertEquals("Native", otherThread.state) + assertEquals(false, otherThread.isCrashed) + assertEquals(false, otherThread.isMain) + val firstFrame = otherThread.stacktrace!!.frames!!.first() + assertEquals( + "/apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b)", + firstFrame.`package` + ) }, argThat { val hint = HintUtils.getSentrySdkHint(this) @@ -218,7 +271,7 @@ class AnrV2IntegrationTest { } @Test - fun `when latest ANR has foreground importance, does not add Background to the name`() { + fun `when latest ANR has foreground importance, sets abnormal mechanism to anr_foreground`() { val integration = fixture.getSut(tmpDir, lastReportedAnrTimestamp = oldTimestamp) fixture.addAppExitInfo( timestamp = newTimestamp, @@ -228,10 +281,11 @@ class AnrV2IntegrationTest { integration.register(fixture.hub, fixture.options) verify(fixture.hub).captureEvent( - argThat { - throwable is ApplicationNotResponding && throwable!!.message == "ANR" - }, - anyOrNull() + any(), + argThat { + val hint = HintUtils.getSentrySdkHint(this) + (hint as AnrV2Hint).mechanism() == "anr_foreground" + } ) } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/internal/threaddump/ThreadDumpParserTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/internal/threaddump/ThreadDumpParserTest.kt new file mode 100644 index 00000000000..92afb3823fc --- /dev/null +++ b/sentry-android-core/src/test/java/io/sentry/android/core/internal/threaddump/ThreadDumpParserTest.kt @@ -0,0 +1,67 @@ +package io.sentry.android.core.internal.threaddump + +import io.sentry.SentryLockReason +import io.sentry.SentryOptions +import java.io.File +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull + +class ThreadDumpParserTest { + + @Test + fun `parses thread dump into SentryThread list`() { + val lines = Lines.readLines(File("src/test/resources/thread_dump.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 main = threads.find { it.name == "main" } + assertEquals(1, main!!.id) + assertEquals("Blocked", main.state) + assertEquals(true, main.isCrashed) + assertEquals(true, main.isMain) + assertEquals(true, main.isCurrent) + assertNotNull(main.heldLocks!!["0x0d3a2f0a"]) + assertEquals(SentryLockReason.BLOCKED, main.heldLocks!!["0x0d3a2f0a"]!!.type) + assertEquals(5, main.heldLocks!!["0x0d3a2f0a"]!!.threadId) + val lastFrame = main.stacktrace!!.frames!!.last() + assertEquals("io.sentry.samples.android.MainActivity$2", lastFrame.module) + assertEquals("MainActivity.java", lastFrame.filename) + assertEquals("run", lastFrame.function) + assertEquals(177, lastFrame.lineno) + assertEquals(true, lastFrame.isInApp) + + val blockingThread = threads.find { it.name == "Thread-9" } + assertEquals(5, blockingThread!!.id) + assertEquals("Sleeping", blockingThread.state) + assertEquals(false, blockingThread.isCrashed) + assertEquals(false, blockingThread.isMain) + assertNotNull(blockingThread.heldLocks!!["0x0d3a2f0a"]) + assertEquals(SentryLockReason.LOCKED, blockingThread.heldLocks!!["0x0d3a2f0a"]!!.type) + assertEquals(null, blockingThread.heldLocks!!["0x0d3a2f0a"]!!.threadId) + assertNotNull(blockingThread.heldLocks!!["0x09228c2d"]) + assertEquals(SentryLockReason.SLEEPING, blockingThread.heldLocks!!["0x09228c2d"]!!.type) + assertEquals(null, blockingThread.heldLocks!!["0x09228c2d"]!!.threadId) + + val randomThread = + threads.find { it.name == "io.sentry.android.core.internal.util.SentryFrameMetricsCollector" } + assertEquals(19, randomThread!!.id) + assertEquals("Native", randomThread.state) + assertEquals(false, randomThread.isCrashed) + assertEquals(false, randomThread.isMain) + assertEquals(false, randomThread.isCurrent) + assertEquals( + "/apex/com.android.runtime/lib64/bionic/libc.so (__epoll_pwait+8) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b)", + randomThread.stacktrace!!.frames!!.last().`package` + ) + val firstFrame = randomThread.stacktrace!!.frames!!.first() + assertEquals("android.os.HandlerThread", firstFrame.module) + assertEquals("run", firstFrame.function) + assertEquals("HandlerThread.java", firstFrame.filename) + assertEquals(67, firstFrame.lineno) + assertEquals(null, firstFrame.isInApp) + } +} diff --git a/sentry-android-core/src/test/resources/thread_dump.txt b/sentry-android-core/src/test/resources/thread_dump.txt new file mode 100644 index 00000000000..e1f3aa2c647 --- /dev/null +++ b/sentry-android-core/src/test/resources/thread_dump.txt @@ -0,0 +1,660 @@ + +----- pid 28941 at 2023-04-04 22:06:31.064728684+0200 ----- +Cmd line: io.sentry.samples.android +Build fingerprint: 'google/sdk_gphone64_arm64/emu64a:13/TE1A.220922.012/9302419:userdebug/dev-keys' +ABI: 'arm64' +Build type: optimized +Zygote loaded classes=21575 post zygote classes=2000 +Dumping registered class loaders +#0 dalvik.system.PathClassLoader: [], parent #1 +#1 java.lang.BootClassLoader: [], no parent +#2 dalvik.system.PathClassLoader: [/data/app/~~YQzOOq5EdbRpDSLuP4W5bg==/io.sentry.samples.android-rf1QICG-91naFlmtGfmOTQ==/base.apk!classes20.dex:/data/app/~~YQzOOq5EdbRpDSLuP4W5bg==/io.sentry.samples.android-rf1QICG-91naFlmtGfmOTQ==/base.apk!classes18.dex:/data/app/~~YQzOOq5EdbRpDSLuP4W5bg==/io.sentry.samples.android-rf1QICG-91naFlmtGfmOTQ==/base.apk!classes17.dex:/data/app/~~YQzOOq5EdbRpDSLuP4W5bg==/io.sentry.samples.android-rf1QICG-91naFlmtGfmOTQ==/base.apk!classes9.dex:/data/app/~~YQzOOq5EdbRpDSLuP4W5bg==/io.sentry.samples.android-rf1QICG-91naFlmtGfmOTQ==/base.apk!classes7.dex:/data/app/~~YQzOOq5EdbRpDSLuP4W5bg==/io.sentry.samples.android-rf1QICG-91naFlmtGfmOTQ==/base.apk!classes11.dex:/data/app/~~YQzOOq5EdbRpDSLuP4W5bg==/io.sentry.samples.android-rf1QICG-91naFlmtGfmOTQ==/base.apk!classes13.dex:/data/app/~~YQzOOq5EdbRpDSLuP4W5bg==/io.sentry.samples.android-rf1QICG-91naFlmtGfmOTQ==/base.apk!classes12.dex:/data/app/~~YQzOOq5EdbRpDSLuP4W5bg==/io.sentry.samples.android-rf1QICG-91naFlmtGfmOTQ==/base.apk!classes8.dex:/data/app/~~YQzOOq5EdbRpDSLuP4W5bg==/io.sentry.samples.android-rf1QICG-91naFlmtGfmOTQ==/base.apk!classes15.dex:/data/app/~~YQzOOq5EdbRpDSLuP4W5bg==/io.sentry.samples.android-rf1QICG-91naFlmtGfmOTQ==/base.apk!classes10.dex:/data/app/~~YQzOOq5EdbRpDSLuP4W5bg==/io.sentry.samples.android-rf1QICG-91naFlmtGfmOTQ==/base.apk!classes16.dex:/data/app/~~YQzOOq5EdbRpDSLuP4W5bg==/io.sentry.samples.android-rf1QICG-91naFlmtGfmOTQ==/base.apk!classes19.dex:/data/app/~~YQzOOq5EdbRpDSLuP4W5bg==/io.sentry.samples.android-rf1QICG-91naFlmtGfmOTQ==/base.apk:/data/app/~~YQzOOq5EdbRpDSLuP4W5bg==/io.sentry.samples.android-rf1QICG-91naFlmtGfmOTQ==/base.apk!classes21.dex:/data/app/~~YQzOOq5EdbRpDSLuP4W5bg==/io.sentry.samples.android-rf1QICG-91naFlmtGfmOTQ==/base.apk!classes14.dex:/data/app/~~YQzOOq5EdbRpDSLuP4W5bg==/io.sentry.samples.android-rf1QICG-91naFlmtGfmOTQ==/base.apk!classes3.dex:/data/app/~~YQzOOq5EdbRpDSLuP4W5bg==/io.sentry.samples.android-rf1QICG-91naFlmtGfmOTQ==/base.apk!classes2.dex:/data/app/~~YQzOOq5EdbRpDSLuP4W5bg==/io.sentry.samples.android-rf1QICG-91naFlmtGfmOTQ==/base.apk!classes4.dex], parent #1 +Done dumping class loaders +Classes initialized: 0 in 0 +Intern table: 31377 strong; 1217 weak +JNI: CheckJNI is on; globals=412 (plus 73 weak) +Libraries: /data/app/~~YQzOOq5EdbRpDSLuP4W5bg==/io.sentry.samples.android-rf1QICG-91naFlmtGfmOTQ==/lib/arm64/libsentry-android.so /data/app/~~YQzOOq5EdbRpDSLuP4W5bg==/io.sentry.samples.android-rf1QICG-91naFlmtGfmOTQ==/lib/arm64/libsentry.so /system/lib64/liblog.so libandroid.so libaudioeffect_jni.so libcompiler_rt.so libframework-connectivity-jni.so libframework-connectivity-tiramisu-jni.so libicu_jni.so libjavacore.so libjavacrypto.so libjnigraphics.so libmedia_jni.so libopenjdk.so librs_jni.so librtp_jni.so libsoundpool.so libstats_jni.so libwebviewchromium_loader.so (19) +Heap: 40% free, 4484KB/7592KB; 169353 objects +Dumping cumulative Gc timings +Start Dumping Averages for 1 iterations for concurrent copying +SweepSystemWeaks: Sum: 5.115ms Avg: 5.115ms +Process mark stacks and References: Sum: 1.689ms Avg: 1.689ms +VisitConcurrentRoots: Sum: 1.549ms Avg: 1.549ms +MarkingPhase: Sum: 1.212ms Avg: 1.212ms +ScanImmuneSpaces: Sum: 913us Avg: 913us +GrayAllDirtyImmuneObjects: Sum: 500us Avg: 500us +ClearFromSpace: Sum: 245us Avg: 245us +SweepLargeObjects: Sum: 113us Avg: 113us +CaptureThreadRootsForMarking: Sum: 96us Avg: 96us +EnqueueFinalizerReferences: Sum: 94us Avg: 94us +ScanCardsForSpace: Sum: 69us Avg: 69us +FlipOtherThreads: Sum: 33us Avg: 33us +ForwardSoftReferences: Sum: 30us Avg: 30us +InitializePhase: Sum: 28us Avg: 28us +ResumeRunnableThreads: Sum: 19us Avg: 19us +VisitNonThreadRoots: Sum: 17us Avg: 17us +MarkStackAsLive: Sum: 15us Avg: 15us +RecordFree: Sum: 13us Avg: 13us +MarkZygoteLargeObjects: Sum: 10us Avg: 10us +SweepAllocSpace: Sum: 10us Avg: 10us +ProcessReferences: Sum: 10us Avg: 10us +(Paused)GrayAllNewlyDirtyImmuneObjects: Sum: 9us Avg: 9us +SwapBitmaps: Sum: 7us Avg: 7us +EmptyRBMarkBitStack: Sum: 4us Avg: 4us +CopyingPhase: Sum: 2us Avg: 2us +ThreadListFlip: Sum: 2us Avg: 2us +(Paused)ClearCards: Sum: 2us Avg: 2us +ReclaimPhase: Sum: 1us Avg: 1us +(Paused)FlipCallback: Sum: 0 Avg: 0 +UnBindBitmaps: Sum: 0 Avg: 0 +ResumeOtherThreads: Sum: 0 Avg: 0 +FlipThreadRoots: Sum: 0 Avg: 0 +Sweep: Sum: 0 Avg: 0 +(Paused)SetFromSpace: Sum: 0 Avg: 0 +Done Dumping Averages +concurrent copying paused: Sum: 20us 99% C.I. 5us-15us Avg: 10us Max: 15us +concurrent copying freed-bytes: Avg: 7364KB Max: 7364KB Min: 7364KB +Freed-bytes histogram: 7040:1 +concurrent copying total time: 11.807ms mean time: 11.807ms +concurrent copying freed: 53426 objects with total size 7364KB +concurrent copying throughput: 4.85691e+06/s / 653MB/s per cpu-time: 1077389714/s / 1027MB/s +concurrent copying tracing throughput: 198MB/s per cpu-time: 312MB/s +Average major GC reclaim bytes ratio 1.11802 over 1 GC cycles +Average major GC copied live bytes ratio 0.725846 over 5 major GCs +Cumulative bytes moved 30421320 +Cumulative objects moved 534294 +Peak regions allocated 50 (12MB) / 768 (192MB) +Total madvise time 1.829ms +Average minor GC reclaim bytes ratio inf over 0 GC cycles +Average minor GC copied live bytes ratio 0.28859 over 2 minor GCs +Cumulative bytes moved 3695344 +Cumulative objects moved 89281 +Peak regions allocated 50 (12MB) / 768 (192MB) +Total time spent in GC: 11.807ms +Mean GC size throughput: 609MB/s per cpu-time: 937MB/s +Mean GC object throughput: 4.52494e+06 objects/s +Total number of allocations 222779 +Total bytes allocated 11MB +Total bytes freed 7364KB +Free memory 3107KB +Free memory until GC 3107KB +Free memory until OOME 187MB +Total memory 7592KB +Max memory 192MB +Zygote space size 7744KB +Total mutator paused time: 20us +Total time waiting for GC to complete: 8.054ms +Total GC count: 1 +Total GC time: 11.807ms +Total blocking GC count: 1 +Total blocking GC time: 11.873ms +Total pre-OOME GC count: 0 +Histogram of GC count per 10000 ms: 0:1 +Histogram of blocking GC count per 10000 ms: 0:1 +Native bytes total: 18678627 registered: 635795 +Total native bytes at last GC: 21677907 +/system/framework/oat/arm64/android.hidl.manager-V1.0-java.odex: verify +/system/framework/oat/arm64/android.hidl.base-V1.0-java.odex: verify +/system/framework/oat/arm64/android.test.base.odex: verify +Current JIT code cache size (used / resident): 59KB / 64KB +Current JIT data cache size (used / resident): 47KB / 52KB +Zygote JIT code cache size (at point of fork): 19KB / 32KB +Zygote JIT data cache size (at point of fork): 14KB / 32KB +Current JIT mini-debug-info size: 32KB +Current JIT capacity: 128KB +Current number of JIT JNI stub entries: 0 +Current number of JIT code cache entries: 44 +Total number of JIT baseline compilations: 31 +Total number of JIT optimized compilations: 1 +Total number of JIT compilations for on stack replacement: 0 +Total number of JIT code cache collections: 1 +Memory used for stack maps: Avg: 533B Max: 2728B Min: 32B +Memory used for compiled code: Avg: 2014B Max: 7140B Min: 196B +Memory used for profiling info: Avg: 371B Max: 1296B Min: 24B +Start Dumping Averages for 47 iterations for JIT timings +Compiling optimized: Sum: 66.471ms Avg: 1.414ms +Code cache collection: Sum: 21.797ms Avg: 463.765us +Compiling baseline: Sum: 14.750ms Avg: 313.829us +TrimMaps: Sum: 717us Avg: 15.255us +Done Dumping Averages +Memory used for compilation: Avg: 156KB Max: 1693KB Min: 16KB +ProfileSaver total_bytes_written=0 +ProfileSaver total_number_of_writes=0 +ProfileSaver total_number_of_code_cache_queries=0 +ProfileSaver total_number_of_skipped_writes=0 +ProfileSaver total_number_of_failed_writes=0 +ProfileSaver total_ms_of_sleep=5000 +ProfileSaver total_ms_of_work=0 +ProfileSaver total_number_of_hot_spikes=0 +ProfileSaver total_number_of_wake_ups=0 + +*** ART internal metrics *** + Metadata: + timestamp_since_start_ms: 18063 + Metrics: + ClassLoadingTotalTime: count = 26287 + ClassVerificationTotalTime: count = 99135 + ClassVerificationCount: count = 1044 + WorldStopTimeDuringGCAvg: count = 21 + YoungGcCount: count = 0 + FullGcCount: count = 1 + TotalBytesAllocated: count = 10151688 + TotalGcCollectionTime: count = 11 + YoungGcThroughputAvg: count = 0 + FullGcThroughputAvg: count = 393 + YoungGcTracingThroughputAvg: count = 0 + FullGcTracingThroughputAvg: count = 184 + JitMethodCompileTotalTime: count = 70979 + JitMethodCompileCount: count = 32 + YoungGcCollectionTime: range = 0...60000, buckets: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + FullGcCollectionTime: range = 0...60000, buckets: 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + YoungGcThroughput: range = 0...10000, buckets: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + FullGcThroughput: range = 0...10000, buckets: 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + YoungGcTracingThroughput: range = 0...10000, buckets: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + FullGcTracingThroughput: range = 0...10000, buckets: 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + GcWorldStopTime: count = 21 + GcWorldStopCount: count = 1 + YoungGcScannedBytes: count = 0 + YoungGcFreedBytes: count = 0 + YoungGcDuration: count = 0 + FullGcScannedBytes: count = 2293021 + FullGcFreedBytes: count = 4957152 + FullGcDuration: count = 11 +*** Done dumping ART internal metrics *** + +suspend all histogram: Sum: 3.220ms 99% C.I. 0.161us-58.527us Avg: 5.639us Max: 1225us +DALVIK THREADS (29): +"Signal Catcher" daemon prio=10 tid=6 Runnable + | group="system" sCount=0 ucsCount=0 flags=0 obj=0x136c0248 self=0xb400007cabc689a0 + | sysTid=28957 nice=-20 cgrp=top-app sched=0/0 handle=0x7b21319cb0 + | state=R schedstat=( 6825503 402209 26 ) utm=0 stm=0 core=0 HZ=100 + | stack=0x7b21222000-0x7b21224000 stackSize=991KB + | held mutexes= "mutator lock"(shared held) + native: #00 pc 000000000053a6e0 /apex/com.android.art/lib64/libart.so (art::DumpNativeStack(std::__1::basic_ostream >&, int, BacktraceMap*, char const*, art::ArtMethod*, void*, bool)+128) (BuildId: e24a1818231cfb1649cb83a5d2869598) + native: #01 pc 00000000006f0e84 /apex/com.android.art/lib64/libart.so (art::Thread::DumpStack(std::__1::basic_ostream >&, bool, BacktraceMap*, bool) const+236) (BuildId: e24a1818231cfb1649cb83a5d2869598) + native: #02 pc 00000000006fe710 /apex/com.android.art/lib64/libart.so (art::DumpCheckpoint::Run(art::Thread*)+208) (BuildId: e24a1818231cfb1649cb83a5d2869598) + native: #03 pc 0000000000364248 /apex/com.android.art/lib64/libart.so (art::ThreadList::RunCheckpoint(art::Closure*, art::Closure*)+440) (BuildId: e24a1818231cfb1649cb83a5d2869598) + native: #04 pc 00000000006fceb0 /apex/com.android.art/lib64/libart.so (art::ThreadList::Dump(std::__1::basic_ostream >&, bool)+280) (BuildId: e24a1818231cfb1649cb83a5d2869598) + native: #05 pc 00000000006fc8a4 /apex/com.android.art/lib64/libart.so (art::ThreadList::DumpForSigQuit(std::__1::basic_ostream >&)+292) (BuildId: e24a1818231cfb1649cb83a5d2869598) + native: #06 pc 00000000006d5974 /apex/com.android.art/lib64/libart.so (art::Runtime::DumpForSigQuit(std::__1::basic_ostream >&)+184) (BuildId: e24a1818231cfb1649cb83a5d2869598) + native: #07 pc 00000000006e1a20 /apex/com.android.art/lib64/libart.so (art::SignalCatcher::HandleSigQuit()+468) (BuildId: e24a1818231cfb1649cb83a5d2869598) + native: #08 pc 0000000000574230 /apex/com.android.art/lib64/libart.so (art::SignalCatcher::Run(void*)+264) (BuildId: e24a1818231cfb1649cb83a5d2869598) + native: #09 pc 00000000000b63b0 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+208) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #10 pc 00000000000530b8 /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + (no managed stack frames) + +"main" prio=5 tid=1 Blocked + | group="main" sCount=1 ucsCount=0 flags=1 obj=0x72a985e0 self=0xb400007cabc57380 + | sysTid=28941 nice=-10 cgrp=top-app sched=0/0 handle=0x7deceb74f8 + | state=S schedstat=( 324804784 183300334 997 ) utm=23 stm=8 core=3 HZ=100 + | stack=0x7ff93a9000-0x7ff93ab000 stackSize=8188KB + | held mutexes= + at io.sentry.samples.android.MainActivity$2.run(MainActivity.java:177) + - waiting to lock <0x0d3a2f0a> (a java.lang.Object) held by thread 5 + at android.os.Handler.handleCallback(Handler.java:942) + at android.os.Handler.dispatchMessage(Handler.java:99) + at android.os.Looper.loopOnce(Looper.java:201) + at android.os.Looper.loop(Looper.java:288) + at android.app.ActivityThread.main(ActivityThread.java:7872) + at java.lang.reflect.Method.invoke(Native method) + at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) + at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936) + +"perfetto_hprof_listener" prio=10 tid=7 Native (still starting up) + | group="" sCount=1 ucsCount=0 flags=1 obj=0x0 self=0xb400007cabc5ab20 + | sysTid=28959 nice=-20 cgrp=top-app sched=0/0 handle=0x7b2021bcb0 + | state=S schedstat=( 72750 1679167 1 ) utm=0 stm=0 core=3 HZ=100 + | stack=0x7b20124000-0x7b20126000 stackSize=991KB + | held mutexes= + native: #00 pc 00000000000a20f4 /apex/com.android.runtime/lib64/bionic/libc.so (read+4) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #01 pc 000000000001d840 /apex/com.android.art/lib64/libperfetto_hprof.so (void* std::__1::__thread_proxy >, ArtPlugin_Initialize::$_34> >(void*)+260) (BuildId: 525cc92a7dc49130157aeb74f6870364) + native: #02 pc 00000000000b63b0 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+208) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #03 pc 00000000000530b8 /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + (no managed stack frames) + +"ADB-JDWP Connection Control Thread" daemon prio=0 tid=8 WaitingInMainDebuggerLoop + | group="system" sCount=1 ucsCount=0 flags=1 obj=0x136c02c0 self=0xb400007cabc5c6f0 + | sysTid=28960 nice=-20 cgrp=top-app sched=0/0 handle=0x7b2011dcb0 + | state=S schedstat=( 1003335 1331749 27 ) utm=0 stm=0 core=3 HZ=100 + | stack=0x7b20026000-0x7b20028000 stackSize=991KB + | held mutexes= + native: #00 pc 00000000000a34b8 /apex/com.android.runtime/lib64/bionic/libc.so (__ppoll+8) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #01 pc 000000000005dc1c /apex/com.android.runtime/lib64/bionic/libc.so (poll+92) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #02 pc 00000000000099e4 /apex/com.android.art/lib64/libadbconnection.so (adbconnection::AdbConnectionState::RunPollLoop(art::Thread*)+724) (BuildId: 3952e992b55a158a16b3d569cf8894e7) + native: #03 pc 00000000000080ac /apex/com.android.art/lib64/libadbconnection.so (adbconnection::CallbackFunction(void*)+1320) (BuildId: 3952e992b55a158a16b3d569cf8894e7) + native: #04 pc 00000000000b63b0 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+208) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #05 pc 00000000000530b8 /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + (no managed stack frames) + +"HeapTaskDaemon" daemon prio=5 tid=9 WaitingForTaskProcessor + | group="system" sCount=1 ucsCount=0 flags=1 obj=0x136c1690 self=0xb400007cabc80f00 + | sysTid=28962 nice=4 cgrp=top-app sched=0/0 handle=0x7ad35e8cb0 + | state=S schedstat=( 9212958 1006583 26 ) utm=0 stm=0 core=3 HZ=100 + | stack=0x7ad34e5000-0x7ad34e7000 stackSize=1039KB + | held mutexes= + native: #00 pc 000000000004df60 /apex/com.android.runtime/lib64/bionic/libc.so (syscall+32) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #01 pc 000000000048771c /apex/com.android.art/lib64/libart.so (art::ConditionVariable::TimedWait(art::Thread*, long, int)+252) (BuildId: e24a1818231cfb1649cb83a5d2869598) + native: #02 pc 000000000046cf20 /apex/com.android.art/lib64/libart.so (art::gc::TaskProcessor::GetTask(art::Thread*)+196) (BuildId: e24a1818231cfb1649cb83a5d2869598) + native: #03 pc 000000000046ce10 /apex/com.android.art/lib64/libart.so (art::gc::TaskProcessor::RunAllTasks(art::Thread*)+32) (BuildId: e24a1818231cfb1649cb83a5d2869598) + at dalvik.system.VMRuntime.runHeapTasks(Native method) + at java.lang.Daemons$HeapTaskDaemon.runInternal(Daemons.java:609) + at java.lang.Daemons$Daemon.run(Daemons.java:140) + at java.lang.Thread.run(Thread.java:1012) + +"FinalizerDaemon" daemon prio=5 tid=10 Waiting + | group="system" sCount=1 ucsCount=0 flags=1 obj=0x136c0338 self=0xb400007cabc7f330 + | sysTid=28964 nice=4 cgrp=top-app sched=0/0 handle=0x7ad33d4cb0 + | state=S schedstat=( 1727043 1091459 22 ) utm=0 stm=0 core=0 HZ=100 + | stack=0x7ad32d1000-0x7ad32d3000 stackSize=1039KB + | held mutexes= + at java.lang.Object.wait(Native method) + - waiting on <0x07d7437b> (a java.lang.Object) + at java.lang.Object.wait(Object.java:442) + at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:203) + - locked <0x07d7437b> (a java.lang.Object) + at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:224) + at java.lang.Daemons$FinalizerDaemon.runInternal(Daemons.java:300) + at java.lang.Daemons$Daemon.run(Daemons.java:140) + at java.lang.Thread.run(Thread.java:1012) + +"FinalizerWatchdogDaemon" daemon prio=5 tid=11 Sleeping + | group="system" sCount=1 ucsCount=0 flags=1 obj=0x136c03b0 self=0xb400007cabc7bb90 + | sysTid=28965 nice=4 cgrp=top-app sched=0/0 handle=0x7ad32cacb0 + | state=S schedstat=( 176667 969916 4 ) utm=0 stm=0 core=3 HZ=100 + | stack=0x7ad31c7000-0x7ad31c9000 stackSize=1039KB + | held mutexes= + at java.lang.Thread.sleep(Native method) + - sleeping on <0x09596598> (a java.lang.Object) + at java.lang.Thread.sleep(Thread.java:450) + - locked <0x09596598> (a java.lang.Object) + at java.lang.Thread.sleep(Thread.java:355) + at java.lang.Daemons$FinalizerWatchdogDaemon.sleepForNanos(Daemons.java:438) + at java.lang.Daemons$FinalizerWatchdogDaemon.waitForProgress(Daemons.java:480) + at java.lang.Daemons$FinalizerWatchdogDaemon.runInternal(Daemons.java:369) + at java.lang.Daemons$Daemon.run(Daemons.java:140) + at java.lang.Thread.run(Thread.java:1012) + +"ReferenceQueueDaemon" daemon prio=5 tid=12 Waiting + | group="system" sCount=1 ucsCount=0 flags=1 obj=0x136c0428 self=0xb400007cabc7d760 + | sysTid=28963 nice=4 cgrp=top-app sched=0/0 handle=0x7ad34decb0 + | state=S schedstat=( 437749 1043042 4 ) utm=0 stm=0 core=1 HZ=100 + | stack=0x7ad33db000-0x7ad33dd000 stackSize=1039KB + | held mutexes= + at java.lang.Object.wait(Native method) + - waiting on <0x0394c1f1> (a java.lang.Class) + at java.lang.Object.wait(Object.java:442) + at java.lang.Object.wait(Object.java:568) + at java.lang.Daemons$ReferenceQueueDaemon.runInternal(Daemons.java:232) + - locked <0x0394c1f1> (a java.lang.Class) + at java.lang.Daemons$Daemon.run(Daemons.java:140) + at java.lang.Thread.run(Thread.java:1012) + +"Jit thread pool worker thread 0" daemon prio=5 tid=13 Native + | group="system" sCount=1 ucsCount=0 flags=1 obj=0x136c04a0 self=0xb400007cabc87e40 + | sysTid=28961 nice=9 cgrp=top-app sched=0/0 handle=0x7ad36eecb0 + | state=S schedstat=( 17155174 12570536 78 ) utm=1 stm=0 core=3 HZ=100 + | stack=0x7ad35ef000-0x7ad35f1000 stackSize=1023KB + | held mutexes= + native: #00 pc 000000000004df5c /apex/com.android.runtime/lib64/bionic/libc.so (syscall+28) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #01 pc 000000000047cc80 /apex/com.android.art/lib64/libart.so (art::ConditionVariable::WaitHoldingLocks(art::Thread*)+140) (BuildId: e24a1818231cfb1649cb83a5d2869598) + native: #02 pc 000000000047cb18 /apex/com.android.art/lib64/libart.so (art::ThreadPool::GetTask(art::Thread*)+120) (BuildId: e24a1818231cfb1649cb83a5d2869598) + native: #03 pc 00000000006199e4 /apex/com.android.art/lib64/libart.so (art::ThreadPoolWorker::Run()+136) (BuildId: e24a1818231cfb1649cb83a5d2869598) + native: #04 pc 00000000006198c4 /apex/com.android.art/lib64/libart.so (art::ThreadPoolWorker::Callback(void*)+160) (BuildId: e24a1818231cfb1649cb83a5d2869598) + native: #05 pc 00000000000b63b0 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+208) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #06 pc 00000000000530b8 /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + (no managed stack frames) + +"binder:28941_1" prio=5 tid=14 Native + | group="main" sCount=1 ucsCount=0 flags=1 obj=0x136c0518 self=0xb400007cabc82ad0 + | sysTid=28966 nice=0 cgrp=top-app sched=0/0 handle=0x7ace0a9cb0 + | state=S schedstat=( 574039 4838087 11 ) utm=0 stm=0 core=1 HZ=100 + | stack=0x7acdfb2000-0x7acdfb4000 stackSize=991KB + | held mutexes= + native: #00 pc 00000000000a23d8 /apex/com.android.runtime/lib64/bionic/libc.so (__ioctl+8) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #01 pc 000000000005b50c /apex/com.android.runtime/lib64/bionic/libc.so (ioctl+156) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #02 pc 0000000000094690 /system/lib64/libbinder.so (android::IPCThreadState::joinThreadPool(bool)+316) (BuildId: ee18e52b95e38eaab55a9a48518c8c3b) + native: #03 pc 0000000000094540 /system/lib64/libbinder.so (android::PoolThread::threadLoop()+24) (BuildId: ee18e52b95e38eaab55a9a48518c8c3b) + native: #04 pc 00000000000148e8 /system/lib64/libutils.so (android::Thread::_threadLoop(void*)+528) (BuildId: 5a0d720732600c94ad8354a1188e9f52) + native: #05 pc 00000000000c8918 /system/lib64/libandroid_runtime.so (android::AndroidRuntime::javaThreadShell(void*)+140) (BuildId: a31474ac581b716d4588f8c97eb06009) + native: #06 pc 00000000000b63b0 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+208) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #07 pc 00000000000530b8 /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + (no managed stack frames) + +"binder:28941_2" prio=5 tid=15 Native + | group="main" sCount=1 ucsCount=0 flags=1 obj=0x136c0590 self=0xb400007cabc86270 + | sysTid=28967 nice=0 cgrp=top-app sched=0/0 handle=0x7accfabcb0 + | state=S schedstat=( 4159627 3435666 30 ) utm=0 stm=0 core=2 HZ=100 + | stack=0x7acceb4000-0x7acceb6000 stackSize=991KB + | held mutexes= + native: #00 pc 00000000000a23d8 /apex/com.android.runtime/lib64/bionic/libc.so (__ioctl+8) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #01 pc 000000000005b50c /apex/com.android.runtime/lib64/bionic/libc.so (ioctl+156) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #02 pc 0000000000094690 /system/lib64/libbinder.so (android::IPCThreadState::joinThreadPool(bool)+316) (BuildId: ee18e52b95e38eaab55a9a48518c8c3b) + native: #03 pc 0000000000094540 /system/lib64/libbinder.so (android::PoolThread::threadLoop()+24) (BuildId: ee18e52b95e38eaab55a9a48518c8c3b) + native: #04 pc 00000000000148e8 /system/lib64/libutils.so (android::Thread::_threadLoop(void*)+528) (BuildId: 5a0d720732600c94ad8354a1188e9f52) + native: #05 pc 00000000000c8918 /system/lib64/libandroid_runtime.so (android::AndroidRuntime::javaThreadShell(void*)+140) (BuildId: a31474ac581b716d4588f8c97eb06009) + native: #06 pc 00000000000b63b0 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+208) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #07 pc 00000000000530b8 /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + (no managed stack frames) + +"binder:28941_3" prio=5 tid=16 Native + | group="main" sCount=1 ucsCount=0 flags=1 obj=0x136c0608 self=0xb400007cabc846a0 + | sysTid=28975 nice=0 cgrp=top-app sched=0/0 handle=0x7acbeadcb0 + | state=S schedstat=( 2250710 7051668 27 ) utm=0 stm=0 core=1 HZ=100 + | stack=0x7acbdb6000-0x7acbdb8000 stackSize=991KB + | held mutexes= + native: #00 pc 00000000000a23d8 /apex/com.android.runtime/lib64/bionic/libc.so (__ioctl+8) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #01 pc 000000000005b50c /apex/com.android.runtime/lib64/bionic/libc.so (ioctl+156) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #02 pc 0000000000094690 /system/lib64/libbinder.so (android::IPCThreadState::joinThreadPool(bool)+316) (BuildId: ee18e52b95e38eaab55a9a48518c8c3b) + native: #03 pc 0000000000094540 /system/lib64/libbinder.so (android::PoolThread::threadLoop()+24) (BuildId: ee18e52b95e38eaab55a9a48518c8c3b) + native: #04 pc 00000000000148e8 /system/lib64/libutils.so (android::Thread::_threadLoop(void*)+528) (BuildId: 5a0d720732600c94ad8354a1188e9f52) + native: #05 pc 00000000000c8918 /system/lib64/libandroid_runtime.so (android::AndroidRuntime::javaThreadShell(void*)+140) (BuildId: a31474ac581b716d4588f8c97eb06009) + native: #06 pc 00000000000b63b0 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+208) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #07 pc 00000000000530b8 /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + (no managed stack frames) + +"Profile Saver" daemon prio=5 tid=17 Native + | group="system" sCount=1 ucsCount=0 flags=1 obj=0x136c0680 self=0xb400007cabc89a10 + | sysTid=28980 nice=9 cgrp=top-app sched=0/0 handle=0x7ac9a80cb0 + | state=S schedstat=( 6243002 407667 10 ) utm=0 stm=0 core=2 HZ=100 + | stack=0x7ac9989000-0x7ac998b000 stackSize=991KB + | held mutexes= + native: #00 pc 000000000004df5c /apex/com.android.runtime/lib64/bionic/libc.so (syscall+28) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #01 pc 000000000047cc80 /apex/com.android.art/lib64/libart.so (art::ConditionVariable::WaitHoldingLocks(art::Thread*)+140) (BuildId: e24a1818231cfb1649cb83a5d2869598) + native: #02 pc 0000000000543774 /apex/com.android.art/lib64/libart.so (art::ProfileSaver::Run()+372) (BuildId: e24a1818231cfb1649cb83a5d2869598) + native: #03 pc 0000000000538fc0 /apex/com.android.art/lib64/libart.so (art::ProfileSaver::RunProfileSaverThread(void*)+148) (BuildId: e24a1818231cfb1649cb83a5d2869598) + native: #04 pc 00000000000b63b0 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+208) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #05 pc 00000000000530b8 /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + (no managed stack frames) + +"io.sentry.android.core.internal.util.SentryFrameMetricsCollector" prio=5 tid=19 Native + | group="main" sCount=1 ucsCount=0 flags=1 obj=0x136c06f8 self=0xb400007cabc8b5e0 + | sysTid=28991 nice=0 cgrp=top-app sched=0/0 handle=0x7ac74d0cb0 + | state=S schedstat=( 6494997 2246873 96 ) utm=0 stm=0 core=0 HZ=100 + | stack=0x7ac73cd000-0x7ac73cf000 stackSize=1039KB + | held mutexes= + native: #00 pc 00000000000a33b8 /apex/com.android.runtime/lib64/bionic/libc.so (__epoll_pwait+8) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #01 pc 0000000000010dfc /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+176) (BuildId: 5a0d720732600c94ad8354a1188e9f52) + native: #02 pc 000000000015a56c /system/lib64/libandroid_runtime.so (android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long, int)+44) (BuildId: a31474ac581b716d4588f8c97eb06009) + at android.os.MessageQueue.nativePollOnce(Native method) + at android.os.MessageQueue.next(MessageQueue.java:335) + at android.os.Looper.loopOnce(Looper.java:161) + at android.os.Looper.loop(Looper.java:288) + at android.os.HandlerThread.run(HandlerThread.java:67) + +"pool-2-thread-1" prio=5 tid=20 TimedWaiting + | group="main" sCount=1 ucsCount=0 flags=1 obj=0x136c0818 self=0xb400007cabc95cc0 + | sysTid=28993 nice=0 cgrp=top-app sched=0/0 handle=0x7ac63a6cb0 + | state=S schedstat=( 51027282 10362044 134 ) utm=2 stm=2 core=2 HZ=100 + | stack=0x7ac62a3000-0x7ac62a5000 stackSize=1039KB + | held mutexes= + at jdk.internal.misc.Unsafe.park(Native method) + - waiting on an unknown object + at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:234) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2123) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1188) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:905) + at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1063) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1123) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637) + at java.lang.Thread.run(Thread.java:1012) + +"SentryAsyncConnection-0" daemon prio=5 tid=18 Waiting + | group="main" sCount=1 ucsCount=0 flags=1 obj=0x136c0970 self=0xb400007cabc8d1b0 + | sysTid=28994 nice=0 cgrp=top-app sched=0/0 handle=0x7ac857acb0 + | state=S schedstat=( 48202960 58606620 247 ) utm=4 stm=0 core=1 HZ=100 + | stack=0x7ac8477000-0x7ac8479000 stackSize=1039KB + | held mutexes= + at jdk.internal.misc.Unsafe.park(Native method) + - waiting on an unknown object + at java.util.concurrent.locks.LockSupport.park(LockSupport.java:194) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2081) + at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:433) + at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1063) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1123) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637) + at java.lang.Thread.run(Thread.java:1012) + +"FileObserver" prio=5 tid=21 Native + | group="main" sCount=1 ucsCount=0 flags=1 obj=0x136c0ae8 self=0xb400007cabc92520 + | sysTid=28995 nice=0 cgrp=top-app sched=0/0 handle=0x7ac8450cb0 + | state=S schedstat=( 147291 1694959 5 ) utm=0 stm=0 core=3 HZ=100 + | stack=0x7ac834d000-0x7ac834f000 stackSize=1039KB + | held mutexes= + native: #00 pc 00000000000a20f4 /apex/com.android.runtime/lib64/bionic/libc.so (read+4) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #01 pc 00000000001b0fe4 /system/lib64/libandroid_runtime.so (android::android_os_fileobserver_observe(_JNIEnv*, _jobject*, int)+164) (BuildId: a31474ac581b716d4588f8c97eb06009) + at android.os.FileObserver$ObserverThread.observe(Native method) + at android.os.FileObserver$ObserverThread.run(FileObserver.java:116) + +"Timer-0" daemon prio=5 tid=22 Waiting + | group="main" sCount=1 ucsCount=0 flags=1 obj=0x136c0b68 self=0xb400007cabc940f0 + | sysTid=28996 nice=0 cgrp=top-app sched=0/0 handle=0x7ac8346cb0 + | state=S schedstat=( 32541 2753958 2 ) utm=0 stm=0 core=1 HZ=100 + | stack=0x7ac8243000-0x7ac8245000 stackSize=1039KB + | held mutexes= + at java.lang.Object.wait(Native method) + - waiting on <0x0ad431d6> (a java.util.TaskQueue) + at java.lang.Object.wait(Object.java:442) + at java.lang.Object.wait(Object.java:568) + at java.util.TimerThread.mainLoop(Timer.java:534) + - locked <0x0ad431d6> (a java.util.TaskQueue) + at java.util.TimerThread.run(Timer.java:513) + +"ConnectivityThread" prio=5 tid=23 Native + | group="main" sCount=1 ucsCount=0 flags=1 obj=0x136c0bf0 self=0xb400007cabc90950 + | sysTid=28997 nice=0 cgrp=top-app sched=0/0 handle=0x7ac823ccb0 + | state=S schedstat=( 4569546 2179707 44 ) utm=0 stm=0 core=1 HZ=100 + | stack=0x7ac8139000-0x7ac813b000 stackSize=1039KB + | held mutexes= + native: #00 pc 00000000000a33b8 /apex/com.android.runtime/lib64/bionic/libc.so (__epoll_pwait+8) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #01 pc 0000000000010dfc /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+176) (BuildId: 5a0d720732600c94ad8354a1188e9f52) + native: #02 pc 000000000015a56c /system/lib64/libandroid_runtime.so (android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long, int)+44) (BuildId: a31474ac581b716d4588f8c97eb06009) + at android.os.MessageQueue.nativePollOnce(Native method) + at android.os.MessageQueue.next(MessageQueue.java:335) + at android.os.Looper.loopOnce(Looper.java:161) + at android.os.Looper.loop(Looper.java:288) + at android.os.HandlerThread.run(HandlerThread.java:67) + +"LeakCanary-Heap-Dump" prio=5 tid=24 Native + | group="main" sCount=1 ucsCount=0 flags=1 obj=0x136c0d10 self=0xb400007cabc8ed80 + | sysTid=29000 nice=0 cgrp=top-app sched=0/0 handle=0x7ac80d2cb0 + | state=S schedstat=( 10523711 3945164 46 ) utm=0 stm=0 core=2 HZ=100 + | stack=0x7ac7fcf000-0x7ac7fd1000 stackSize=1039KB + | held mutexes= + native: #00 pc 00000000000a33b8 /apex/com.android.runtime/lib64/bionic/libc.so (__epoll_pwait+8) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #01 pc 0000000000010dfc /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+176) (BuildId: 5a0d720732600c94ad8354a1188e9f52) + native: #02 pc 000000000015a56c /system/lib64/libandroid_runtime.so (android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long, int)+44) (BuildId: a31474ac581b716d4588f8c97eb06009) + at android.os.MessageQueue.nativePollOnce(Native method) + at android.os.MessageQueue.next(MessageQueue.java:335) + at android.os.Looper.loopOnce(Looper.java:161) + at android.os.Looper.loop(Looper.java:288) + at android.os.HandlerThread.run(HandlerThread.java:67) + +"plumber-android-leaks" prio=5 tid=25 TimedWaiting + | group="main" sCount=1 ucsCount=0 flags=1 obj=0x136c0e30 self=0xb400007cabc99460 + | sysTid=29001 nice=10 cgrp=top-app sched=0/0 handle=0x7ac7fc8cb0 + | state=S schedstat=( 3488459 357249 18 ) utm=0 stm=0 core=1 HZ=100 + | stack=0x7ac7ec5000-0x7ac7ec7000 stackSize=1039KB + | held mutexes= + at jdk.internal.misc.Unsafe.park(Native method) + - waiting on an unknown object + at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:234) + at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2123) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1188) + at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:905) + at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1063) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1123) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637) + at leakcanary.AndroidLeakFixes$Companion$backgroundExecutor$1$thread$1.run(AndroidLeakFixes.kt:728) + +"RenderThread" daemon prio=7 tid=26 Native + | group="main" sCount=1 ucsCount=0 flags=1 obj=0x136c0f88 self=0xb400007cabc97890 + | sysTid=29004 nice=-10 cgrp=top-app sched=0/0 handle=0x7ac7ebecb0 + | state=S schedstat=( 160761573 37179129 468 ) utm=4 stm=11 core=1 HZ=100 + | stack=0x7ac7dc7000-0x7ac7dc9000 stackSize=991KB + | held mutexes= + native: #00 pc 00000000000a33b8 /apex/com.android.runtime/lib64/bionic/libc.so (__epoll_pwait+8) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #01 pc 0000000000010dfc /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+176) (BuildId: 5a0d720732600c94ad8354a1188e9f52) + native: #02 pc 000000000057c4c0 /system/lib64/libhwui.so (android::uirenderer::renderthread::RenderThread::threadLoop()+220) (BuildId: 5e787210ce0f171dbee073e4a14a376c) + native: #03 pc 00000000000148e8 /system/lib64/libutils.so (android::Thread::_threadLoop(void*)+528) (BuildId: 5a0d720732600c94ad8354a1188e9f52) + native: #04 pc 00000000000b63b0 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+208) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #05 pc 00000000000530b8 /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + (no managed stack frames) + +"OkHttp ConnectionPool" daemon prio=5 tid=29 TimedWaiting + | group="main" sCount=1 ucsCount=0 flags=1 obj=0x136c1000 self=0xb400007cabca03a0 + | sysTid=29010 nice=0 cgrp=top-app sched=0/0 handle=0x7ac7905cb0 + | state=S schedstat=( 198166 0 1 ) utm=0 stm=0 core=1 HZ=100 + | stack=0x7ac7802000-0x7ac7804000 stackSize=1039KB + | held mutexes= + at java.lang.Object.wait(Native method) + - waiting on <0x050c9c57> (a com.android.okhttp.ConnectionPool) + at com.android.okhttp.ConnectionPool$1.run(ConnectionPool.java:106) + - locked <0x050c9c57> (a com.android.okhttp.ConnectionPool) + at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1137) + at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:637) + at java.lang.Thread.run(Thread.java:1012) + +"FrameMetricsAggregator" prio=5 tid=30 Native + | group="main" sCount=1 ucsCount=0 flags=1 obj=0x136c1128 self=0xb400007cabca1f70 + | sysTid=29011 nice=0 cgrp=top-app sched=0/0 handle=0x7ac77fbcb0 + | state=S schedstat=( 7302250 10493628 107 ) utm=0 stm=0 core=1 HZ=100 + | stack=0x7ac76f8000-0x7ac76fa000 stackSize=1039KB + | held mutexes= + native: #00 pc 00000000000a33b8 /apex/com.android.runtime/lib64/bionic/libc.so (__epoll_pwait+8) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #01 pc 0000000000010dfc /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+176) (BuildId: 5a0d720732600c94ad8354a1188e9f52) + native: #02 pc 000000000015a56c /system/lib64/libandroid_runtime.so (android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long, int)+44) (BuildId: a31474ac581b716d4588f8c97eb06009) + at android.os.MessageQueue.nativePollOnce(Native method) + at android.os.MessageQueue.next(MessageQueue.java:335) + at android.os.Looper.loopOnce(Looper.java:161) + at android.os.Looper.loop(Looper.java:288) + at android.os.HandlerThread.run(HandlerThread.java:67) + +"hwuiTask0" daemon prio=6 tid=31 Native + | group="main" sCount=1 ucsCount=0 flags=1 obj=0x136c1248 self=0xb400007cabca3b40 + | sysTid=29026 nice=-2 cgrp=top-app sched=0/0 handle=0x7ac75f3cb0 + | state=S schedstat=( 148373 0 2 ) utm=0 stm=0 core=1 HZ=100 + | stack=0x7ac74fc000-0x7ac74fe000 stackSize=991KB + | held mutexes= + native: #00 pc 000000000004df5c /apex/com.android.runtime/lib64/bionic/libc.so (syscall+28) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #01 pc 0000000000052664 /apex/com.android.runtime/lib64/bionic/libc.so (__futex_wait_ex(void volatile*, bool, int, bool, timespec const*)+144) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #02 pc 00000000000b56cc /apex/com.android.runtime/lib64/bionic/libc.so (pthread_cond_wait+76) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #03 pc 00000000000699e0 /system/lib64/libc++.so (std::__1::condition_variable::wait(std::__1::unique_lock&)+20) (BuildId: 6ae0290e5bfb8abb216bde2a4ee48d9e) + native: #04 pc 0000000000250af8 /system/lib64/libhwui.so (android::uirenderer::CommonPool::workerLoop()+96) (BuildId: 5e787210ce0f171dbee073e4a14a376c) + native: #05 pc 0000000000250d5c /system/lib64/libhwui.so (android::uirenderer::CommonPool::CommonPool()::$_0::operator()() const (.__uniq.99815402873434996937524029735804459536)+188) (BuildId: 5e787210ce0f171dbee073e4a14a376c) + native: #06 pc 0000000000250c9c /system/lib64/libhwui.so (void* std::__1::__thread_proxy >, android::uirenderer::CommonPool::CommonPool()::$_0> >(void*) (.__uniq.99815402873434996937524029735804459536)+40) (BuildId: 5e787210ce0f171dbee073e4a14a376c) + native: #07 pc 00000000000b63b0 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+208) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #08 pc 00000000000530b8 /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + (no managed stack frames) + +"hwuiTask1" daemon prio=6 tid=32 Native + | group="main" sCount=1 ucsCount=0 flags=1 obj=0x136c12c0 self=0xb400007cabcb19c0 + | sysTid=29027 nice=-2 cgrp=top-app sched=0/0 handle=0x7ab7b63cb0 + | state=S schedstat=( 112416 0 2 ) utm=0 stm=0 core=0 HZ=100 + | stack=0x7ab7a6c000-0x7ab7a6e000 stackSize=991KB + | held mutexes= + native: #00 pc 000000000004df5c /apex/com.android.runtime/lib64/bionic/libc.so (syscall+28) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #01 pc 0000000000052664 /apex/com.android.runtime/lib64/bionic/libc.so (__futex_wait_ex(void volatile*, bool, int, bool, timespec const*)+144) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #02 pc 00000000000b56cc /apex/com.android.runtime/lib64/bionic/libc.so (pthread_cond_wait+76) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #03 pc 00000000000699e0 /system/lib64/libc++.so (std::__1::condition_variable::wait(std::__1::unique_lock&)+20) (BuildId: 6ae0290e5bfb8abb216bde2a4ee48d9e) + native: #04 pc 0000000000250af8 /system/lib64/libhwui.so (android::uirenderer::CommonPool::workerLoop()+96) (BuildId: 5e787210ce0f171dbee073e4a14a376c) + native: #05 pc 0000000000250d5c /system/lib64/libhwui.so (android::uirenderer::CommonPool::CommonPool()::$_0::operator()() const (.__uniq.99815402873434996937524029735804459536)+188) (BuildId: 5e787210ce0f171dbee073e4a14a376c) + native: #06 pc 0000000000250c9c /system/lib64/libhwui.so (void* std::__1::__thread_proxy >, android::uirenderer::CommonPool::CommonPool()::$_0> >(void*) (.__uniq.99815402873434996937524029735804459536)+40) (BuildId: 5e787210ce0f171dbee073e4a14a376c) + native: #07 pc 00000000000b63b0 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+208) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #08 pc 00000000000530b8 /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + (no managed stack frames) + +"Okio Watchdog" daemon prio=5 tid=33 Waiting + | group="main" sCount=1 ucsCount=0 flags=1 obj=0x136c1338 self=0xb400007cabcb6d30 + | sysTid=29029 nice=0 cgrp=top-app sched=0/0 handle=0x7ab7967cb0 + | state=S schedstat=( 231164 396334 6 ) utm=0 stm=0 core=0 HZ=100 + | stack=0x7ab7864000-0x7ab7866000 stackSize=1039KB + | held mutexes= + at java.lang.Object.wait(Native method) + - waiting on <0x004b4344> (a java.lang.Class) + at java.lang.Object.wait(Object.java:442) + at java.lang.Object.wait(Object.java:568) + at com.android.okhttp.okio.AsyncTimeout.awaitTimeout(AsyncTimeout.java:313) + - locked <0x004b4344> (a java.lang.Class) + at com.android.okhttp.okio.AsyncTimeout.access$000(AsyncTimeout.java:42) + at com.android.okhttp.okio.AsyncTimeout$Watchdog.run(AsyncTimeout.java:288) + +"binder:28941_4" prio=5 tid=35 Native + | group="main" sCount=1 ucsCount=0 flags=1 obj=0x136c13b0 self=0xb400007cabcb8900 + | sysTid=29039 nice=0 cgrp=top-app sched=0/0 handle=0x7ab06b5cb0 + | state=S schedstat=( 11108162 4295500 140 ) utm=1 stm=0 core=2 HZ=100 + | stack=0x7ab05be000-0x7ab05c0000 stackSize=991KB + | held mutexes= + native: #00 pc 00000000000a23d8 /apex/com.android.runtime/lib64/bionic/libc.so (__ioctl+8) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #01 pc 000000000005b50c /apex/com.android.runtime/lib64/bionic/libc.so (ioctl+156) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #02 pc 0000000000094690 /system/lib64/libbinder.so (android::IPCThreadState::joinThreadPool(bool)+316) (BuildId: ee18e52b95e38eaab55a9a48518c8c3b) + native: #03 pc 0000000000094540 /system/lib64/libbinder.so (android::PoolThread::threadLoop()+24) (BuildId: ee18e52b95e38eaab55a9a48518c8c3b) + native: #04 pc 00000000000148e8 /system/lib64/libutils.so (android::Thread::_threadLoop(void*)+528) (BuildId: 5a0d720732600c94ad8354a1188e9f52) + native: #05 pc 00000000000c8918 /system/lib64/libandroid_runtime.so (android::AndroidRuntime::javaThreadShell(void*)+140) (BuildId: a31474ac581b716d4588f8c97eb06009) + native: #06 pc 00000000000b63b0 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+208) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #07 pc 00000000000530b8 /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + (no managed stack frames) + +"Thread-9" prio=5 tid=5 Sleeping + | group="main" sCount=1 ucsCount=0 flags=1 obj=0x136c1600 self=0xb400007cabcae220 + | sysTid=29157 nice=0 cgrp=top-app sched=0/0 handle=0x7b21c88cb0 + | state=S schedstat=( 38083 149917 1 ) utm=0 stm=0 core=1 HZ=100 + | stack=0x7b21b85000-0x7b21b87000 stackSize=1039KB + | held mutexes= + at java.lang.Thread.sleep(Native method) + - sleeping on <0x09228c2d> (a java.lang.Object) + at java.lang.Thread.sleep(Thread.java:450) + - locked <0x09228c2d> (a java.lang.Object) + at java.lang.Thread.sleep(Thread.java:355) + at io.sentry.samples.android.MainActivity$1.run(MainActivity.java:162) + - locked <0x0d3a2f0a> (a java.lang.Object) + at java.lang.Thread.run(Thread.java:1012) + +"binder:28941_3" prio=5 (not attached) + | sysTid=29028 nice=0 cgrp=top-app + | state=S schedstat=( 3124378 30612789 84 ) utm=0 stm=0 core=0 HZ=100 + native: #00 pc 000000000004df5c /apex/com.android.runtime/lib64/bionic/libc.so (syscall+28) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #01 pc 0000000000052664 /apex/com.android.runtime/lib64/bionic/libc.so (__futex_wait_ex(void volatile*, bool, int, bool, timespec const*)+144) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #02 pc 00000000000b56cc /apex/com.android.runtime/lib64/bionic/libc.so (pthread_cond_wait+76) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #03 pc 00000000000699e0 /system/lib64/libc++.so (std::__1::condition_variable::wait(std::__1::unique_lock&)+20) (BuildId: 6ae0290e5bfb8abb216bde2a4ee48d9e) + native: #04 pc 00000000000a048c /system/lib64/libgui.so (android::AsyncWorker::run()+112) (BuildId: 383a37b5342fd0249afb25e7134deb33) + native: #05 pc 00000000000a0878 /system/lib64/libgui.so (void* std::__1::__thread_proxy >, void (android::AsyncWorker::*)(), android::AsyncWorker*> >(void*)+80) (BuildId: 383a37b5342fd0249afb25e7134deb33) + native: #06 pc 00000000000b63b0 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+208) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + native: #07 pc 00000000000530b8 /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+64) (BuildId: 01331f74b0bb2cb958bdc15282b8ec7b) + +----- end 28941 ----- + +----- Waiting Channels: pid 28941 at 2023-04-04 22:06:31.057056350+0200 ----- +Cmd line: io.sentry.samples.android + +sysTid=28941 futex_wait_queue_me +sysTid=28957 do_sigtimedwait +sysTid=28959 pipe_read +sysTid=28960 do_sys_poll +sysTid=28961 futex_wait_queue_me +sysTid=28962 futex_wait_queue_me +sysTid=28963 futex_wait_queue_me +sysTid=28964 futex_wait_queue_me +sysTid=28965 futex_wait_queue_me +sysTid=28966 binder_wait_for_work +sysTid=28967 binder_wait_for_work +sysTid=28975 binder_wait_for_work +sysTid=28980 futex_wait_queue_me +sysTid=28991 do_epoll_wait +sysTid=28993 futex_wait_queue_me +sysTid=28994 futex_wait_queue_me +sysTid=28995 inotify_read +sysTid=28996 futex_wait_queue_me +sysTid=28997 do_epoll_wait +sysTid=29000 do_epoll_wait +sysTid=29001 futex_wait_queue_me +sysTid=29004 do_epoll_wait +sysTid=29010 futex_wait_queue_me +sysTid=29011 do_epoll_wait +sysTid=29026 futex_wait_queue_me +sysTid=29027 futex_wait_queue_me +sysTid=29028 futex_wait_queue_me +sysTid=29029 futex_wait_queue_me +sysTid=29039 binder_wait_for_work +sysTid=29157 futex_wait_queue_me + +----- end 28941 ----- diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index d10f74b40b3..a94b9a32fad 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -1498,6 +1498,7 @@ public final class io/sentry/SentryEvent$JsonKeys { public final class io/sentry/SentryExceptionFactory { public fun (Lio/sentry/SentryStackTraceFactory;)V public fun getSentryExceptions (Ljava/lang/Throwable;)Ljava/util/List; + public fun getSentryExceptionsFromThread (Lio/sentry/protocol/SentryThread;Lio/sentry/protocol/Mechanism;Ljava/lang/Throwable;)Ljava/util/List; } public final class io/sentry/SentryInstantDate : io/sentry/SentryDate { @@ -1550,6 +1551,46 @@ public final class io/sentry/SentryLevel : java/lang/Enum, io/sentry/JsonSeriali public static fun values ()[Lio/sentry/SentryLevel; } +public final class io/sentry/SentryLockReason : io/sentry/JsonSerializable, io/sentry/JsonUnknown { + public static final field ANY I + public static final field BLOCKED I + public static final field LOCKED I + public static final field SLEEPING I + public static final field WAITING I + public fun ()V + public fun (Lio/sentry/SentryLockReason;)V + public fun equals (Ljava/lang/Object;)Z + public fun getAddress ()Ljava/lang/String; + public fun getClassName ()Ljava/lang/String; + public fun getPackageName ()Ljava/lang/String; + public fun getThreadId ()Ljava/lang/Long; + public fun getType ()I + public fun getUnknown ()Ljava/util/Map; + public fun hashCode ()I + public fun serialize (Lio/sentry/JsonObjectWriter;Lio/sentry/ILogger;)V + public fun setAddress (Ljava/lang/String;)V + public fun setClassName (Ljava/lang/String;)V + public fun setPackageName (Ljava/lang/String;)V + public fun setThreadId (Ljava/lang/Long;)V + public fun setType (I)V + public fun setUnknown (Ljava/util/Map;)V +} + +public final class io/sentry/SentryLockReason$Deserializer : io/sentry/JsonDeserializer { + public fun ()V + public fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Lio/sentry/SentryLockReason; + public synthetic fun deserialize (Lio/sentry/JsonObjectReader;Lio/sentry/ILogger;)Ljava/lang/Object; +} + +public final class io/sentry/SentryLockReason$JsonKeys { + public static final field ADDRESS Ljava/lang/String; + public static final field CLASS_NAME Ljava/lang/String; + public static final field PACKAGE_NAME Ljava/lang/String; + public static final field THREAD_ID Ljava/lang/String; + public static final field TYPE Ljava/lang/String; + public fun ()V +} + public final class io/sentry/SentryLongDate : io/sentry/SentryDate { public fun (J)V public fun nanoTimestamp ()J @@ -1800,9 +1841,10 @@ public final class io/sentry/SentrySpanStorage { } public final class io/sentry/SentryStackTraceFactory { - public fun (Ljava/util/List;Ljava/util/List;)V + public fun (Lio/sentry/SentryOptions;)V public fun getInAppCallStack ()Ljava/util/List; public fun getStackFrames ([Ljava/lang/StackTraceElement;)Ljava/util/List; + public fun isInApp (Ljava/lang/String;)Ljava/lang/Boolean; } public final class io/sentry/SentryThreadFactory { @@ -3516,6 +3558,7 @@ public final class io/sentry/protocol/SentryStackFrame : io/sentry/JsonSerializa public fun getPostContext ()Ljava/util/List; public fun getPreContext ()Ljava/util/List; public fun getRawFunction ()Ljava/lang/String; + public fun getSymbol ()Ljava/lang/String; public fun getSymbolAddr ()Ljava/lang/String; public fun getUnknown ()Ljava/util/Map; public fun getVars ()Ljava/util/Map; @@ -3539,6 +3582,7 @@ public final class io/sentry/protocol/SentryStackFrame : io/sentry/JsonSerializa public fun setPostContext (Ljava/util/List;)V public fun setPreContext (Ljava/util/List;)V public fun setRawFunction (Ljava/lang/String;)V + public fun setSymbol (Ljava/lang/String;)V public fun setSymbolAddr (Ljava/lang/String;)V public fun setUnknown (Ljava/util/Map;)V public fun setVars (Ljava/util/Map;)V @@ -3565,6 +3609,7 @@ public final class io/sentry/protocol/SentryStackFrame$JsonKeys { public static final field PACKAGE Ljava/lang/String; public static final field PLATFORM Ljava/lang/String; public static final field RAW_FUNCTION Ljava/lang/String; + public static final field SYMBOL Ljava/lang/String; public static final field SYMBOL_ADDR Ljava/lang/String; public fun ()V } @@ -3598,6 +3643,7 @@ public final class io/sentry/protocol/SentryStackTrace$JsonKeys { public final class io/sentry/protocol/SentryThread : io/sentry/JsonSerializable, io/sentry/JsonUnknown { public fun ()V + public fun getHeldLocks ()Ljava/util/Map; public fun getId ()Ljava/lang/Long; public fun getName ()Ljava/lang/String; public fun getPriority ()Ljava/lang/Integer; @@ -3612,6 +3658,7 @@ public final class io/sentry/protocol/SentryThread : io/sentry/JsonSerializable, public fun setCrashed (Ljava/lang/Boolean;)V public fun setCurrent (Ljava/lang/Boolean;)V public fun setDaemon (Ljava/lang/Boolean;)V + public fun setHeldLocks (Ljava/util/Map;)V public fun setId (Ljava/lang/Long;)V public fun setMain (Ljava/lang/Boolean;)V public fun setName (Ljava/lang/String;)V @@ -3631,6 +3678,7 @@ public final class io/sentry/protocol/SentryThread$JsonKeys { public static final field CRASHED Ljava/lang/String; public static final field CURRENT Ljava/lang/String; public static final field DAEMON Ljava/lang/String; + public static final field HELD_LOCKS Ljava/lang/String; public static final field ID Ljava/lang/String; public static final field MAIN Ljava/lang/String; public static final field NAME Ljava/lang/String; diff --git a/sentry/src/main/java/io/sentry/JsonSerializer.java b/sentry/src/main/java/io/sentry/JsonSerializer.java index e78ec2265d4..8018211d567 100644 --- a/sentry/src/main/java/io/sentry/JsonSerializer.java +++ b/sentry/src/main/java/io/sentry/JsonSerializer.java @@ -98,6 +98,7 @@ public JsonSerializer(@NotNull SentryOptions options) { deserializersByClass.put(SentryException.class, new SentryException.Deserializer()); deserializersByClass.put(SentryItemType.class, new SentryItemType.Deserializer()); deserializersByClass.put(SentryLevel.class, new SentryLevel.Deserializer()); + deserializersByClass.put(SentryLockReason.class, new SentryLockReason.Deserializer()); deserializersByClass.put(SentryPackage.class, new SentryPackage.Deserializer()); deserializersByClass.put(SentryRuntime.class, new SentryRuntime.Deserializer()); deserializersByClass.put(SentrySpan.class, new SentrySpan.Deserializer()); diff --git a/sentry/src/main/java/io/sentry/MainEventProcessor.java b/sentry/src/main/java/io/sentry/MainEventProcessor.java index ad8fc836128..eaacc5fd643 100644 --- a/sentry/src/main/java/io/sentry/MainEventProcessor.java +++ b/sentry/src/main/java/io/sentry/MainEventProcessor.java @@ -32,8 +32,7 @@ public MainEventProcessor(final @NotNull SentryOptions options) { this.options = Objects.requireNonNull(options, "The SentryOptions is required."); final SentryStackTraceFactory sentryStackTraceFactory = - new SentryStackTraceFactory( - this.options.getInAppExcludes(), this.options.getInAppIncludes()); + new SentryStackTraceFactory(this.options); sentryExceptionFactory = new SentryExceptionFactory(sentryStackTraceFactory); sentryThreadFactory = new SentryThreadFactory(sentryStackTraceFactory, this.options); diff --git a/sentry/src/main/java/io/sentry/SentryExceptionFactory.java b/sentry/src/main/java/io/sentry/SentryExceptionFactory.java index 49be9100447..f268a11bf61 100644 --- a/sentry/src/main/java/io/sentry/SentryExceptionFactory.java +++ b/sentry/src/main/java/io/sentry/SentryExceptionFactory.java @@ -5,6 +5,7 @@ import io.sentry.protocol.SentryException; import io.sentry.protocol.SentryStackFrame; import io.sentry.protocol.SentryStackTrace; +import io.sentry.protocol.SentryThread; import io.sentry.util.Objects; import java.util.ArrayDeque; import java.util.ArrayList; @@ -34,6 +35,22 @@ public SentryExceptionFactory(final @NotNull SentryStackTraceFactory sentryStack Objects.requireNonNull(sentryStackTraceFactory, "The SentryStackTraceFactory is required."); } + @NotNull + public List getSentryExceptionsFromThread( + final @NotNull SentryThread thread, + final @NotNull Mechanism mechanism, + final @NotNull Throwable throwable) { + final SentryStackTrace threadStacktrace = thread.getStacktrace(); + if (threadStacktrace == null) { + return new ArrayList<>(0); + } + final List exceptions = new ArrayList<>(1); + exceptions.add( + getSentryException( + throwable, mechanism, thread.getId(), threadStacktrace.getFrames(), true)); + return exceptions; + } + /** * Creates a new instance from the given {@code throwable}. * @@ -63,14 +80,17 @@ public List getSentryExceptions(final @NotNull Throwable throwa * @param throwable Java exception to send to Sentry. * @param exceptionMechanism The optional {@link Mechanism} of the {@code throwable}. Or null if * none exist. - * @param thread The optional {@link Thread} which the exception originated. Or null if not known. + * @param threadId The optional id of a {@link Thread} which the exception originated. Or null if + * not known. + * @param frames stack frames that should be assigned to the stacktrace of this exception. * @param snapshot if the captured {@link java.lang.Thread}'s stacktrace is a snapshot, See {@link * SentryStackTrace#getSnapshot()} */ private @NotNull SentryException getSentryException( @NotNull final Throwable throwable, @Nullable final Mechanism exceptionMechanism, - @Nullable final Thread thread, + @Nullable final Long threadId, + @Nullable final List frames, final boolean snapshot) { final Package exceptionPackage = throwable.getClass().getPackage(); @@ -88,8 +108,6 @@ public List getSentryExceptions(final @NotNull Throwable throwa final String exceptionPackageName = exceptionPackage != null ? exceptionPackage.getName() : null; - final List frames = - sentryStackTraceFactory.getStackFrames(throwable.getStackTrace()); if (frames != null && !frames.isEmpty()) { final SentryStackTrace sentryStackTrace = new SentryStackTrace(frames); if (snapshot) { @@ -98,9 +116,7 @@ public List getSentryExceptions(final @NotNull Throwable throwa exception.setStacktrace(sentryStackTrace); } - if (thread != null) { - exception.setThreadId(thread.getId()); - } + exception.setThreadId(threadId); exception.setType(exceptionClassName); exception.setMechanism(exceptionMechanism); exception.setModule(exceptionPackageName); @@ -143,8 +159,11 @@ Deque extractExceptionQueue(final @NotNull Throwable throwable) thread = Thread.currentThread(); } + final List frames = + sentryStackTraceFactory.getStackFrames(currentThrowable.getStackTrace()); SentryException exception = - getSentryException(currentThrowable, exceptionMechanism, thread, snapshot); + getSentryException( + currentThrowable, exceptionMechanism, thread.getId(), frames, snapshot); exceptions.addFirst(exception); currentThrowable = currentThrowable.getCause(); } diff --git a/sentry/src/main/java/io/sentry/SentryLockReason.java b/sentry/src/main/java/io/sentry/SentryLockReason.java new file mode 100644 index 00000000000..607e461ee96 --- /dev/null +++ b/sentry/src/main/java/io/sentry/SentryLockReason.java @@ -0,0 +1,187 @@ +package io.sentry; + +import io.sentry.util.CollectionUtils; +import io.sentry.util.Objects; +import io.sentry.vendor.gson.stream.JsonToken; +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** Represents an instance of a held lock (java monitor object) in a thread. */ +public final class SentryLockReason implements JsonUnknown, JsonSerializable { + + public static final int LOCKED = 1; + public static final int WAITING = 2; + public static final int SLEEPING = 4; + public static final int BLOCKED = 8; + + public static final int ANY = LOCKED | WAITING | SLEEPING | BLOCKED; + + private int type; + private @Nullable String address; + private @Nullable String packageName; + private @Nullable String className; + private @Nullable Long threadId; + private @Nullable Map unknown; + + public SentryLockReason() {} + + public SentryLockReason(final @NotNull SentryLockReason other) { + this.type = other.type; + this.address = other.address; + this.packageName = other.packageName; + this.className = other.className; + this.threadId = other.threadId; + this.unknown = CollectionUtils.newConcurrentHashMap(other.unknown); + } + + @SuppressWarnings("unused") + public int getType() { + return type; + } + + public void setType(final int type) { + this.type = type; + } + + @Nullable + public String getAddress() { + return address; + } + + public void setAddress(final @Nullable String address) { + this.address = address; + } + + @Nullable + public String getPackageName() { + return packageName; + } + + public void setPackageName(final @Nullable String packageName) { + this.packageName = packageName; + } + + @Nullable + public String getClassName() { + return className; + } + + public void setClassName(final @Nullable String className) { + this.className = className; + } + + @Nullable + public Long getThreadId() { + return threadId; + } + + public void setThreadId(final @Nullable Long threadId) { + this.threadId = threadId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SentryLockReason that = (SentryLockReason) o; + return Objects.equals(address, that.address); + } + + @Override + public int hashCode() { + return Objects.hash(address); + } + + // region json + + @Nullable + @Override + public Map getUnknown() { + return unknown; + } + + @Override + public void setUnknown(final @Nullable Map unknown) { + this.unknown = unknown; + } + + public static final class JsonKeys { + public static final String TYPE = "type"; + public static final String ADDRESS = "address"; + public static final String PACKAGE_NAME = "package_name"; + public static final String CLASS_NAME = "class_name"; + public static final String THREAD_ID = "thread_id"; + } + + @Override + public void serialize(@NotNull JsonObjectWriter writer, @NotNull ILogger logger) + throws IOException { + writer.beginObject(); + writer.name(JsonKeys.TYPE).value(type); + if (address != null) { + writer.name(JsonKeys.ADDRESS).value(address); + } + if (packageName != null) { + writer.name(JsonKeys.PACKAGE_NAME).value(packageName); + } + if (className != null) { + writer.name(JsonKeys.CLASS_NAME).value(className); + } + if (threadId != null) { + writer.name(JsonKeys.THREAD_ID).value(threadId); + } + if (unknown != null) { + for (String key : unknown.keySet()) { + Object value = unknown.get(key); + writer.name(key); + writer.value(logger, value); + } + } + writer.endObject(); + } + + public static final class Deserializer implements JsonDeserializer { + + @Override + public @NotNull SentryLockReason deserialize( + @NotNull JsonObjectReader reader, @NotNull ILogger logger) throws Exception { + final SentryLockReason sentryLockReason = new SentryLockReason(); + Map unknown = null; + reader.beginObject(); + while (reader.peek() == JsonToken.NAME) { + final String nextName = reader.nextName(); + switch (nextName) { + case JsonKeys.TYPE: + sentryLockReason.type = reader.nextInt(); + break; + case JsonKeys.ADDRESS: + sentryLockReason.address = reader.nextStringOrNull(); + break; + case JsonKeys.PACKAGE_NAME: + sentryLockReason.packageName = reader.nextStringOrNull(); + break; + case JsonKeys.CLASS_NAME: + sentryLockReason.className = reader.nextStringOrNull(); + break; + case JsonKeys.THREAD_ID: + sentryLockReason.threadId = reader.nextLongOrNull(); + break; + default: + if (unknown == null) { + unknown = new ConcurrentHashMap<>(); + } + reader.nextUnknown(logger, unknown, nextName); + break; + } + } + sentryLockReason.setUnknown(unknown); + reader.endObject(); + return sentryLockReason; + } + } + + // endregion +} diff --git a/sentry/src/main/java/io/sentry/SentryStackTraceFactory.java b/sentry/src/main/java/io/sentry/SentryStackTraceFactory.java index d804c0a4d86..9e63e133f32 100644 --- a/sentry/src/main/java/io/sentry/SentryStackTraceFactory.java +++ b/sentry/src/main/java/io/sentry/SentryStackTraceFactory.java @@ -8,22 +8,15 @@ import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.TestOnly; /** class responsible for converting Java StackTraceElements to SentryStackFrames */ @ApiStatus.Internal public final class SentryStackTraceFactory { - /** list of inApp excludes */ - private final @Nullable List inAppExcludes; + private final @NotNull SentryOptions options; - /** list of inApp includes */ - private final @Nullable List inAppIncludes; - - public SentryStackTraceFactory( - @Nullable final List inAppExcludes, @Nullable List inAppIncludes) { - this.inAppExcludes = inAppExcludes; - this.inAppIncludes = inAppIncludes; + public SentryStackTraceFactory(final @NotNull SentryOptions options) { + this.options = options; } /** @@ -76,27 +69,26 @@ public List getStackFrames(@Nullable final StackTraceElement[] * @param className the className * @return true if it is or false otherwise */ - @TestOnly @Nullable - Boolean isInApp(final @Nullable String className) { + public Boolean isInApp(final @Nullable String className) { if (className == null || className.isEmpty()) { return true; } - if (inAppIncludes != null) { - for (String include : inAppIncludes) { - if (className.startsWith(include)) { - return true; - } + final List inAppIncludes = options.getInAppIncludes(); + for (String include : inAppIncludes) { + if (className.startsWith(include)) { + return true; } } - if (inAppExcludes != null) { - for (String exclude : inAppExcludes) { - if (className.startsWith(exclude)) { - return false; - } + + final List inAppExcludes = options.getInAppExcludes(); + for (String exclude : inAppExcludes) { + if (className.startsWith(exclude)) { + return false; } } + return null; } diff --git a/sentry/src/main/java/io/sentry/instrumentation/file/FileIOSpanManager.java b/sentry/src/main/java/io/sentry/instrumentation/file/FileIOSpanManager.java index 0ace5d8b63a..93127c20f20 100644 --- a/sentry/src/main/java/io/sentry/instrumentation/file/FileIOSpanManager.java +++ b/sentry/src/main/java/io/sentry/instrumentation/file/FileIOSpanManager.java @@ -39,8 +39,7 @@ final class FileIOSpanManager { this.currentSpan = currentSpan; this.file = file; this.options = options; - this.stackTraceFactory = - new SentryStackTraceFactory(options.getInAppExcludes(), options.getInAppIncludes()); + this.stackTraceFactory = new SentryStackTraceFactory(options); SentryIntegrationPackageStorage.getInstance().addIntegration("FileIO"); } diff --git a/sentry/src/main/java/io/sentry/protocol/SentryStackFrame.java b/sentry/src/main/java/io/sentry/protocol/SentryStackFrame.java index 832074f2a2f..210c7726d67 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryStackFrame.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryStackFrame.java @@ -93,6 +93,14 @@ public final class SentryStackFrame implements JsonUnknown, JsonSerializable { */ private @Nullable String instructionAddr; + /** + * Potentially mangled name of the symbol as it appears in an executable. + * + *

This is different from a function name by generally being the mangled name that appears + * natively in the binary. This is relevant for languages like Swift, C++ or Rust. + */ + private @Nullable String symbol; + @SuppressWarnings("unused") private @Nullable Map unknown; @@ -267,6 +275,15 @@ public void setRawFunction(final @Nullable String rawFunction) { this.rawFunction = rawFunction; } + @Nullable + public String getSymbol() { + return symbol; + } + + public void setSymbol(final @Nullable String symbol) { + this.symbol = symbol; + } + // region json @Nullable @@ -296,6 +313,7 @@ public static final class JsonKeys { public static final String SYMBOL_ADDR = "symbol_addr"; public static final String INSTRUCTION_ADDR = "instruction_addr"; public static final String RAW_FUNCTION = "raw_function"; + public static final String SYMBOL = "symbol"; } @Override @@ -347,6 +365,9 @@ public void serialize(@NotNull JsonObjectWriter writer, @NotNull ILogger logger) if (rawFunction != null) { writer.name(JsonKeys.RAW_FUNCTION).value(rawFunction); } + if (symbol != null) { + writer.name(JsonKeys.SYMBOL).value(symbol); + } if (unknown != null) { for (String key : unknown.keySet()) { Object value = unknown.get(key); @@ -412,6 +433,9 @@ public static final class Deserializer implements JsonDeserializer(); diff --git a/sentry/src/main/java/io/sentry/protocol/SentryThread.java b/sentry/src/main/java/io/sentry/protocol/SentryThread.java index 1c955064e33..b3e1c7c6a66 100644 --- a/sentry/src/main/java/io/sentry/protocol/SentryThread.java +++ b/sentry/src/main/java/io/sentry/protocol/SentryThread.java @@ -6,8 +6,10 @@ import io.sentry.JsonObjectWriter; import io.sentry.JsonSerializable; import io.sentry.JsonUnknown; +import io.sentry.SentryLockReason; import io.sentry.vendor.gson.stream.JsonToken; import java.io.IOException; +import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.jetbrains.annotations.NotNull; @@ -38,6 +40,8 @@ public final class SentryThread implements JsonUnknown, JsonSerializable { private @Nullable Boolean main; private @Nullable SentryStackTrace stacktrace; + private @Nullable Map heldLocks; + @SuppressWarnings("unused") private @Nullable Map unknown; @@ -208,6 +212,24 @@ public void setState(final @Nullable String state) { this.state = state; } + /** + * Gets locks held by this thread. + * + * @return locks held by this thread + */ + public @Nullable Map getHeldLocks() { + return heldLocks; + } + + /** + * Sets locks held by this thread. + * + * @param heldLocks list of locks held by this thread + */ + public void setHeldLocks(final @Nullable Map heldLocks) { + this.heldLocks = heldLocks; + } + // region json @Nullable @@ -231,6 +253,7 @@ public static final class JsonKeys { public static final String DAEMON = "daemon"; public static final String MAIN = "main"; public static final String STACKTRACE = "stacktrace"; + public static final String HELD_LOCKS = "held_locks"; } @Override @@ -264,6 +287,9 @@ public void serialize(@NotNull JsonObjectWriter writer, @NotNull ILogger logger) if (stacktrace != null) { writer.name(JsonKeys.STACKTRACE).value(logger, stacktrace); } + if (heldLocks != null) { + writer.name(JsonKeys.HELD_LOCKS).value(logger, heldLocks); + } if (unknown != null) { for (String key : unknown.keySet()) { Object value = unknown.get(key); @@ -313,6 +339,13 @@ public static final class Deserializer implements JsonDeserializer sentryThread.stacktrace = reader.nextOrNull(logger, new SentryStackTrace.Deserializer()); break; + case JsonKeys.HELD_LOCKS: + final Map heldLocks = + reader.nextMapOrNull(logger, new SentryLockReason.Deserializer()); + if (heldLocks != null) { + sentryThread.heldLocks = new HashMap<>(heldLocks); + } + break; default: if (unknown == null) { unknown = new ConcurrentHashMap<>(); diff --git a/sentry/src/test/java/io/sentry/JsonUnknownSerializationTest.kt b/sentry/src/test/java/io/sentry/JsonUnknownSerializationTest.kt index f24ce46766f..2396275e61b 100644 --- a/sentry/src/test/java/io/sentry/JsonUnknownSerializationTest.kt +++ b/sentry/src/test/java/io/sentry/JsonUnknownSerializationTest.kt @@ -51,7 +51,7 @@ class JsonUnknownSerializationTest( companion object { @JvmStatic - @Parameterized.Parameters + @Parameterized.Parameters(name = "{0}") fun data(): Collection> { val app = givenJsonUnknown(App()) val breadcrumb = givenJsonUnknown(Breadcrumb()) @@ -75,6 +75,7 @@ class JsonUnknownSerializationTest( val sentryStackFrame = givenJsonUnknown(SentryStackFrame()) val sentryStackTrace = givenJsonUnknown(SentryStackTrace()) val sentryThread = givenJsonUnknown(SentryThread()) + val sentryLockReason = givenJsonUnknown(SentryLockReason()) val sentryTransaction = givenJsonUnknown(SentryTransactionSerializationTest.Fixture().getSut()) val session = givenJsonUnknown(SessionSerializationTest.Fixture().getSut()) val skdVersion = givenJsonUnknown(SdkVersion("3e934135-3f2b-49bc-8756-9f025b55143e", "3e31738e-4106-42d0-8be2-4a3a1bc648d3")) @@ -110,6 +111,7 @@ class JsonUnknownSerializationTest( arrayOf(sentryStackFrame, sentryStackFrame, SentryStackFrame.Deserializer()::deserialize), arrayOf(sentryStackTrace, sentryStackTrace, SentryStackTrace.Deserializer()::deserialize), arrayOf(sentryThread, sentryThread, SentryThread.Deserializer()::deserialize), + arrayOf(sentryLockReason, sentryLockReason, SentryLockReason.Deserializer()::deserialize), arrayOf(sentryTransaction, sentryTransaction, SentryTransaction.Deserializer()::deserialize), arrayOf(session, session, Session.Deserializer()::deserialize), arrayOf(skdVersion, skdVersion, SdkVersion.Deserializer()::deserialize), diff --git a/sentry/src/test/java/io/sentry/SentryExceptionFactoryTest.kt b/sentry/src/test/java/io/sentry/SentryExceptionFactoryTest.kt index 16120ce10dd..c4794afd39d 100644 --- a/sentry/src/test/java/io/sentry/SentryExceptionFactoryTest.kt +++ b/sentry/src/test/java/io/sentry/SentryExceptionFactoryTest.kt @@ -2,6 +2,9 @@ package io.sentry import io.sentry.exception.ExceptionMechanismException import io.sentry.protocol.Mechanism +import io.sentry.protocol.SentryStackFrame +import io.sentry.protocol.SentryStackTrace +import io.sentry.protocol.SentryThread import org.mockito.kotlin.any import org.mockito.kotlin.mock import org.mockito.kotlin.whenever @@ -15,7 +18,11 @@ import kotlin.test.assertTrue class SentryExceptionFactoryTest { private class Fixture { - fun getSut(stackTraceFactory: SentryStackTraceFactory = SentryStackTraceFactory(listOf("io.sentry"), listOf())): SentryExceptionFactory { + fun getSut( + stackTraceFactory: SentryStackTraceFactory = SentryStackTraceFactory( + SentryOptions().apply { addInAppExclude("io.sentry") } + ) + ): SentryExceptionFactory { return SentryExceptionFactory(stackTraceFactory) } } @@ -88,7 +95,8 @@ class SentryExceptionFactoryTest { fun `When ExceptionMechanismException has threads snapshot, stack trace should set snapshot flag`() { val error = Exception("Exception") - val throwable = ExceptionMechanismException(Mechanism(), error, Thread.currentThread(), true) + val throwable = + ExceptionMechanismException(Mechanism(), error, Thread.currentThread(), true) val sentryExceptions = fixture.getSut().getSentryExceptions(throwable) assertTrue(sentryExceptions[0].stacktrace?.snapshot!!) @@ -138,6 +146,49 @@ class SentryExceptionFactoryTest { assertEquals(thread.id, queue.first.threadId) } + @Test + fun `returns empty list if stacktrace is not available for SentryThread`() { + val thread = SentryThread() + val mechanism = Mechanism() + val throwable = Exception("msg") + + val exceptions = fixture.getSut().getSentryExceptionsFromThread(thread, mechanism, throwable) + + assertTrue(exceptions.isEmpty()) + } + + @Test + fun `returns proper exception backfilled from SentryThread`() { + val thread = SentryThread().apply { + id = 121 + stacktrace = SentryStackTrace().apply { + frames = listOf( + SentryStackFrame().apply { + lineno = 777 + module = "io.sentry.samples.MainActivity" + function = "run" + } + ) + } + } + val mechanism = Mechanism().apply { type = "AppExitInfo" } + val throwable = Exception("msg") + + val exceptions = fixture.getSut().getSentryExceptionsFromThread(thread, mechanism, throwable) + + val exception = exceptions.first() + assertEquals("AppExitInfo", exception.mechanism!!.type) + assertEquals("java.lang", exception.module) + assertEquals("Exception", exception.type) + assertEquals("msg", exception.value) + assertEquals(121, exception.threadId) + assertEquals(true, exception.stacktrace!!.snapshot) + val frame = exception.stacktrace!!.frames!!.first() + assertEquals("io.sentry.samples.MainActivity", frame.module) + assertEquals("run", frame.function) + assertEquals(777, frame.lineno) + } + internal class InnerClassThrowable constructor(cause: Throwable? = null) : Throwable(cause) private val anonymousException = object : Exception() { diff --git a/sentry/src/test/java/io/sentry/SentryStackTraceFactoryTest.kt b/sentry/src/test/java/io/sentry/SentryStackTraceFactoryTest.kt index 57fd6f05aff..8a3a90e6d30 100644 --- a/sentry/src/test/java/io/sentry/SentryStackTraceFactoryTest.kt +++ b/sentry/src/test/java/io/sentry/SentryStackTraceFactoryTest.kt @@ -9,7 +9,7 @@ import kotlin.test.assertNull import kotlin.test.assertTrue class SentryStackTraceFactoryTest { - private val sut = SentryStackTraceFactory(listOf(), listOf()) + private val sut = SentryStackTraceFactory(SentryOptions()) @Test fun `when getStackFrames is called passing a valid Array, not empty result`() { @@ -60,7 +60,9 @@ class SentryStackTraceFactoryTest { fun `when getStackFrames is called passing a valid inAppExcludes, inApp should be false if prefix matches it`() { val element = generateStackTrace("io.mysentry.MyActivity") val elements = arrayOf(element) - val sentryStackTraceFactory = SentryStackTraceFactory(listOf("io.mysentry"), null) + val sentryStackTraceFactory = SentryStackTraceFactory( + SentryOptions().apply { addInAppExclude("io.mysentry") } + ) val sentryElements = sentryStackTraceFactory.getStackFrames(elements) assertFalse(sentryElements!!.first().isInApp!!) @@ -70,7 +72,10 @@ class SentryStackTraceFactoryTest { fun `when getStackFrames is called passing a valid inAppExcludes, inApp should be undecided if prefix doesnt matches it`() { val element = generateStackTrace("io.myapp.MyActivity") val elements = arrayOf(element) - val sentryStackTraceFactory = SentryStackTraceFactory(listOf("io.mysentry"), null) + val sentryStackTraceFactory = SentryStackTraceFactory( + SentryOptions().apply { addInAppExclude("io.mysentry") } + + ) val sentryElements = sentryStackTraceFactory.getStackFrames(elements) assertNull(sentryElements!!.first().isInApp) @@ -80,7 +85,7 @@ class SentryStackTraceFactoryTest { fun `when getStackFrames is called passing an invalid inAppExcludes, inApp should undecided`() { val element = generateStackTrace("io.mysentry.MyActivity") val elements = arrayOf(element) - val sentryStackTraceFactory = SentryStackTraceFactory(null, null) + val sentryStackTraceFactory = SentryStackTraceFactory(SentryOptions()) val sentryElements = sentryStackTraceFactory.getStackFrames(elements) assertNull(sentryElements!!.first().isInApp) @@ -92,7 +97,9 @@ class SentryStackTraceFactoryTest { fun `when getStackFrames is called passing a valid inAppIncludes, inApp should be true if prefix matches it`() { val element = generateStackTrace("io.mysentry.MyActivity") val elements = arrayOf(element) - val sentryStackTraceFactory = SentryStackTraceFactory(null, listOf("io.mysentry")) + val sentryStackTraceFactory = SentryStackTraceFactory( + SentryOptions().apply { addInAppInclude("io.mysentry") } + ) val sentryElements = sentryStackTraceFactory.getStackFrames(elements) assertTrue(sentryElements!!.first().isInApp!!) @@ -102,7 +109,9 @@ class SentryStackTraceFactoryTest { fun `when getStackFrames is called passing a valid inAppIncludes, inApp should be undecided if prefix doesnt matches it`() { val element = generateStackTrace("io.myapp.MyActivity") val elements = arrayOf(element) - val sentryStackTraceFactory = SentryStackTraceFactory(null, listOf("io.mysentry")) + val sentryStackTraceFactory = SentryStackTraceFactory( + SentryOptions().apply { addInAppInclude("io.mysentry") } + ) val sentryElements = sentryStackTraceFactory.getStackFrames(elements) assertNull(sentryElements!!.first().isInApp) @@ -112,7 +121,7 @@ class SentryStackTraceFactoryTest { fun `when getStackFrames is called passing an invalid inAppIncludes, inApp should be undecided`() { val element = generateStackTrace("io.mysentry.MyActivity") val elements = arrayOf(element) - val sentryStackTraceFactory = SentryStackTraceFactory(null, null) + val sentryStackTraceFactory = SentryStackTraceFactory(SentryOptions()) val sentryElements = sentryStackTraceFactory.getStackFrames(elements) assertNull(sentryElements!!.first().isInApp) @@ -123,7 +132,12 @@ class SentryStackTraceFactoryTest { fun `when getStackFrames is called passing a valid inAppIncludes and inAppExcludes, inApp should take precedence`() { val element = generateStackTrace("io.mysentry.MyActivity") val elements = arrayOf(element) - val sentryStackTraceFactory = SentryStackTraceFactory(listOf("io.mysentry"), listOf("io.mysentry")) + val sentryStackTraceFactory = SentryStackTraceFactory( + SentryOptions().apply { + addInAppExclude("io.mysentry") + addInAppInclude("io.mysentry") + } + ) val sentryElements = sentryStackTraceFactory.getStackFrames(elements) assertTrue(sentryElements!!.first().isInApp!!) @@ -131,7 +145,13 @@ class SentryStackTraceFactoryTest { @Test fun `when class is defined in the app, inApp is true`() { - val sentryStackTraceFactory = SentryStackTraceFactory(listOf("io.mysentry.not"), listOf("io.mysentry.inApp")) + val sentryStackTraceFactory = + SentryStackTraceFactory( + SentryOptions().apply { + addInAppExclude("io.mysentry.not") + addInAppInclude("io.mysentry.inApp") + } + ) assertTrue(sentryStackTraceFactory.isInApp("io.mysentry.inApp.ClassName")!!) assertTrue(sentryStackTraceFactory.isInApp("io.mysentry.inApp.somePackage.ClassName")!!) assertFalse(sentryStackTraceFactory.isInApp("io.mysentry.not.ClassName")!!) @@ -140,7 +160,9 @@ class SentryStackTraceFactoryTest { @Test fun `when class is not in the list, is left undecided`() { - val sentryStackTraceFactory = SentryStackTraceFactory(listOf(), listOf("io.mysentry")) + val sentryStackTraceFactory = SentryStackTraceFactory( + SentryOptions().apply { addInAppInclude("io.mysentry") } + ) assertNull(sentryStackTraceFactory.isInApp("com.getsentry")) } @@ -187,7 +209,9 @@ class SentryStackTraceFactoryTest { fun `when stacktrace is not available, returns empty list for call stack`() { val exception = Exception() exception.stackTrace = arrayOf() - val sut = SentryStackTraceFactory(listOf(), listOf("io.mysentry")) + val sut = SentryStackTraceFactory( + SentryOptions().apply { addInAppInclude("io.mysentry") } + ) val callStack = sut.getInAppCallStack(exception) @@ -202,7 +226,9 @@ class SentryStackTraceFactoryTest { generateStackTrace("io.sentry.instrumentation.file.SentryFileOutputStream"), generateStackTrace("com.example.myapp.MainActivity") ) - val sut = SentryStackTraceFactory(listOf(), listOf("io.mysentry")) + val sut = SentryStackTraceFactory( + SentryOptions().apply { addInAppInclude("io.mysentry") } + ) val callStack = sut.getInAppCallStack(exception) @@ -219,7 +245,9 @@ class SentryStackTraceFactoryTest { generateStackTrace("com.example.myapp.MainActivity"), generateStackTrace("com.thirdparty.Adapter") ) - val sut = SentryStackTraceFactory(listOf(), listOf("com.example")) + val sut = SentryStackTraceFactory( + SentryOptions().apply { addInAppInclude("com.example") } + ) val callStack = sut.getInAppCallStack(exception) @@ -238,7 +266,7 @@ class SentryStackTraceFactoryTest { generateStackTrace("sun.misc.unsafe.park.Object"), generateStackTrace("java.lang.Object") ) - val sut = SentryStackTraceFactory(listOf(), listOf()) + val sut = SentryStackTraceFactory(SentryOptions()) val callStack = sut.getInAppCallStack(exception) diff --git a/sentry/src/test/java/io/sentry/SentryThreadFactoryTest.kt b/sentry/src/test/java/io/sentry/SentryThreadFactoryTest.kt index 49065708c0c..bc8276ef05e 100644 --- a/sentry/src/test/java/io/sentry/SentryThreadFactoryTest.kt +++ b/sentry/src/test/java/io/sentry/SentryThreadFactoryTest.kt @@ -12,7 +12,7 @@ class SentryThreadFactoryTest { class Fixture { internal fun getSut(attachStacktrace: Boolean = true) = SentryThreadFactory( - SentryStackTraceFactory(listOf("io.sentry"), listOf()), + SentryStackTraceFactory(SentryOptions().apply { addInAppExclude("io.sentry") }), with(SentryOptions()) { isAttachStacktrace = attachStacktrace this diff --git a/sentry/src/test/java/io/sentry/protocol/SentryLockReasonSerializationTest.kt b/sentry/src/test/java/io/sentry/protocol/SentryLockReasonSerializationTest.kt new file mode 100644 index 00000000000..380f471f003 --- /dev/null +++ b/sentry/src/test/java/io/sentry/protocol/SentryLockReasonSerializationTest.kt @@ -0,0 +1,44 @@ +package io.sentry.protocol + +import io.sentry.ILogger +import io.sentry.SentryLockReason +import org.junit.Test +import org.mockito.kotlin.mock +import kotlin.test.assertEquals + +class SentryLockReasonSerializationTest { + + class Fixture { + val logger = mock() + + fun getSut() = SentryLockReason().apply { + address = "0x0d3a2f0a" + type = SentryLockReason.BLOCKED + threadId = 11 + className = "Object" + packageName = "java.lang" + } + } + private val fixture = Fixture() + + @Test + fun serialize() { + val expected = SerializationUtils.sanitizedFile("json/sentry_lock_reason.json") + val actual = SerializationUtils.serializeToString(fixture.getSut(), fixture.logger) + + assertEquals(expected, actual) + } + + @Test + fun deserialize() { + val expectedJson = SerializationUtils.sanitizedFile("json/sentry_lock_reason.json") + val actual = SerializationUtils.deserializeJson( + expectedJson, + SentryLockReason.Deserializer(), + fixture.logger + ) + val actualJson = SerializationUtils.serializeToString(actual, fixture.logger) + + assertEquals(expectedJson, actualJson) + } +} diff --git a/sentry/src/test/java/io/sentry/protocol/SentryStackFrameSerializationTest.kt b/sentry/src/test/java/io/sentry/protocol/SentryStackFrameSerializationTest.kt index 1f9d74321d3..d57b15e0a64 100644 --- a/sentry/src/test/java/io/sentry/protocol/SentryStackFrameSerializationTest.kt +++ b/sentry/src/test/java/io/sentry/protocol/SentryStackFrameSerializationTest.kt @@ -32,6 +32,7 @@ class SentryStackFrameSerializationTest { symbolAddr = "180e12cd-1fa8-405d-8dd8-e87b33fa2eb0" instructionAddr = "19864a78-2466-461f-9f0b-93a5c9ae7622" rawFunction = "f33035a4-0cf0-453d-b6f4-d7c27e9af924" + symbol = "d9807ffe-d517-11ed-afa1-0242ac120002" } } private val fixture = Fixture() diff --git a/sentry/src/test/java/io/sentry/protocol/SentryThreadSerializationTest.kt b/sentry/src/test/java/io/sentry/protocol/SentryThreadSerializationTest.kt index c3c5597ae3e..b1ba0af79c5 100644 --- a/sentry/src/test/java/io/sentry/protocol/SentryThreadSerializationTest.kt +++ b/sentry/src/test/java/io/sentry/protocol/SentryThreadSerializationTest.kt @@ -5,6 +5,7 @@ import io.sentry.ILogger import io.sentry.JsonObjectReader import io.sentry.JsonObjectWriter import io.sentry.JsonSerializable +import io.sentry.SentryLockReason import org.junit.Test import org.mockito.kotlin.mock import java.io.StringReader @@ -50,6 +51,15 @@ class SentryThreadSerializationTest { "e7a3db0b-8cad-4eab-8315-03d5dc2edcd9" to "8bba0819-ac58-4e5c-bec7-32e1033b7bdf" ) snapshot = true + heldLocks = mapOf( + "0x0d3a2f0a" to SentryLockReason().apply { + address = "0x0d3a2f0a" + className = "Object" + packageName = "java.lang" + type = SentryLockReason.BLOCKED + threadId = 11 + } + ) } } } diff --git a/sentry/src/test/resources/json/sentry_event.json b/sentry/src/test/resources/json/sentry_event.json index 55b057504be..3e1cc833226 100644 --- a/sentry/src/test/resources/json/sentry_event.json +++ b/sentry/src/test/resources/json/sentry_event.json @@ -52,6 +52,17 @@ "e7a3db0b-8cad-4eab-8315-03d5dc2edcd9": "8bba0819-ac58-4e5c-bec7-32e1033b7bdf" }, "snapshot": true + }, + "held_locks": + { + "0x0d3a2f0a": + { + "type": 8, + "address": "0x0d3a2f0a", + "package_name": "java.lang", + "class_name": "Object", + "thread_id": 11 + } } } ] diff --git a/sentry/src/test/resources/json/sentry_lock_reason.json b/sentry/src/test/resources/json/sentry_lock_reason.json new file mode 100644 index 00000000000..49fd6da5532 --- /dev/null +++ b/sentry/src/test/resources/json/sentry_lock_reason.json @@ -0,0 +1,7 @@ +{ + "type": 8, + "address": "0x0d3a2f0a", + "package_name": "java.lang", + "class_name": "Object", + "thread_id": 11 +} diff --git a/sentry/src/test/resources/json/sentry_stack_frame.json b/sentry/src/test/resources/json/sentry_stack_frame.json index 39c8687f8d7..1fd0182049e 100644 --- a/sentry/src/test/resources/json/sentry_stack_frame.json +++ b/sentry/src/test/resources/json/sentry_stack_frame.json @@ -13,5 +13,6 @@ "image_addr": "27ec1be5-e8a1-485c-b020-f4d9f80a6624", "symbol_addr": "180e12cd-1fa8-405d-8dd8-e87b33fa2eb0", "instruction_addr": "19864a78-2466-461f-9f0b-93a5c9ae7622", - "raw_function": "f33035a4-0cf0-453d-b6f4-d7c27e9af924" + "raw_function": "f33035a4-0cf0-453d-b6f4-d7c27e9af924", + "symbol": "d9807ffe-d517-11ed-afa1-0242ac120002" } diff --git a/sentry/src/test/resources/json/sentry_thread.json b/sentry/src/test/resources/json/sentry_thread.json index da11f6f7880..f2cb0098498 100644 --- a/sentry/src/test/resources/json/sentry_thread.json +++ b/sentry/src/test/resources/json/sentry_thread.json @@ -35,5 +35,16 @@ "e7a3db0b-8cad-4eab-8315-03d5dc2edcd9": "8bba0819-ac58-4e5c-bec7-32e1033b7bdf" }, "snapshot": true + }, + "held_locks": + { + "0x0d3a2f0a": + { + "type": 8, + "address": "0x0d3a2f0a", + "package_name": "java.lang", + "class_name": "Object", + "thread_id": 11 + } } }