Update logic to parse crash for android

Summary:
Updated the logic of parsing the crash log. Earlier the function written to parse crash log just expected the iOS crashes. Now it also gets android crash logs.

I have also written the tests for the same and updated the tests to accommodate for the API change

Reviewed By: passy

Differential Revision: D13612576

fbshipit-source-id: cf7e9d2bca4a425ad7338fc5ec15e29cf88e2e77
This commit is contained in:
Pritesh Nandgaonkar
2019-01-10 06:34:23 -08:00
committed by Facebook Github Bot
parent cc0615c7d9
commit 972f8b91d6
2 changed files with 110 additions and 22 deletions

View File

@@ -56,14 +56,14 @@ afterAll(() => {
test('test the parsing of the reason for crash when log matches the predefined regex', () => {
const log =
'Blaa Blaaa \n Blaa Blaaa \n Exception Type: SIGSEGV \n Blaa Blaa \n Blaa Blaa';
const crash = parseCrashLog(log);
const crash = parseCrashLog(log, 'iOS');
expect(crash.callstack).toEqual(log);
expect(crash.reason).toEqual('SIGSEGV');
expect(crash.name).toEqual('SIGSEGV');
});
test('test the parsing of the crash log when log does not match the predefined regex but is alphanumeric', () => {
const log = 'Blaa Blaaa \n Blaa Blaaa \n Blaa Blaaa';
const crash = parseCrashLog(log);
const crash = parseCrashLog(log, 'iOS');
expect(crash.callstack).toEqual(log);
expect(crash.reason).toEqual('Cannot figure out the cause');
expect(crash.name).toEqual('Cannot figure out the cause');
@@ -72,14 +72,46 @@ test('test the parsing of the crash log when log does not match the predefined r
test('test the parsing of the reason for crash when log does not match the predefined regex contains unicode character', () => {
const log =
'Blaa Blaaa \n Blaa Blaaa \n Exception Type: 🍕🐬 \n Blaa Blaa \n Blaa Blaa';
const crash = parseCrashLog(log);
const crash = parseCrashLog(log, 'iOS');
expect(crash.callstack).toEqual(log);
expect(crash.reason).toEqual('Cannot figure out the cause');
expect(crash.name).toEqual('Cannot figure out the cause');
});
test('test the parsing of the reason for crash when log is empty', () => {
const log = '';
const crash = parseCrashLog(log);
const crash = parseCrashLog(log, 'iOS');
expect(crash.callstack).toEqual(log);
expect(crash.reason).toEqual('Cannot figure out the cause');
expect(crash.name).toEqual('Cannot figure out the cause');
});
test('test the parsing of the Android crash log for the proper android crash format', () => {
const log =
'FATAL EXCEPTION: main\nProcess: com.facebook.flipper.sample, PID: 27026\njava.lang.IndexOutOfBoundsException: Index: 190, Size: 0\n\tat java.util.ArrayList.get(ArrayList.java:437)\n\tat com.facebook.flipper.sample.RootComponentSpec.hitGetRequest(RootComponentSpec.java:72)\n\tat com.facebook.flipper.sample.RootComponent.hitGetRequest(RootComponent.java:46)\n';
const crash = parseCrashLog(log, 'Android');
expect(crash.callstack).toEqual(log);
expect(crash.reason).toEqual(
'java.lang.IndexOutOfBoundsException: Index: 190, Size: 0',
);
expect(crash.name).toEqual('FATAL EXCEPTION: main');
});
test('test the parsing of the Android crash log for the unknown crash format', () => {
const log = 'Blaa Blaa Blaa';
const crash = parseCrashLog(log, 'Android');
expect(crash.callstack).toEqual(log);
expect(crash.reason).toEqual('Cannot figure out the cause');
expect(crash.name).toEqual('Cannot figure out the cause');
});
test('test the parsing of the Android crash log for the partial format matching the crash format', () => {
const log = 'First Line Break \n Blaa Blaa \n Blaa Blaa ';
const crash = parseCrashLog(log, 'Android');
expect(crash.callstack).toEqual(log);
expect(crash.reason).toEqual('Cannot figure out the cause');
expect(crash.name).toEqual('First Line Break ');
});
test('test the parsing of the Android crash log with os being iOS', () => {
const log =
'FATAL EXCEPTION: main\nProcess: com.facebook.flipper.sample, PID: 27026\njava.lang.IndexOutOfBoundsException: Index: 190, Size: 0\n\tat java.util.ArrayList.get(ArrayList.java:437)\n\tat com.facebook.flipper.sample.RootComponentSpec.hitGetRequest(RootComponentSpec.java:72)\n\tat com.facebook.flipper.sample.RootComponent.hitGetRequest(RootComponent.java:46)\n';
const crash = parseCrashLog(log, 'iOS');
expect(crash.callstack).toEqual(log);
expect(crash.reason).toEqual('Cannot figure out the cause');
expect(crash.name).toEqual('Cannot figure out the cause');
@@ -152,6 +184,7 @@ test('test getNewPersisitedStateFromCrashLog for non-empty defaultPersistedState
perisistedState,
CrashReporterPlugin,
content,
'iOS',
);
expect(newPersistedState).toEqual({
crashes: [pluginStateCrash, getCrash(1, content, 'SIGSEGV', 'SIGSEGV')],
@@ -174,6 +207,7 @@ test('test getNewPersisitedStateFromCrashLog for non-empty defaultPersistedState
perisistedState,
CrashReporterPlugin,
content,
'iOS',
);
expect(newPersistedState).toEqual({
crashes: [crash, getCrash(1, content, 'SIGSEGV', 'SIGSEGV')],
@@ -197,6 +231,7 @@ test('test getNewPersisitedStateFromCrashLog for non-empty defaultPersistedState
perisistedState,
CrashReporterPlugin,
content,
'iOS',
);
expect(newPersistedState).toEqual({
crashes: [
@@ -210,6 +245,26 @@ test('test getNewPersisitedStateFromCrashLog for non-empty defaultPersistedState
],
});
});
test('test getNewPersisitedStateFromCrashLog when os is undefined', () => {
setNotificationID(0);
const crash = getCrash(0, 'callstack', 'crash0', 'crash0');
const pluginKey = getPluginKey(null, null, CrashReporterPlugin.id);
setDefaultPersistedState({crashes: [crash]});
const pluginStateCrash = getCrash(1, 'callstack', 'crash1', 'crash1');
const pluginStates = {'unknown#CrashReporter': {crashes: [pluginStateCrash]}};
const perisistedState = getPersistedState(
pluginKey,
CrashReporterPlugin,
pluginStates,
);
const content = 'Blaa Blaaa \n Blaa Blaaa';
const newPersistedState = getNewPersisitedStateFromCrashLog(
perisistedState,
CrashReporterPlugin,
content,
);
expect(newPersistedState).toEqual(null);
});
test('test parsing of path when inputs are correct', () => {
const content =
'Blaa Blaaa \n Blaa Blaaa \n Path: path/to/simulator/TH1S-15DEV1CE-1D/AppName.app/AppName \n Blaa Blaa \n Blaa Blaa';

View File

@@ -82,12 +82,14 @@ export function getNewPersisitedStateFromCrashLog(
persistedState: ?PersistedState,
persistingPlugin: Class<FlipperDevicePlugin<> | FlipperPlugin<>>,
content: string,
os: ?string,
): ?PersistedState {
const crash = parseCrashLog(content);
if (!persistingPlugin.persistedStateReducer) {
const persistedStateReducer = persistingPlugin.persistedStateReducer;
if (!os || !persistedStateReducer) {
return null;
}
const newPluginState = persistingPlugin.persistedStateReducer(
const crash = parseCrashLog(content, os);
const newPluginState = persistedStateReducer(
persistedState,
'crash-report',
crash,
@@ -103,11 +105,12 @@ export function parseCrashLogAndUpdateState(
newPluginState: ?PersistedState,
) => void,
) {
const os = store.getState().connections.selectedDevice?.os;
if (
!shouldShowCrashNotification(
store.getState().connections.selectedDevice,
content,
store.getState().connections.selectedDevice?.os,
os,
)
) {
return;
@@ -134,6 +137,7 @@ export function parseCrashLogAndUpdateState(
persistedState,
persistingPlugin,
content,
os,
);
setPersistedState(pluginKey, newPluginState);
}
@@ -155,20 +159,49 @@ export function shouldShowCrashNotification(
return true;
}
export function parseCrashLog(content: string): CrashLog {
const regex = /Exception Type: *[\w]*/;
const arr = regex.exec(content);
const exceptionString = arr ? arr[0] : '';
const exceptionRegex = /[\w]*$/;
const tmp = exceptionRegex.exec(exceptionString);
const exception =
tmp && tmp[0].length ? tmp[0] : 'Cannot figure out the cause';
const crash = {
callstack: content,
name: exception,
reason: exception,
};
return crash;
export function parseCrashLog(content: string, os: string): CrashLog {
const stubString = 'Cannot figure out the cause';
if (os.includes('iOS')) {
const regex = /Exception Type: *[\w]*/;
const arr = regex.exec(content);
const exceptionString = arr ? arr[0] : '';
const exceptionRegex = /[\w]*$/;
const tmp = exceptionRegex.exec(exceptionString);
const exception =
tmp && tmp[0].length ? tmp[0] : 'Cannot figure out the cause';
const crash = {
callstack: content,
name: exception,
reason: exception,
};
return crash;
} else if (os.includes('Android')) {
const regForName = /.*\n/;
const nameRegArr = regForName.exec(content);
let name = nameRegArr ? nameRegArr[0] : stubString;
const regForCallStack = /\tat[\w\s\n.$&+,:;=?@#|'<>.^*()%!-]*$/;
const callStackArray = regForCallStack.exec(content);
const callStack = callStackArray ? callStackArray[0] : '';
let remainingString =
callStack.length > 0 ? content.replace(callStack, '') : '';
if (remainingString[remainingString.length - 1] === '\n') {
remainingString = remainingString.slice(0, -1);
}
const reason =
remainingString.length > 0
? remainingString.split('\n').pop()
: stubString;
if (name[name.length - 1] === '\n') {
name = name.slice(0, -1);
}
const crash = {
callstack: content,
name: name,
reason: reason,
};
return crash;
}
throw new Error('Unsupported OS');
}
export function parsePath(content: string): ?string {