/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @format */ import type {DeviceLogEntry, DevicePluginClient} from 'flipper-plugin'; import {UNKNOWN_CRASH_REASON} from './crash-utils'; import type {Crash, CrashLog} from './index'; export function parseAndroidCrash(content: string, logDate?: Date) { const regForName = /.*\n/; const nameRegArr = regForName.exec(content); let name = nameRegArr ? nameRegArr[0] : UNKNOWN_CRASH_REASON; 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 reasonText = remainingString.length > 0 ? remainingString.split('\n').pop() : UNKNOWN_CRASH_REASON; const reason = reasonText ? reasonText : UNKNOWN_CRASH_REASON; if (name[name.length - 1] === '\n') { name = name.slice(0, -1); } const crash: CrashLog = { callstack: content, name: name, reason: reason, date: logDate, }; return crash; } export function shouldParseAndroidLog( entry: DeviceLogEntry, date: Date, ): boolean { return ( entry.date.getTime() - date.getTime() > 0 && // The log should have arrived after the device has been registered ((entry.type === 'error' && entry.tag === 'AndroidRuntime') || entry.type === 'fatal') ); } export function startAndroidCrashWatcher( client: DevicePluginClient, reportCrash: (payload: CrashLog | Crash) => void, ) { const referenceDate = new Date(); let androidLog: string = ''; let androidLogUnderProcess = false; let timer: null | NodeJS.Timeout = null; client.device.onLogEntry((entry: DeviceLogEntry) => { if (shouldParseAndroidLog(entry, referenceDate)) { if (androidLogUnderProcess) { androidLog += '\n' + entry.message; androidLog = androidLog.trim(); if (timer) { clearTimeout(timer); } } else { androidLog = entry.message; androidLogUnderProcess = true; } timer = setTimeout(() => { if (androidLog.length > 0) { reportCrash(parseAndroidCrash(androidLog, entry.date)); } androidLogUnderProcess = false; androidLog = ''; }, 50); } }); }