Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

- Send `otel.kind` to Sentry ([#3907](https://github.com/getsentry/sentry-java/pull/3907))
- Allow passing `environment` to `CheckinUtils.withCheckIn` ([3889](https://github.com/getsentry/sentry-java/pull/3889))
- Changes up to `7.18.0` have been merged and are now included as well

### Fixes

Expand Down Expand Up @@ -59,6 +60,7 @@
- Uses faster Random implementation to generate UUIDs
- Android 15: Add support for 16KB page sizes ([#3851](https://github.com/getsentry/sentry-java/pull/3851))
- See https://developer.android.com/guide/practices/page-sizes for more details
- Changes up to `7.17.0` have been merged and are now included as well

### Fixes

Expand Down Expand Up @@ -326,6 +328,29 @@ You may also use `LifecycleHelper.close(token)`, e.g. in case you need to pass t

- Report exceptions returned by Throwable.getSuppressed() to Sentry as exception groups ([#3396] https://github.com/getsentry/sentry-java/pull/3396)

## 7.18.0

### Features

- Android 15: Add support for 16KB page sizes ([#3620](https://github.com/getsentry/sentry-java/pull/3620))
- See https://developer.android.com/guide/practices/page-sizes for more details
- Session Replay: Add `beforeSendReplay` callback ([#3855](https://github.com/getsentry/sentry-java/pull/3855))
- Session Replay: Add support for masking/unmasking view containers ([#3881](https://github.com/getsentry/sentry-java/pull/3881))

### Fixes

- Avoid collecting normal frames ([#3782](https://github.com/getsentry/sentry-java/pull/3782))
- Ensure android initialization process continues even if options configuration block throws an exception ([#3887](https://github.com/getsentry/sentry-java/pull/3887))
- Do not report parsing ANR error when there are no threads ([#3888](https://github.com/getsentry/sentry-java/pull/3888))
- This should significantly reduce the number of events with message "Sentry Android SDK failed to parse system thread dump..." reported
- Session Replay: Disable replay in session mode when rate limit is active ([#3854](https://github.com/getsentry/sentry-java/pull/3854))

### Dependencies

- Bump Native SDK from v0.7.2 to v0.7.8 ([#3620](https://github.com/getsentry/sentry-java/pull/3620))
- [changelog](https://github.com/getsentry/sentry-native/blob/master/CHANGELOG.md#078)
- [diff](https://github.com/getsentry/sentry-native/compare/0.7.2...0.7.8)

## 7.17.0

### Features
Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ spotless {
target("**/*.java")
removeUnusedImports()
googleJavaFormat()
targetExclude("**/generated/**", "**/vendor/**")
targetExclude("**/generated/**", "**/vendor/**", "**/sentry-native/**")
}
kotlin {
target("**/*.kt")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -313,8 +313,11 @@ private void reportAsSentryEvent(
final ThreadDumpParser threadDumpParser = new ThreadDumpParser(options, isBackground);
final List<SentryThread> threads = threadDumpParser.parse(lines);
if (threads.isEmpty()) {
// if the list is empty this means our regex matching is garbage and this is still error
return new ParseResult(ParseResult.Type.ERROR, dump);
// if the list is empty this means the system failed to capture a proper thread dump of
// the android threads, and only contains kernel-level threads and statuses, those ANRs
// are not actionable and neither they are reported by Google Play Console, so we just
// fall back to not reporting them
return new ParseResult(ParseResult.Type.NO_DUMP);
}
return new ParseResult(ParseResult.Type.DUMP, dump, threads);
} catch (Throwable e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,17 @@ public static void init(
isTimberAvailable,
isReplayAvailable);

configuration.configure(options);
try {
configuration.configure(options);
} catch (Throwable t) {
// let it slip, but log it
options
.getLogger()
.log(
SentryLevel.ERROR,
"Error in the 'OptionsConfiguration.configure' callback.",
t);
}

// if SentryPerformanceProvider was disabled or removed,
// we set the app start / sdk init time here instead
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
@ApiStatus.Internal
final class SentryFrameMetrics {

private int normalFrameCount;
private int slowFrameCount;
private int frozenFrameCount;

Expand All @@ -18,15 +17,11 @@ final class SentryFrameMetrics {
public SentryFrameMetrics() {}

public SentryFrameMetrics(
final int normalFrameCount,
final int slowFrameCount,
final long slowFrameDelayNanos,
final int frozenFrameCount,
final long frozenFrameDelayNanos,
final long totalDurationNanos) {

this.normalFrameCount = normalFrameCount;

this.slowFrameCount = slowFrameCount;
this.slowFrameDelayNanos = slowFrameDelayNanos;

Expand All @@ -47,15 +42,9 @@ public void addFrame(
} else if (isSlow) {
slowFrameDelayNanos += delayNanos;
slowFrameCount += 1;
} else {
normalFrameCount += 1;
}
}

public int getNormalFrameCount() {
return normalFrameCount;
}

public int getSlowFrameCount() {
return slowFrameCount;
}
Expand All @@ -72,17 +61,16 @@ public long getFrozenFrameDelayNanos() {
return frozenFrameDelayNanos;
}

public int getTotalFrameCount() {
return normalFrameCount + slowFrameCount + frozenFrameCount;
/** Returns the sum of the slow and frozen frames. */
public int getSlowFrozenFrameCount() {
return slowFrameCount + frozenFrameCount;
}

public long getTotalDurationNanos() {
return totalDurationNanos;
}

public void clear() {
normalFrameCount = 0;

slowFrameCount = 0;
slowFrameDelayNanos = 0;

Expand All @@ -95,7 +83,6 @@ public void clear() {
@NotNull
public SentryFrameMetrics duplicate() {
return new SentryFrameMetrics(
normalFrameCount,
slowFrameCount,
slowFrameDelayNanos,
frozenFrameCount,
Expand All @@ -110,7 +97,6 @@ public SentryFrameMetrics duplicate() {
@NotNull
public SentryFrameMetrics diffTo(final @NotNull SentryFrameMetrics other) {
return new SentryFrameMetrics(
normalFrameCount - other.normalFrameCount,
slowFrameCount - other.slowFrameCount,
slowFrameDelayNanos - other.slowFrameDelayNanos,
frozenFrameCount - other.frozenFrameCount,
Expand All @@ -123,8 +109,7 @@ public SentryFrameMetrics diffTo(final @NotNull SentryFrameMetrics other) {
* to 0
*/
public boolean containsValidData() {
return normalFrameCount >= 0
&& slowFrameCount >= 0
return slowFrameCount >= 0
&& slowFrameDelayNanos >= 0
&& frozenFrameCount >= 0
&& frozenFrameDelayNanos >= 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ private void captureFrameMetrics(@NotNull final ISpan span) {
}
}

int totalFrameCount = frameMetrics.getTotalFrameCount();
int totalFrameCount = frameMetrics.getSlowFrozenFrameCount();

final long nextScheduledFrameNanos = frameMetricsCollector.getLastKnownFrameStartTimeNanos();
// nextScheduledFrameNanos might be -1 if no frames have been scheduled for drawing yet
Expand Down Expand Up @@ -258,15 +258,17 @@ public void onFrameMetricCollected(
(long) ((double) ONE_SECOND_NANOS / (double) refreshRate);
lastKnownFrameDurationNanos = expectedFrameDurationNanos;

frames.add(
new Frame(
frameStartNanos,
frameEndNanos,
durationNanos,
delayNanos,
isSlow,
isFrozen,
expectedFrameDurationNanos));
if (isSlow || isFrozen) {
frames.add(
new Frame(
frameStartNanos,
frameEndNanos,
durationNanos,
delayNanos,
isSlow,
isFrozen,
expectedFrameDurationNanos));
}
}

private static int interpolateFrameCount(
Expand All @@ -281,7 +283,7 @@ private static int interpolateFrameCount(
final long frameMetricsDurationNanos = frameMetrics.getTotalDurationNanos();
final long nonRenderedDuration = spanDurationNanos - frameMetricsDurationNanos;
if (nonRenderedDuration > 0) {
return (int) (nonRenderedDuration / frameDurationNanos);
return (int) Math.ceil((double) nonRenderedDuration / frameDurationNanos);
}
return 0;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ class AnrV2IntegrationTest {
reason: Int? = ApplicationExitInfo.REASON_ANR,
timestamp: Long? = null,
importance: Int? = null,
addTrace: Boolean = true
addTrace: Boolean = true,
addBadTrace: Boolean = false
) {
val builder = ApplicationExitInfoBuilder.newBuilder()
if (reason != null) {
Expand All @@ -117,8 +118,36 @@ class AnrV2IntegrationTest {
if (!addTrace) {
return
}
whenever(mock.traceInputStream).thenReturn(
"""
if (addBadTrace) {
whenever(mock.traceInputStream).thenReturn(
"""
Subject: Input dispatching timed out (7985007 com.example.app/com.example.app.ui.MainActivity (server) is not responding. Waited 5000ms for FocusEvent(hasFocus=false))
Here are no Binder-related exception messages available.
Pid(12233) have D state thread(tid:12236 name:Signal Catcher)


RssHwmKb: 823716
RssKb: 548348
RssAnonKb: 382156
RssShmemKb: 13304
VmSwapKb: 82484


--- CriticalEventLog ---
capacity: 20
timestamp_ms: 1731507490032
window_ms: 300000

----- dumping pid: 12233 at 313446151
libdebuggerd_client: unexpected registration response: 0

----- Waiting Channels: pid 12233 at 2024-11-13 19:48:09.980104540+0530 -----
Cmd line: com.example.app:mainProcess
""".trimIndent().byteInputStream()
)
} else {
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
Expand Down Expand Up @@ -147,8 +176,9 @@ class AnrV2IntegrationTest {
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()
)
""".trimIndent().byteInputStream()
)
}
}
shadowActivityManager.addApplicationExitInfo(exitInfo)
}
Expand Down Expand Up @@ -551,4 +581,14 @@ class AnrV2IntegrationTest {

verify(fixture.scopes, never()).captureEvent(any(), anyOrNull<Hint>())
}

@Test
fun `when traceInputStream has bad data, does not report ANR`() {
val integration = fixture.getSut(tmpDir, lastReportedAnrTimestamp = oldTimestamp)
fixture.addAppExitInfo(timestamp = newTimestamp, addBadTrace = true)

integration.register(fixture.scopes, fixture.options)

verify(fixture.scopes, never()).captureEvent(any(), anyOrNull<Hint>())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,19 @@ class SentryAndroidTest {
assertEquals(99, AppStartMetrics.getInstance().appStartTimeSpan.startUptimeMs)
}

@Test
fun `if the config options block throws still intializes android event processors`() {
lateinit var optionsRef: SentryOptions
fixture.initSut(context = mock<Application>()) { options ->
optionsRef = options
options.dsn = "https://key@sentry.io/123"
throw RuntimeException("Boom!")
}

assertTrue(optionsRef.eventProcessors.any { it is DefaultAndroidEventProcessor })
assertTrue(optionsRef.eventProcessors.any { it is AnrV2EventProcessor })
}

private fun prefillScopeCache(cacheDir: String) {
val scopeDir = File(cacheDir, SCOPE_CACHE).also { it.mkdirs() }
File(scopeDir, BREADCRUMBS_FILENAME).writeText(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,6 @@ import kotlin.test.assertFalse
import kotlin.test.assertTrue

class SentryFrameMetricsTest {
@Test
fun addFastFrame() {
val frameMetrics = SentryFrameMetrics()
frameMetrics.addFrame(10, 0, false, false)
assertEquals(1, frameMetrics.normalFrameCount)

frameMetrics.addFrame(10, 0, false, false)
assertEquals(2, frameMetrics.normalFrameCount)
}

@Test
fun addSlowFrame() {
Expand Down Expand Up @@ -43,10 +34,12 @@ class SentryFrameMetricsTest {
@Test
fun totalFrameCount() {
val frameMetrics = SentryFrameMetrics()
// Normal frames are ignored
frameMetrics.addFrame(10, 0, false, false)
// Slow and frozen frames are considered
frameMetrics.addFrame(116, 100, true, false)
frameMetrics.addFrame(1016, 1000, true, true)
assertEquals(3, frameMetrics.totalFrameCount)
assertEquals(2, frameMetrics.slowFrozenFrameCount)
}

@Test
Expand All @@ -57,12 +50,11 @@ class SentryFrameMetricsTest {
frameMetrics.addFrame(1016, 1000, true, true)

val dup = frameMetrics.duplicate()
assertEquals(1, dup.normalFrameCount)
assertEquals(1, dup.slowFrameCount)
assertEquals(100, dup.slowFrameDelayNanos)
assertEquals(1, dup.frozenFrameCount)
assertEquals(1000, dup.frozenFrameDelayNanos)
assertEquals(3, dup.totalFrameCount)
assertEquals(2, dup.slowFrozenFrameCount)
}

@Test
Expand All @@ -89,7 +81,7 @@ class SentryFrameMetricsTest {
assertEquals(1, diff.frozenFrameCount)
assertEquals(1000, diff.frozenFrameDelayNanos)

assertEquals(2, diff.totalFrameCount)
assertEquals(2, diff.slowFrozenFrameCount)
}

@Test
Expand All @@ -102,12 +94,11 @@ class SentryFrameMetricsTest {

frameMetrics.clear()

assertEquals(0, frameMetrics.normalFrameCount)
assertEquals(0, frameMetrics.slowFrameCount)
assertEquals(0, frameMetrics.slowFrameDelayNanos)
assertEquals(0, frameMetrics.frozenFrameCount)
assertEquals(0, frameMetrics.frozenFrameDelayNanos)
assertEquals(0, frameMetrics.totalFrameCount)
assertEquals(0, frameMetrics.slowFrozenFrameCount)
}

@Test
Expand Down
Loading
Loading