diff --git a/src/interfaces.ts b/src/interfaces.ts index 46b02609..c5defca4 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -19,6 +19,7 @@ export const enum LogType { INSTALLER = 'installer', ALL = 'all', MOBILE = 'mobile', + CHROMIUM = 'chromium', UNKNOWN = '' } @@ -41,7 +42,8 @@ export const LOG_TYPES_TO_PROCESS = [ LogType.PRELOAD, LogType.CALL, LogType.INSTALLER, - LogType.MOBILE + LogType.MOBILE, + LogType.CHROMIUM ]; export interface Bookmark { @@ -143,6 +145,7 @@ export interface ProcessedLogFiles { trace: Array; installer: Array; mobile: Array; + chromium: Array; } export interface MergedLogFile extends BaseFile { @@ -163,6 +166,7 @@ export interface SortedUnzippedFiles { trace: Array; installer: Array; mobile: Array; + chromium: Array; } export interface MergedFilesLoadStatus { diff --git a/src/renderer/components/app-core.tsx b/src/renderer/components/app-core.tsx index 34aa7868..a0790020 100644 --- a/src/renderer/components/app-core.tsx +++ b/src/renderer/components/app-core.tsx @@ -55,6 +55,7 @@ export class CoreApplication extends React.Component{data}); } + public renderChromiumFile(selectedEntry: LogEntry) { + const str = `https://source.chromium.org/search?q=LOG%20filepath:${selectedEntry.meta.sourceFile}&ss=chromium`; + + return
+ Search the log in the Chromium source +
; + } + /** - * Takes a meta string (probably dirty JSON) and attempts to pretty-print it. - * - * @returns {(JSX.Element | null)} + * Takes metadata (probably dirty JSON) and attempts to pretty-print it. */ public render(): JSX.Element | null { - const { raw } = this.props; + const { meta } = this.props; - if (!raw) { + if (!meta) { return null; - } else if (raw && raw.startsWith(`+----`) && raw.endsWith('----+\n')) { - return this.renderTable(raw); - } else { - return this.renderJSON(raw); } + + // string + if (typeof meta === 'string') { + if (meta && meta.startsWith(`+----`) && meta.endsWith('----+\n')) { + return this.renderTable(meta); + } else { + return this.renderJSON(meta); + } + } + + // object + if (meta.sourceFile) { + const { selectedEntry } = this.props.state; + return this.renderChromiumFile(selectedEntry!); + } + + return null; } } diff --git a/src/renderer/components/log-line-details/details.tsx b/src/renderer/components/log-line-details/details.tsx index f7155a82..40118f58 100644 --- a/src/renderer/components/log-line-details/details.tsx +++ b/src/renderer/components/log-line-details/details.tsx @@ -125,7 +125,7 @@ export class LogLineDetails extends React.Component + ); } diff --git a/src/renderer/components/sidebar.tsx b/src/renderer/components/sidebar.tsx index d1095265..f7efe1fc 100644 --- a/src/renderer/components/sidebar.tsx +++ b/src/renderer/components/sidebar.tsx @@ -34,7 +34,8 @@ const enum NODE_ID { INSTALLER = 'installer', NETWORK = 'network', CACHE = 'cache', - MOBILE = 'mobile' + MOBILE = 'mobile', + CHROMIUM = 'chromium', } const DEFAULT_NODES: Array = [ @@ -67,6 +68,12 @@ const DEFAULT_NODES: Array = [ isExpanded: true, childNodes: [], nodeData: { type: 'trace' } + }, { + id: NODE_ID.CHROMIUM, + hasCaret: true, + icon: 'modal', + label: 'Chromium', + isExpanded: true }, { id: NODE_ID.RENDERER, hasCaret: true, @@ -143,7 +150,7 @@ export class Sidebar extends React.Component { Sidebar.setChildNodes(NODE_ID.NETWORK, state, processedLogFiles.netlog.map((file, i) => Sidebar.getNetlogFileNode(file, props, i))); Sidebar.setChildNodes(NODE_ID.MOBILE, state, processedLogFiles.mobile.map((file) => Sidebar.getFileNode(file, props))); Sidebar.setChildNodes(NODE_ID.TRACE, state, processedLogFiles.trace.map((file) => Sidebar.getStateFileNode(file, props))); - + Sidebar.setChildNodes(NODE_ID.CHROMIUM, state, processedLogFiles.chromium.map((file) => Sidebar.getFileNode(file, props))); return { nodes: state.nodes }; } diff --git a/src/renderer/processor.ts b/src/renderer/processor.ts index 77578b0e..c0478862 100644 --- a/src/renderer/processor.ts +++ b/src/renderer/processor.ts @@ -30,6 +30,9 @@ const SHIPIT_MAC_RGX = /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}) (.*)$/; // 2019-01-30 21:08:25> Program: Starting install, writing to C:\Users\felix\AppData\Local\SquirrelTemp const SQUIRREL_RGX = /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})> (.*)$/; +// [70491:0302/160742.806582:WARNING:gpu_process_host.cc(1303)] The GPU process has crashed 1 time(s) +const CHROMIUM_RGX = /^\[(\d+:\d{4}\/\d{6}\.\d{3,6}:[a-zA-Z]+:.*\(\d+\))\] (.*)$/; + /** * Sort an array, but do it on a different thread * @@ -165,6 +168,8 @@ export function getTypeForFile(logFile: UnzippedFile): LogType { || fileName.startsWith('attachment') || /\w{9,}_\w{9,}_\d{16,}\.txt/.test(fileName)) { return LogType.MOBILE; + } else if (fileName.startsWith('electron_debug')) { + return LogType.CHROMIUM; } return LogType.UNKNOWN; @@ -191,7 +196,8 @@ export function getTypesForFiles(logFiles: UnzippedFiles): SortedUnzippedFiles { installer: [], netlog: [], trace: [], - mobile: [] + mobile: [], + chromium: [], }; logFiles.forEach((logFile) => { @@ -913,6 +919,56 @@ export function matchLineCall(line: string): MatchResult | undefined { return; } +export function matchLineChromium(line: string): MatchResult | undefined { + // See format: https://support.google.com/chrome/a/answer/6271282 + const results = CHROMIUM_RGX.exec(line); + + if (!Array.isArray(results)) { + return undefined; + } + + const [, metadata, message] = results; + const [pid, timestamp, level, sourceFile] = metadata.split(':'); + const currentDate = new Date(); + + // ts format is MMDD/HHmmss.SSS + // this log format has no year information. Assume that the logs + // happened in the past year because why would we read stale logs? + const [date, time] = timestamp.split('/'); + const logDate = new Date( + currentDate.getFullYear(), + parseInt(date.slice(0, 2), 10) - 1, // month (0-indexed) + parseInt(date.slice(2, 4), 10), // day + parseInt(time.slice(0, 2), 10), // hour + parseInt(time.slice(2, 4), 10), // minute + parseInt(time.slice(4, 6), 10), // second + parseInt(time.slice(7, 10), 10) // millisecond + ); + + // make sure we aren't time traveling. Maybe this + // log happened in the last calendar year? + if (logDate > currentDate) { + logDate.setFullYear(logDate.getFullYear() - 1); + } + + // FIXME: make this more robust for all chromium log levels + const LEVEL_MAP = { + WARNING: 'warn', + INFO: 'info', + ERROR: 'error', + }; + + return { + level: LEVEL_MAP[level], + message, + momentValue: logDate.valueOf(), + meta: { + sourceFile: sourceFile.split('(')[0], + pid + }, + }; +} + /** * Returns the correct match line function for a given log type. * @@ -947,6 +1003,8 @@ export function getMatchFunction( } else { return matchLineMobile; } + } else if (logType === LogType.CHROMIUM) { + return matchLineChromium; } else { return matchLineElectron; } diff --git a/test/utils/get-first-logfile.test.ts b/test/utils/get-first-logfile.test.ts index 2097db15..733c93ed 100644 --- a/test/utils/get-first-logfile.test.ts +++ b/test/utils/get-first-logfile.test.ts @@ -22,7 +22,8 @@ const files: ProcessedLogFiles = { netlog: [], installer: [], trace: [], - mobile: [] + mobile: [], + chromium: [], }; describe('getFirstLogFile', () => {