Refactor CrashReporter plugin
Summary: This diff refactors CrashReporter Plugin. The business logic of crash reporter plugin was placed in the `iOSDevice.js` as it gets the hook to the initialisation of iOSDevice. This diff moves the business logic to the Crash Reporter plugin files under the plugins folder. To get the hook, so that we can add our watcher, I have added a static function over `FlipperBasePlugin`, which if exists, will be called once `REGISTER_PLUGIN` event is dispatched Reviewed By: danielbuechele Differential Revision: D13529035 fbshipit-source-id: 28216b273b4032727859107d8a6751c6465af9ac
This commit is contained in:
committed by
Facebook Github Bot
parent
780ac863b8
commit
9d4bb45dbc
@@ -9,8 +9,6 @@ import type {ChildProcess} from 'child_process';
|
||||
import type {Store} from '../reducers/index.js';
|
||||
import type Logger from '../fb-stubs/Logger.js';
|
||||
import type {DeviceType} from '../devices/BaseDevice';
|
||||
import BaseDevice from '../devices/BaseDevice';
|
||||
import type {PersistedState} from '../plugins/crash_reporter';
|
||||
import {RecurringError} from '../utils/errors';
|
||||
import {promisify} from 'util';
|
||||
import path from 'path';
|
||||
@@ -20,12 +18,6 @@ import IOSDevice from '../devices/IOSDevice';
|
||||
import iosUtil from '../fb-stubs/iOSContainerUtility';
|
||||
import isProduction from '../utils/isProduction.js';
|
||||
import GK from '../fb-stubs/GK';
|
||||
import fs from 'fs';
|
||||
import os from 'os';
|
||||
import util from 'util';
|
||||
import {setPluginState} from '../reducers/pluginStates.js';
|
||||
import {FlipperDevicePlugin, FlipperPlugin} from '../plugin.js';
|
||||
import type {State as PluginStatesState} from '../reducers/pluginStates.js';
|
||||
|
||||
type iOSSimulatorDevice = {|
|
||||
state: 'Booted' | 'Shutdown' | 'Shutting Down',
|
||||
@@ -35,12 +27,6 @@ type iOSSimulatorDevice = {|
|
||||
udid: string,
|
||||
|};
|
||||
|
||||
type Crash = {|
|
||||
callstack: string,
|
||||
reason: string,
|
||||
name: string,
|
||||
|};
|
||||
|
||||
type IOSDeviceParams = {udid: string, type: DeviceType, name: string};
|
||||
|
||||
const portforwardingClient = isProduction()
|
||||
@@ -64,156 +50,6 @@ window.addEventListener('beforeunload', () => {
|
||||
portForwarders.forEach(process => process.kill());
|
||||
});
|
||||
|
||||
export function parseCrashLog(content: string): Crash {
|
||||
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 parsePath(content: string): ?string {
|
||||
const regex = /Path: *[\w\-\/\.\t\ \_\%]*\n/;
|
||||
const arr = regex.exec(content);
|
||||
if (!arr || arr.length <= 0) {
|
||||
return null;
|
||||
}
|
||||
const pathString = arr[0];
|
||||
const pathRegex = /[\w\-\/\.\t\ \_\%]*\n/;
|
||||
const tmp = pathRegex.exec(pathString);
|
||||
if (!tmp || tmp.length == 0) {
|
||||
return null;
|
||||
}
|
||||
const path = tmp[0];
|
||||
return path.trim();
|
||||
}
|
||||
|
||||
export function getPersistedState(
|
||||
pluginKey: string,
|
||||
persistingPlugin: ?Class<FlipperPlugin<> | FlipperDevicePlugin<>>,
|
||||
pluginStates: PluginStatesState,
|
||||
): ?PersistedState {
|
||||
if (!persistingPlugin) {
|
||||
return null;
|
||||
}
|
||||
const persistedState = {
|
||||
...persistingPlugin.defaultPersistedState,
|
||||
...pluginStates[pluginKey],
|
||||
};
|
||||
return persistedState;
|
||||
}
|
||||
|
||||
export function getPluginKey(
|
||||
selectedDevice: ?BaseDevice,
|
||||
pluginID: string,
|
||||
): string {
|
||||
return `${selectedDevice?.serial || 'unknown'}#${pluginID}`;
|
||||
}
|
||||
|
||||
export function getNewPersisitedStateFromCrashLog(
|
||||
persistedState: ?PersistedState,
|
||||
persistingPlugin: Class<FlipperDevicePlugin<> | FlipperPlugin<>>,
|
||||
content: string,
|
||||
): ?PersistedState {
|
||||
const crash = parseCrashLog(content);
|
||||
if (!persistingPlugin.persistedStateReducer) {
|
||||
return null;
|
||||
}
|
||||
const newPluginState = persistingPlugin.persistedStateReducer(
|
||||
persistedState,
|
||||
'crash-report',
|
||||
crash,
|
||||
);
|
||||
return newPluginState;
|
||||
}
|
||||
|
||||
export function shouldShowCrashNotification(
|
||||
baseDevice: ?BaseDevice,
|
||||
content: string,
|
||||
): boolean {
|
||||
const appPath = parsePath(content);
|
||||
const serial: string = baseDevice?.serial || 'unknown';
|
||||
if (!appPath || !appPath.includes(serial)) {
|
||||
// Do not show notifications for the app which are not the selected one
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function parseCrashLogAndUpdateState(store: Store, content: string) {
|
||||
if (
|
||||
!shouldShowCrashNotification(
|
||||
store.getState().connections.selectedDevice,
|
||||
content,
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const pluginID = 'CrashReporter';
|
||||
const pluginKey = getPluginKey(
|
||||
store.getState().connections.selectedDevice,
|
||||
pluginID,
|
||||
);
|
||||
const persistingPlugin: ?Class<
|
||||
FlipperDevicePlugin<> | FlipperPlugin<>,
|
||||
> = store.getState().plugins.devicePlugins.get('CrashReporter');
|
||||
if (!persistingPlugin) {
|
||||
return;
|
||||
}
|
||||
const pluginStates = store.getState().pluginStates;
|
||||
const persistedState = getPersistedState(
|
||||
pluginKey,
|
||||
persistingPlugin,
|
||||
pluginStates,
|
||||
);
|
||||
const newPluginState = getNewPersisitedStateFromCrashLog(
|
||||
persistedState,
|
||||
persistingPlugin,
|
||||
content,
|
||||
);
|
||||
if (newPluginState && persistedState !== newPluginState) {
|
||||
store.dispatch(
|
||||
setPluginState({
|
||||
pluginKey,
|
||||
state: newPluginState,
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function addFileWatcherForiOSCrashLogs(store: Store, logger: Logger) {
|
||||
const dir = path.join(os.homedir(), 'Library', 'Logs', 'DiagnosticReports');
|
||||
if (!fs.existsSync(dir)) {
|
||||
// Directory doesn't exist
|
||||
return;
|
||||
}
|
||||
fs.watch(dir, (eventType, filename) => {
|
||||
// We just parse the crash logs with extension `.crash`
|
||||
const checkFileExtension = /.crash$/.exec(filename);
|
||||
if (!filename || !checkFileExtension) {
|
||||
return;
|
||||
}
|
||||
fs.readFile(path.join(dir, filename), 'utf8', function(err, data) {
|
||||
if (store.getState().connections.selectedDevice?.os != 'iOS') {
|
||||
// If the selected device is not iOS don't show crash notifications
|
||||
return;
|
||||
}
|
||||
if (err) {
|
||||
console.error(err);
|
||||
return;
|
||||
}
|
||||
parseCrashLogAndUpdateState(store, util.format(data));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function queryDevices(store: Store, logger: Logger): Promise<void> {
|
||||
const {connections} = store.getState();
|
||||
const currentDeviceIDs: Set<string> = new Set(
|
||||
@@ -295,7 +131,6 @@ export default (store: Store, logger: Logger) => {
|
||||
if (process.platform !== 'darwin') {
|
||||
return;
|
||||
}
|
||||
addFileWatcherForiOSCrashLogs(store, logger);
|
||||
queryDevices(store, logger)
|
||||
.then(() => {
|
||||
const simulatorUpdateInterval = setInterval(() => {
|
||||
|
||||
Reference in New Issue
Block a user