use of com.microsoft.appcenter.crashes.ingestion.models.ManagedErrorLog in project mobile-center-sdk-android by Microsoft.
the class CrashesAndroidTest method testNoDuplicateCallbacksOrSending.
@Test
public void testNoDuplicateCallbacksOrSending() throws Exception {
/* Crash on 1st process. */
assertFalse(Crashes.hasCrashedInLastSession().get());
android.util.Log.i(TAG, "Process 1");
Thread.UncaughtExceptionHandler uncaughtExceptionHandler = mock(Thread.UncaughtExceptionHandler.class);
Thread.setDefaultUncaughtExceptionHandler(uncaughtExceptionHandler);
CrashesListener crashesListener = mock(CrashesListener.class);
/* While testing should process, call methods that require the handler to test we avoid a dead lock and run directly. */
when(crashesListener.shouldProcess(any(ErrorReport.class))).thenAnswer(new Answer<Boolean>() {
@Override
public Boolean answer(InvocationOnMock invocationOnMock) throws Throwable {
assertNotNull(AppCenter.getInstallId().get());
return AppCenter.isEnabled().get() && Crashes.isEnabled().get();
}
});
when(crashesListener.shouldAwaitUserConfirmation()).thenReturn(true);
startFresh(crashesListener);
final Error exception = generateStackOverflowError();
assertTrue(exception.getStackTrace().length > ErrorLogHelper.FRAME_LIMIT);
final Thread thread = new Thread() {
@Override
public void run() {
throw exception;
}
};
thread.start();
thread.join();
assertEquals(ErrorLogHelper.FRAME_LIMIT, exception.getStackTrace().length);
verify(uncaughtExceptionHandler).uncaughtException(thread, exception);
assertEquals(2, ErrorLogHelper.getErrorStorageDirectory().listFiles(mMinidumpFilter).length);
verifyZeroInteractions(crashesListener);
/* Second process: enqueue log but network is down... */
android.util.Log.i(TAG, "Process 2");
startFresh(crashesListener);
/* Check last session error report. */
Crashes.getLastSessionCrashReport().thenAccept(new AppCenterConsumer<ErrorReport>() {
@Override
public void accept(ErrorReport errorReport) {
assertNotNull(errorReport);
Throwable lastThrowable = errorReport.getThrowable();
assertTrue(lastThrowable instanceof StackOverflowError);
assertEquals(ErrorLogHelper.FRAME_LIMIT, lastThrowable.getStackTrace().length);
}
});
assertTrue(Crashes.hasCrashedInLastSession().get());
/* Wait U.I. thread callback (shouldAwaitUserConfirmation). */
final Semaphore semaphore = new Semaphore(0);
HandlerUtils.runOnUiThread(new Runnable() {
@Override
public void run() {
semaphore.release();
}
});
semaphore.acquire();
/* Waiting user confirmation so no log sent yet. */
ArgumentMatcher<Log> matchCrashLog = new ArgumentMatcher<Log>() {
@Override
public boolean matches(Object o) {
return o instanceof ManagedErrorLog;
}
};
verify(mChannel, never()).enqueue(argThat(matchCrashLog), anyString());
assertEquals(2, ErrorLogHelper.getErrorStorageDirectory().listFiles(mMinidumpFilter).length);
verify(crashesListener).shouldProcess(any(ErrorReport.class));
verify(crashesListener).shouldAwaitUserConfirmation();
verifyNoMoreInteractions(crashesListener);
/* Confirm to resume processing. */
final AtomicReference<Log> log = new AtomicReference<>();
doAnswer(new Answer() {
@Override
public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
log.set((Log) invocationOnMock.getArguments()[0]);
return null;
}
}).when(mChannel).enqueue(argThat(matchCrashLog), anyString());
Crashes.notifyUserConfirmation(Crashes.ALWAYS_SEND);
assertTrue(Crashes.isEnabled().get());
verify(mChannel).enqueue(argThat(matchCrashLog), anyString());
assertNotNull(log.get());
assertEquals(1, ErrorLogHelper.getErrorStorageDirectory().listFiles(mMinidumpFilter).length);
verify(crashesListener).getErrorAttachments(any(ErrorReport.class));
verifyNoMoreInteractions(crashesListener);
/* Third process: sending succeeds. */
android.util.Log.i(TAG, "Process 3");
mChannel = mock(Channel.class);
ArgumentCaptor<Channel.GroupListener> groupListener = ArgumentCaptor.forClass(Channel.GroupListener.class);
startFresh(crashesListener);
verify(mChannel).addGroup(anyString(), anyInt(), anyInt(), anyInt(), groupListener.capture());
groupListener.getValue().onBeforeSending(log.get());
groupListener.getValue().onSuccess(log.get());
/* Wait callback to be processed in background thread (file manipulations) then called back in UI. */
/*
* Wait background thread to process the 2 previous commands,
* to do we check if crashed in last session, since we restarted process again after crash,
* it's false even if we couldn't send the log yet.
*/
assertFalse(Crashes.hasCrashedInLastSession().get());
assertNull(Crashes.getLastSessionCrashReport().get());
/* Wait U.I. thread callbacks. */
HandlerUtils.runOnUiThread(new Runnable() {
@Override
public void run() {
semaphore.release();
}
});
semaphore.acquire();
assertEquals(0, ErrorLogHelper.getErrorStorageDirectory().listFiles(mMinidumpFilter).length);
verify(mChannel, never()).enqueue(argThat(matchCrashLog), anyString());
verify(crashesListener).onBeforeSending(any(ErrorReport.class));
verify(crashesListener).onSendingSucceeded(any(ErrorReport.class));
verifyNoMoreInteractions(crashesListener);
/* Verify log was truncated to 256 frames. */
assertTrue(log.get() instanceof ManagedErrorLog);
ManagedErrorLog errorLog = (ManagedErrorLog) log.get();
assertNotNull(errorLog.getException());
assertNotNull(errorLog.getException().getFrames());
assertEquals(ErrorLogHelper.FRAME_LIMIT, errorLog.getException().getFrames().size());
}
use of com.microsoft.appcenter.crashes.ingestion.models.ManagedErrorLog in project mobile-center-sdk-android by Microsoft.
the class Crashes method processPendingErrors.
private void processPendingErrors() {
for (File logFile : ErrorLogHelper.getStoredErrorLogFiles()) {
AppCenterLog.debug(LOG_TAG, "Process pending error file: " + logFile);
String logfileContents = StorageHelper.InternalStorage.read(logFile);
if (logfileContents != null) {
try {
ManagedErrorLog log = (ManagedErrorLog) mLogSerializer.deserializeLog(logfileContents);
UUID id = log.getId();
ErrorReport report = buildErrorReport(log);
if (report == null) {
removeAllStoredErrorLogFiles(id);
} else if (!mAutomaticProcessing || mCrashesListener.shouldProcess(report)) {
if (!mAutomaticProcessing) {
AppCenterLog.debug(LOG_TAG, "CrashesListener.shouldProcess returned true, continue processing log: " + id.toString());
}
mUnprocessedErrorReports.put(id, mErrorReportCache.get(id));
} else {
AppCenterLog.debug(LOG_TAG, "CrashesListener.shouldProcess returned false, clean up and ignore log: " + id.toString());
removeAllStoredErrorLogFiles(id);
}
} catch (JSONException e) {
AppCenterLog.error(LOG_TAG, "Error parsing error log", e);
}
}
}
/* If automatic processing is enabled. */
if (mAutomaticProcessing) {
/* Proceed to check if user confirmation is needed. */
sendCrashReportsOrAwaitUserConfirmation();
}
}
use of com.microsoft.appcenter.crashes.ingestion.models.ManagedErrorLog in project mobile-center-sdk-android by Microsoft.
the class Crashes method initialize.
private void initialize() {
boolean enabled = isInstanceEnabled();
mInitializeTimestamp = enabled ? System.currentTimeMillis() : -1;
if (!enabled) {
if (mUncaughtExceptionHandler != null) {
mUncaughtExceptionHandler.unregister();
mUncaughtExceptionHandler = null;
}
} else {
/* Register Java crash handler. */
mUncaughtExceptionHandler = new UncaughtExceptionHandler();
mUncaughtExceptionHandler.register();
/* Convert minidump files to App Center crash files. */
for (File logFile : ErrorLogHelper.getNewMinidumpFiles()) {
/* Create missing files from the native crash that we detected. */
AppCenterLog.debug(LOG_TAG, "Process pending minidump file: " + logFile);
long minidumpDate = logFile.lastModified();
File dest = new File(ErrorLogHelper.getPendingMinidumpDirectory(), logFile.getName());
NativeException nativeException = new NativeException();
Exception modelException = new Exception();
modelException.setType("minidump");
modelException.setWrapperSdkName(Constants.WRAPPER_SDK_NAME_NDK);
modelException.setStackTrace(dest.getPath());
ManagedErrorLog errorLog = new ManagedErrorLog();
errorLog.setException(modelException);
errorLog.setTimestamp(new Date(minidumpDate));
errorLog.setFatal(true);
errorLog.setId(UUID.randomUUID());
/* Lookup app launch timestamp in session history. */
SessionContext.SessionInfo session = SessionContext.getInstance().getSessionAt(minidumpDate);
if (session != null && session.getAppLaunchTimestamp() <= minidumpDate) {
errorLog.setAppLaunchTimestamp(new Date(session.getAppLaunchTimestamp()));
} else {
/*
* Fall back to log date if app launch timestamp information lost
* or in the future compared to crash time.
* This also covers the case where app launches then crashes within 1s:
* app launch timestamp would have ms accuracy while minidump file is without
* ms, in that case we also falls back to log timestamp
* (this would be same result as truncating ms).
*/
errorLog.setAppLaunchTimestamp(errorLog.getTimestamp());
}
/*
* TODO The following properties are placeholders because fields are required.
* They should be removed from schema as not used by server.
*/
errorLog.setProcessId(0);
errorLog.setProcessName("");
/*
* TODO device properties are read after restart contrary to Java crashes.
* We should have a device property history like we did for session to fix that issue.
* The main issue with the current code is that app version can change between crash and reporting.
*/
try {
errorLog.setDevice(DeviceInfoHelper.getDeviceInfo(mContext));
errorLog.getDevice().setWrapperSdkName(Constants.WRAPPER_SDK_NAME_NDK);
saveErrorLogFiles(nativeException, errorLog);
if (!logFile.renameTo(dest)) {
throw new IOException("Failed to move file");
}
} catch (java.lang.Exception e) {
// noinspection ResultOfMethodCallIgnored
logFile.delete();
removeAllStoredErrorLogFiles(errorLog.getId());
AppCenterLog.error(LOG_TAG, "Failed to process new minidump file: " + logFile, e);
}
}
/* Check last session crash. */
File logFile = ErrorLogHelper.getLastErrorLogFile();
if (logFile != null) {
AppCenterLog.debug(LOG_TAG, "Processing crash report for the last session.");
String logFileContents = StorageHelper.InternalStorage.read(logFile);
if (logFileContents == null) {
AppCenterLog.error(LOG_TAG, "Error reading last session error log.");
} else {
try {
ManagedErrorLog log = (ManagedErrorLog) mLogSerializer.deserializeLog(logFileContents);
mLastSessionErrorReport = buildErrorReport(log);
AppCenterLog.debug(LOG_TAG, "Processed crash report for the last session.");
} catch (JSONException e) {
AppCenterLog.error(LOG_TAG, "Error parsing last session error log.", e);
}
}
}
}
}
use of com.microsoft.appcenter.crashes.ingestion.models.ManagedErrorLog in project mobile-center-sdk-android by Microsoft.
the class CrashesTest method testNativeCrashLog.
private ManagedErrorLog testNativeCrashLog(long appStartTime, long crashTime, boolean correlateSession) throws Exception {
/* Setup mock for a crash in disk. */
File minidumpFile = mock(File.class);
when(minidumpFile.getName()).thenReturn("mockFile");
when(minidumpFile.lastModified()).thenReturn(crashTime);
mockStatic(SessionContext.class);
SessionContext sessionContext = mock(SessionContext.class);
when(SessionContext.getInstance()).thenReturn(sessionContext);
if (correlateSession) {
SessionContext.SessionInfo sessionInfo = mock(SessionContext.SessionInfo.class);
when(sessionContext.getSessionAt(crashTime)).thenReturn(sessionInfo);
when(sessionInfo.getAppLaunchTimestamp()).thenReturn(appStartTime);
}
mockStatic(DeviceInfoHelper.class);
when(DeviceInfoHelper.getDeviceInfo(any(Context.class))).thenReturn(mock(Device.class));
ErrorReport report = new ErrorReport();
mockStatic(ErrorLogHelper.class);
when(ErrorLogHelper.getLastErrorLogFile()).thenReturn(mock(File.class));
when(ErrorLogHelper.getStoredErrorLogFiles()).thenReturn(new File[] { mock(File.class) });
when(ErrorLogHelper.getNewMinidumpFiles()).thenReturn(new File[] { minidumpFile });
File pendingDir = mock(File.class);
Whitebox.setInternalState(pendingDir, "path", "");
when(ErrorLogHelper.getPendingMinidumpDirectory()).thenReturn(pendingDir);
when(ErrorLogHelper.getStoredThrowableFile(any(UUID.class))).thenReturn(mock(File.class));
when(ErrorLogHelper.getErrorReportFromErrorLog(any(ManagedErrorLog.class), any(Throwable.class))).thenReturn(report);
when(StorageHelper.InternalStorage.read(any(File.class))).thenReturn("");
when(StorageHelper.InternalStorage.readObject(any(File.class))).thenReturn(new NativeException());
LogSerializer logSerializer = mock(LogSerializer.class);
ArgumentCaptor<Log> log = ArgumentCaptor.forClass(Log.class);
when(logSerializer.serializeLog(log.capture())).thenReturn("{}");
when(logSerializer.deserializeLog(anyString())).thenAnswer(new Answer<ManagedErrorLog>() {
@Override
public ManagedErrorLog answer(InvocationOnMock invocation) throws Throwable {
ManagedErrorLog log = mock(ManagedErrorLog.class);
when(log.getId()).thenReturn(UUID.randomUUID());
return log;
}
});
/* Start crashes. */
Crashes crashes = Crashes.getInstance();
crashes.setLogSerializer(logSerializer);
crashes.onStarting(mAppCenterHandler);
crashes.onStarted(mock(Context.class), "", mock(Channel.class));
/* Verify timestamps on the crash log. */
assertTrue(Crashes.hasCrashedInLastSession().get());
assertTrue(log.getValue() instanceof ManagedErrorLog);
return (ManagedErrorLog) log.getValue();
}
use of com.microsoft.appcenter.crashes.ingestion.models.ManagedErrorLog in project mobile-center-sdk-android by Microsoft.
the class CrashesTest method minidumpAppLaunchTimestampFromSessionContext.
@Test
@PrepareForTest({ SessionContext.class, DeviceInfoHelper.class })
public void minidumpAppLaunchTimestampFromSessionContext() throws Exception {
long appStartTime = 99L;
long crashTime = 123L;
ManagedErrorLog crashLog = testNativeCrashLog(appStartTime, crashTime, true);
assertEquals(new Date(crashTime), crashLog.getTimestamp());
assertEquals(new Date(appStartTime), crashLog.getAppLaunchTimestamp());
}
Aggregations