Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
90322b3
Add collectAnrs to FeatureSettings
tejasd Apr 13, 2021
4e186a6
Add unit test to test settings received when anr collection is enabled
tejasd Apr 13, 2021
62e8c18
Cleanup json file
tejasd Apr 13, 2021
eb08e6b
Add ability to get the startTimestampMs of a session
tejasd Apr 14, 2021
ec3e5c9
Merge branch 'crashlytics/anrs' into td/anr-2
tejasd Apr 14, 2021
c5f5f26
Change Long to long
tejasd Apr 14, 2021
4d68ae4
Merge branch 'crashlytics/anrs' into td/anr
tejasd Apr 20, 2021
0422970
Add ApplicationExitInfo to CrashlyticsReport
tejasd Apr 21, 2021
b928e3f
Comment cleanup
tejasd Apr 21, 2021
20b4f30
Revert makeEvents refactoring
tejasd Apr 21, 2021
2c8f5a9
More cleanup
tejasd Apr 21, 2021
b0f9cc4
Merge branch 'crashlytics/anrs' into td/anr
tejasd Apr 22, 2021
3a56029
Merge branch 'crashlytics/anrs' into td/anr
tejasd Apr 23, 2021
3452ee4
Merge branch 'crashlytics/anrs' into td/anr-2
tejasd Apr 23, 2021
d1eeea8
AppExitInfo json transform
tejasd Apr 26, 2021
490733e
Merge branch 'crashlytics/anrs' into td/anr-2
tejasd Apr 26, 2021
5bb61f5
Merge branch 'crashlytics/anrs' into td/anr
tejasd Apr 26, 2021
1047983
AppExitInfo json transform
tejasd Apr 26, 2021
47511bc
Merge branch 'crashlytics/anrs' into td/anr-2
tejasd Apr 28, 2021
76f8438
Merge branch 'td/anr-2' into td/anr
tejasd Apr 28, 2021
2e17dee
Collect and Persist ANRs + robolectric tests fpr CrashlyticsControlle…
tejasd May 4, 2021
cb840c3
Add missing feature to read an app exit info when finalizing report +…
tejasd May 4, 2021
c780da6
Fix NPE and add relevant unit test
tejasd May 4, 2021
6c51f32
Add copyright to new test classes
tejasd May 4, 2021
91cda29
Merge branch 'crashlytics/anrs' into td/anr
tejasd May 11, 2021
dbc72dc
Merge branch 'crashlytics/anrs' into td/anr
tejasd May 14, 2021
b27e040
Add ApplicationExitInfo to Event.Application.Executor and make Thread…
tejasd May 14, 2021
bac6dcd
Merge branch 'crashlytics/anrs' into td/anr
tejasd May 14, 2021
08794f0
Refactor the persistence of AppExitInfo to move it to the Event
tejasd May 14, 2021
a085edc
Some cleanup
tejasd May 14, 2021
e069f72
Some cleanup + additional unit test
tejasd May 17, 2021
012d806
Try to fix tests
tejasd May 25, 2021
6a2dc78
Fix a broken unit test
tejasd May 25, 2021
2810b7b
Merge branch 'crashlytics/anrs' into td/anr
tejasd May 25, 2021
6a391ec
Merge branch 'crashlytics/anrs' into td/anr
tejasd May 26, 2021
8bad7a4
Revert Crashlytics androidTest manifest
tejasd May 27, 2021
eeb550b
Revert Crashlytics androidTest manifest
tejasd May 27, 2021
dbe533a
java format
tejasd May 27, 2021
d603cba
Clean up old usage of AppExitInfo
tejasd May 31, 2021
95a3c56
Add custom keys and logs to ANR event
tejasd May 31, 2021
c22350d
Fix unit test + cleanup with orientation in ANR event
tejasd May 31, 2021
8d00013
Revert orientation for ANRs
tejasd May 31, 2021
e32e503
Merge branch 'crashlytics/anrs' into td/anr
tejasd Jun 4, 2021
f27bbc7
Add ability to add logs and breadcrumbs to ANR events
tejasd Jun 4, 2021
5129de9
Revert "Add ability to add logs and breadcrumbs to ANR events"
tejasd Jun 4, 2021
21d4f2a
Merge branch 'crashlytics/anrs' into td/anr
tejasd Jun 4, 2021
8f1af2c
Change device to API 30
tejasd Jun 4, 2021
ca67b3f
Merge branch 'crashlytics/anrs' into td/anr
tejasd Jun 15, 2021
167f2c1
Clean up unit test
tejasd Jun 23, 2021
fa7caa1
Merge branch 'crashlytics/anrs' into td/anr
tejasd Jun 23, 2021
6af964a
Add comment about device orientation
tejasd Jun 23, 2021
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
5 changes: 4 additions & 1 deletion firebase-crashlytics/firebase-crashlytics.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,10 @@ plugins {
}

firebaseLibrary {
testLab.enabled = true
testLab {
enabled = true
device 'model=flame,version=30'
}
}

android {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@
import org.mockito.ArgumentCaptor;

public class CrashlyticsControllerTest extends CrashlyticsTestCase {

private static final String GOOGLE_APP_ID = "google:app:id";

private Context testContext;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;

import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.ContextWrapper;
Expand Down Expand Up @@ -101,6 +103,30 @@ public void testCaptureReport_containsNoDeveloperPlatformFieldsWhenUnityIsMissin
assertNull(report.getSession().getApp().getDevelopmentPlatformVersion());
}

@Test
public void testCaptureAnrEvent_foregroundAnr() {
CrashlyticsReport.ApplicationExitInfo testApplicationExitInfo = makeAppExitInfo(false);
final CrashlyticsReport.Session.Event event =
dataCapture.captureAnrEventData(testApplicationExitInfo);

assertEquals("anr", event.getType());
assertEquals(testApplicationExitInfo, event.getApp().getExecution().getAppExitInfo());
assertEquals(testApplicationExitInfo.getTimestamp(), event.getTimestamp());
assertEquals(false, event.getApp().getBackground());
}

@Test
public void testCaptureAnrEvent_backgroundAnr() {
CrashlyticsReport.ApplicationExitInfo testApplicationExitInfo = makeAppExitInfo(true);
final CrashlyticsReport.Session.Event event =
dataCapture.captureAnrEventData(testApplicationExitInfo);

assertEquals("anr", event.getType());
assertEquals(testApplicationExitInfo, event.getApp().getExecution().getAppExitInfo());
assertEquals(testApplicationExitInfo.getTimestamp(), event.getTimestamp());
assertEquals(true, event.getApp().getBackground());
}

@Test
public void testCaptureReportSessionFields() {
final String sessionId = "sessionId";
Expand Down Expand Up @@ -369,4 +395,21 @@ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter)
}
};
}

private static CrashlyticsReport.ApplicationExitInfo makeAppExitInfo(boolean isBackground) {
final int anrImportance =
isBackground
? RunningAppProcessInfo.IMPORTANCE_CACHED
: ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
return CrashlyticsReport.ApplicationExitInfo.builder()
.setTraceFile("trace")
.setTimestamp(1L)
.setImportance(anrImportance)
.setReasonCode(1)
.setProcessName("test")
.setPid(1)
.setPss(1L)
.setRss(1L)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,12 @@ public void testReportToJsonAndBack_with_developmentPlatform_equals() throws IOE
}

@Test
public void testReportToJsonAndBack_with_appExitInfo_equals() throws IOException {
final CrashlyticsReport testReport = makeTestReport(false).withAppExitInfo(makeAppExitInfo());
final String testReportJson = transform.reportToJson(testReport);
final CrashlyticsReport reifiedReport = transform.reportFromJson(testReportJson);
assertNotSame(reifiedReport, testReport);
assertEquals(reifiedReport, testReport);
public void testAnrEventToJsonAndBack_equals() throws IOException {
final CrashlyticsReport.Session.Event testEvent = makeAnrEvent();
final String testEventJson = transform.eventToJson(testEvent);
final CrashlyticsReport.Session.Event reifiedEvent = transform.eventFromJson(testEventJson);
assertNotSame(reifiedEvent, testEvent);
assertEquals(reifiedEvent, testEvent);
}

@Test
Expand Down Expand Up @@ -180,6 +180,40 @@ private static Event makeTestEvent() {
.build();
}

private static Event makeAnrEvent() {
return Event.builder()
.setType("anr")
.setTimestamp(1000)
.setApp(
Session.Event.Application.builder()
.setBackground(false)
.setExecution(
Execution.builder()
.setBinaries(
ImmutableList.from(
Execution.BinaryImage.builder()
.setBaseAddress(0)
.setName("name")
.setSize(100000)
.setUuid("uuid")
.build()))
.setSignal(Signal.builder().setCode("0").setName("0").setAddress(0).build())
.setAppExitInfo(makeAppExitInfo())
.build())
.setUiOrientation(1)
.build())
.setDevice(
Session.Event.Device.builder()
.setBatteryLevel(0.5)
.setBatteryVelocity(3)
.setDiskUsed(10000000)
.setOrientation(1)
.setProximityOn(true)
.setRamUsed(10000000)
.build())
.build();
}

private static ImmutableList<Frame> makeTestFrames() {
return ImmutableList.from(
Frame.builder()
Expand Down Expand Up @@ -219,6 +253,9 @@ private static CrashlyticsReport.ApplicationExitInfo makeAppExitInfo() {
.setImportance(1)
.setReasonCode(1)
.setProcessName("test")
.setPid(1)
.setPss(1L)
.setRss(1L)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -669,17 +669,15 @@ public void testPersistEvent_whenSettingsChanges_keepsAppropriateNumberOfMostRec
}

@Test
public void testPersistAppExitInfo() throws IOException {
public void testPersistReportWithAnrEvent() throws IOException {
reportPersistence =
new CrashlyticsReportPersistence(
folder.newFolder(), getSettingsMock(VERY_LARGE_UPPER_LIMIT, 4));
final String sessionId = "testSession";
final CrashlyticsReport testReport = makeTestReport(sessionId);
final CrashlyticsReport.Session.Event testEvent1 = makeTestEvent("anr", "reason1");
final CrashlyticsReport.ApplicationExitInfo testAppExitInfo = makeAppExitInfo();

final Event testEvent = makeTestAnrEvent();
reportPersistence.persistReport(testReport);
reportPersistence.persistAppExitInfoEvent(testEvent1, sessionId, testAppExitInfo);
reportPersistence.persistEvent(testEvent, sessionId, true);

final long endedAt = System.currentTimeMillis();

Expand All @@ -690,12 +688,6 @@ public void testPersistAppExitInfo() throws IOException {
assertEquals(1, finalizedReports.size());
final CrashlyticsReport finalizedReport = finalizedReports.get(0).getReport();
assertEquals(1, finalizedReport.getSession().getEvents().size());
assertEquals(
testReport
.withSessionEndFields(endedAt, true, null)
.withEvents(ImmutableList.from(testEvent1))
.withAppExitInfo(testAppExitInfo),
finalizedReport);
}

private static void persistReportWithEvent(
Expand Down Expand Up @@ -818,6 +810,40 @@ private static Event makeTestEvent(String type, String reason) {
.build();
}

private static Event makeTestAnrEvent() {
return Event.builder()
.setType("anr")
.setTimestamp(1000)
.setApp(
Session.Event.Application.builder()
.setBackground(false)
.setExecution(
Execution.builder()
.setBinaries(
ImmutableList.from(
Execution.BinaryImage.builder()
.setBaseAddress(0)
.setName("name")
.setSize(100000)
.setUuid("uuid")
.build()))
.setSignal(Signal.builder().setCode("0").setName("0").setAddress(0).build())
.setAppExitInfo(makeAppExitInfo())
.build())
.setUiOrientation(1)
.build())
.setDevice(
Session.Event.Device.builder()
.setBatteryLevel(0.5)
.setBatteryVelocity(3)
.setDiskUsed(10000000)
.setOrientation(1)
.setProximityOn(true)
.setRamUsed(10000000)
.build())
.build();
}

private static ImmutableList<Frame> makeTestFrames() {
return ImmutableList.from(
Frame.builder()
Expand Down Expand Up @@ -857,6 +883,9 @@ private static CrashlyticsReport.ApplicationExitInfo makeAppExitInfo() {
.setImportance(1)
.setReasonCode(1)
.setProcessName("test")
.setPid(1)
.setPss(1L)
.setRss(1L)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,20 @@ public Event captureEventData(
.build();
}

public Event captureAnrEventData(CrashlyticsReport.ApplicationExitInfo applicationExitInfo) {
// This is not the orientation of the device at the time of ANR.
// It's filtered out when the backend processes it.
// TODO: Consider setting it to 0 to mark it as unknown.
final int orientation = context.getResources().getConfiguration().orientation;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this is not the orientation of device at the time of ANR. may be do you want to add a comment here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


return Event.builder()
.setType("anr")
.setTimestamp(applicationExitInfo.getTimestamp())
.setApp(populateEventApplicationData(orientation, applicationExitInfo))
.setDevice(populateEventDeviceData(orientation))
.build();
}

private CrashlyticsReport.Builder buildReportData() {
return CrashlyticsReport.builder()
.setSdkVersion(BuildConfig.VERSION_NAME)
Expand Down Expand Up @@ -212,6 +226,18 @@ private Event.Application populateEventApplicationData(
.build();
}

private Event.Application populateEventApplicationData(
int orientation, CrashlyticsReport.ApplicationExitInfo applicationExitInfo) {
boolean isBackground =
applicationExitInfo.getImportance() != RunningAppProcessInfo.IMPORTANCE_FOREGROUND;

return Event.Application.builder()
.setBackground(isBackground)
.setUiOrientation(orientation)
.setExecution(populateExecutionData(applicationExitInfo))
.build();
}

private Event.Device populateEventDeviceData(int orientation) {
final BatteryState battery = BatteryState.get(context);
final Float batteryLevel = battery.getBatteryLevel();
Expand Down Expand Up @@ -250,6 +276,15 @@ private Execution populateExecutionData(
.build();
}

private Execution populateExecutionData(
CrashlyticsReport.ApplicationExitInfo applicationExitInfo) {
return Execution.builder()
.setAppExitInfo(applicationExitInfo)
.setSignal(populateSignalData())
.setBinaries(populateBinaryImagesList())
.build();
}

private ImmutableList<Execution.Thread> populateThreadsList(
TrimmedThrowableData trimmedEvent,
Thread eventThread,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@
public class SessionReportingCoordinator implements CrashlyticsLifecycleEvents {

private static final String EVENT_TYPE_CRASH = "crash";
private static final String EVENT_TYPE_ANR = "anr";
private static final String EVENT_TYPE_LOGGED = "error";
private static final int EVENT_THREAD_IMPORTANCE = 4;
private static final int MAX_CHAINED_EXCEPTION_DEPTH = 8;
Expand Down Expand Up @@ -147,21 +146,11 @@ public void persistAppExitInfoEvent(String sessionId, ApplicationExitInfo applic
return;
}

// TODO: Refactor Event to only contain relevant information rather than unnecessary data like
// thread, exception etc.
final CrashlyticsReport.Session.Event capturedEvent =
dataCapture.captureEventData(
new Exception("ANR"),
Thread.currentThread(),
EVENT_TYPE_ANR,
applicationExitInfo.getTimestamp(),
EVENT_THREAD_IMPORTANCE,
MAX_CHAINED_EXCEPTION_DEPTH,
false);
CrashlyticsReport.ApplicationExitInfo crashlyticsAppExitInfo =
convertApplicationExitInfo(applicationExitInfo);
dataCapture.captureAnrEventData(convertApplicationExitInfo(applicationExitInfo));

Logger.getLogger().d("Persisting anr for session " + sessionId);
reportPersistence.persistAppExitInfoEvent(capturedEvent, sessionId, crashlyticsAppExitInfo);
reportPersistence.persistEvent(addLogsAndCustomKeysToEvent(capturedEvent), sessionId, true);
}

public void finalizeSessionWithNativeEvent(
Expand Down Expand Up @@ -227,28 +216,9 @@ public Task<Void> sendReports(@NonNull Executor reportSendCompleteExecutor) {
return Tasks.whenAll(sendTasks);
}

private void persistEvent(
@NonNull Throwable event,
@NonNull Thread thread,
@NonNull String sessionId,
@NonNull String eventType,
long timestamp,
boolean includeAllThreads) {

final boolean isHighPriority = eventType.equals(EVENT_TYPE_CRASH);

final CrashlyticsReport.Session.Event capturedEvent =
dataCapture.captureEventData(
event,
thread,
eventType,
timestamp,
EVENT_THREAD_IMPORTANCE,
MAX_CHAINED_EXCEPTION_DEPTH,
includeAllThreads);

private CrashlyticsReport.Session.Event addLogsAndCustomKeysToEvent(
CrashlyticsReport.Session.Event capturedEvent) {
final CrashlyticsReport.Session.Event.Builder eventBuilder = capturedEvent.toBuilder();

final String content = logFileManager.getLogString();

if (content != null) {
Expand All @@ -274,7 +244,31 @@ private void persistEvent(
.build());
}

reportPersistence.persistEvent(eventBuilder.build(), sessionId, isHighPriority);
return eventBuilder.build();
}

private void persistEvent(
@NonNull Throwable event,
@NonNull Thread thread,
@NonNull String sessionId,
@NonNull String eventType,
long timestamp,
boolean includeAllThreads) {

final boolean isHighPriority = eventType.equals(EVENT_TYPE_CRASH);

final CrashlyticsReport.Session.Event capturedEvent =
dataCapture.captureEventData(
event,
thread,
eventType,
timestamp,
EVENT_THREAD_IMPORTANCE,
MAX_CHAINED_EXCEPTION_DEPTH,
includeAllThreads);

reportPersistence.persistEvent(
addLogsAndCustomKeysToEvent(capturedEvent), sessionId, isHighPriority);
}

private boolean onReportSendComplete(@NonNull Task<CrashlyticsReportWithSessionId> task) {
Expand Down Expand Up @@ -328,6 +322,9 @@ private static CrashlyticsReport.ApplicationExitInfo convertApplicationExitInfo(
.setProcessName(applicationExitInfo.getProcessName())
.setReasonCode(applicationExitInfo.getReason())
.setTimestamp(applicationExitInfo.getTimestamp())
.setPid(applicationExitInfo.getPid())
.setPss(applicationExitInfo.getPss())
.setRss(applicationExitInfo.getRss())
.setTraceFile(traceFile)
.build();
}
Expand Down
Loading