blob: b2b6ffd22f225363247495d7688d89f0ac912a46 [file] [log] [blame]
// Copyright 2015 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
'use strict';
/**
* @fileoverview Helper class for shill log processing.
*/
var logHelper = {};
/**
* RegExp for netlog start.
* @type {RegExp}
*/
logHelper.NET_LOG_START = /netlog=<multiline>/;
/**
* RegExp for netlog end.
* @type {RegExp}
*/
logHelper.NET_LOG_END = /---------- END ----------/;
/**
* RegExp for syslog start.
* @type {RegExp}
*/
logHelper.SYS_LOG_START = /syslog=<multiline>/;
/**
* RegExp for system.log section end.
* @type {RegExp}
*/
logHelper.MULTILINE_LOG_END = /---------- END ----------/;
/**
* RegExp for brillo log start.
* @type {RegExp}
*/
logHelper.BRILLO_LOG_START = /--------- beginning of main/;
/**
* RegExp for start of main log section.
* @type {RegExp}
*/
logHelper.ANDROID_MAIN_SECTION = /--------- beginning of main/;
/**
* RegExp for end of Android log section.
* @type {RegExp}
*/
logHelper.ANDROID_SECTION_END = /\[logcat: \d+.\d+s elapsed\]/;
/**
* RegExp for start of "LAST LOGCAT" section.
* @type {RegExp}
*/
logHelper.ANDROID_LAST_LOGCAT_SECTION = / LAST LOGCAT /;
/**
* RegExp for start of Android bug report file
* @type {RegExp}
*/
logHelper.ANDROID_BUG_REPORT =
/== dumpstate: \d{4}-\d{2}-\d{2}.\d{2}:\d{2}:\d{2}/;
/**
* RegExp for log timestamp.
* Sample timestamp from ChomeOS net.log:
* 2015-10-28T15:40:30.879026-05:00
* @type {RegExp}
*/
logHelper.TIME_FORMAT =
/\d{4}-\d+-\d+T\d{2}:\d{2}:\d{2}.\d{6}(-|\+)(\d{2}):(\d{2})/;
/**
* RegExp for brillo log timestamp. Due to an issue with multiple times being
* logged, also eliminates log lines that appear to be from the kernel.
* Sample valid timestamp from Brillo log:
* 10-30 22:49:30.243 238 238
* Sample timestamp to skip from Brillo log:
* 10-30 22:49:30.243 0 0
* @type {RegExp}
*/
logHelper.BRILLO_TIME_FORMAT =
/(\d{2}-\d{2}.\d{2}:\d{2}:\d{2}.\d{3})(?: +[1-9][0-9]* +[1-9][0-9]*)/;
/**
* RegExp for Android log timestamp.
* Sample valid timestamp from Android log:
* 11-20 12:08:06.412
* @type {RegExp}
*/
logHelper.ANDROID_TIME_FORMAT = /(\d{2}-\d{2}.\d{2}:\d{2}:\d{2}.\d{3})/;
/**
* Method to get time offset from log line.
*
* @param {Object} timeMatchResults results from matching regex.
* @return {Number} offsetMS offset ms for log time.
* @public
*/
logHelper.getOffsetMS = function(timeMatchResults) {
if (timeMatchResults.length == 2) {
// Brillo logs omit time zone. Assume all lines in same time zone.
return 0;
}
// first get minutes
var offset = parseInt(timeMatchResults[3]);
// next get hours
offset += parseInt(timeMatchResults[2]) * 60;
if (offset == 0) {
// returns -0 when attempting to test -00:00 offset
return 0;
}
// convert to ms
offset = offset * 60 * 1000;
// and check + or -
if (timeMatchResults[1] == '-')
offset *= -1;
return offset;
};
/**
* Method to format an elapsed time in ms to HH:MM:SS.
*
* @param {Number} elapsedMS elapsed ms to format.
* @return {string} HH:MM:SS formatted version of the elapsed (ms) time
* @public
*/
logHelper.formatElapsedMS = function(elapsedMS) {
var ms = elapsedMS % 1000;
if (ms < 10) {
ms = '00' + ms;
} else if (ms < 100) {
ms = '0' + ms;
}
var elapsed = Math.floor(elapsedMS / 1000);
var sec = Math.floor(elapsed % 3600 % 60);
if (sec < 10) {
sec = '0' + sec;
}
var min = Math.floor(elapsed % 3600 / 60);
if (min < 10) {
min = '0' + min;
}
var hour = Math.floor(elapsed / 3600);
if (hour < 10) {
hour = '0' + hour;
}
var formatted = hour + ':' + min + ':' + sec + '.' + ms;
return formatted;
};
/**
* Array of Objects holding regexs and handlers for system_log subsections
* @type {Object[]}
* @return {int} returns new index into supplied String[]
*/
logHelper.logDetectors = {
'android_bugreport': {
re: logHelper.ANDROID_BUG_REPORT,
handler: function(logHolder, logLines, i) {
logHolder.fileType = 'android_log';
while (i < logLines.length) {
if (logHelper.ANDROID_LAST_LOGCAT_SECTION.test(logLines[i])) {
// this is the "LAST LOGCAT" section... skip.
var lastLogcatSectionLength = logHelper.getSubLog(logLines, i).length;
console.log('skipping ' + lastLogcatSectionLength +
' line(s) of \"LAST LOGCAT\" section');
i = i + lastLogcatSectionLength;
} else if (logHelper.ANDROID_MAIN_SECTION.test(logLines[i])) {
// found start of main log
console.log('found start of main section');
break;
}
i++;
}
logHolder.androidlog = logHelper.getSubLog(logLines, i);
console.log('found an android bug report: ' +
logHolder.androidlog.length);
return i + logHolder.androidlog.length;
}
},
'android': {
re: logHelper.ANDROID_MAIN_SECTION,
handler: function(logHolder, logLines, i) {
var tempLogHolder = logHelper.getSubLog(logLines, i);
if (androidlogSummary.androidlogTypeCheck(tempLogHolder)) {
logHolder.androidlog = tempLogHolder;
logHolder.fileType = 'android_log';
} else if (netlogSummary.brillologTypeCheck(tempLogHolder)) {
logHolder.brillolog = tempLogHolder;
logHolder.fileType = 'brillo_log';
} else {
console.log('Error in log type detection - ' +
'android time stamp but not android or brillo log');
return i;
}
return i + tempLogHolder.length;
}
},
'netlog_multiline': {
re: logHelper.NET_LOG_START,
handler: function(logHolder, logLines, i) {
// we have a netlog section of a system_log
logHolder.fileType = 'system_log';
logHolder.netlog = logHelper.getSubLog(logLines, i);
console.log('found the netlog section!: ' + logHolder.netlog.length);
return i + logHolder.netlog.length;
}
},
'syslog_multiline': {
re: logHelper.SYS_LOG_START,
handler: function(logHolder, logLines, i) {
// we have a syslog section of a system_log
logHolder.fileType = 'system_log';
logHolder.syslog = logHelper.getSubLog(logLines, i);
console.log('found the syslog section!: ' + logHolder.syslog.length);
return i + logHolder.syslog.length;
}
}
};
/**
* Helper method checking what type of log is being processed.
*
* @param {String[]} logLines String array for lines in a log.
* @return {Object} returns String of the file type
*/
logHelper.detectFileType = function(logLines) {
var logHolder = {fileType: 'unknown'};
var match;
// first check if the supplied log has subsections
for (var i = 0; i < logLines.length; i++) {
for (var detector in logHelper.logDetectors) {
match = logLines[i].match(logHelper.logDetectors[detector].re);
if (match != null) {
i = logHelper
.logDetectors[detector]
.handler(logHolder, logLines, i + 2) + 1;
break;
}
}
}
if (logHolder.fileType == 'unknown') {
if (androidlogSummary.androidlogTypeCheck(logLines)) {
// supplied file is an android log file
logHolder.fileType = 'android_log';
logHolder.androidlog = logLines;
} else if (netlogSummary.netlogTypeCheck(logLines)) {
// supplied file is netlog
logHolder.fileType = 'netlog';
logHolder.netlog = logLines;
} else if (syslogSummary.syslogTypeCheck(logLines)) {
// supplied file is syslog
logHolder.fileType = 'syslog';
logHolder.syslog = logLines;
} else if (netlogSummary.brillologTypeCheck(logLines)) {
// supplied file is a brillo log
logHolder.fileType = 'brillolog';
logHolder.brillolog = logLines;
} else {
console.log('file type unknown');
}
}
return logHolder;
};
/**
* Helper method to extract a sublog of a file.
*
* @param {String[]} logLines String array for lines in a log.
* @param {int} i Start index for the sublog
* @return {String[]} returns String[] of the sublog lines
*/
logHelper.getSubLog = function(logLines, i) {
var subLog = [];
while (i < logLines.length) {
if (logHelper.MULTILINE_LOG_END.test(logLines[i])) {
// subsection is done...
return subLog;
}
if (logHelper.ANDROID_SECTION_END.test(logLines[i])) {
// subsection is done...
return subLog;
}
var time = logLines[i].match(logHelper.TIME_FORMAT);
if (time == null) {
time = logLines[i].match(logHelper.ANDROID_TIME_FORMAT);
}
if (time == null) {
time = logLines[i].match(logHelper.BRILLO_TIME_FORMAT);
}
if (time == null) {
console.log('processing sublog, log line time was null');
} else {
subLog.push(logLines[i]);
}
i++;
}
return subLog;
};
/**
* Helper method to add flagged messages to an object.
*
* @param {Object} noteHolder Object to get the new note.
* @param {Number} noteTime Time stamp from log message.
* @param {String} noteMessage Message from the log.
*/
logHelper.addNote = function(noteHolder, noteTime, noteMessage) {
var newNote = {time: noteTime, message: noteMessage};
if (!noteHolder.notes) {
noteHolder.notes = [];
}
var numNotes = noteHolder.notes.length;
if (numNotes == 0) {
noteHolder.notes.push(newNote);
return;
}
// check to see if note should be inserted at the end of the array
var ind = numNotes - 1;
while (ind > -1) {
var note = noteHolder.notes[ind];
if (note.time <= noteTime) {
// add new note after this one
noteHolder.notes.splice(ind + 1, 0, newNote);
return;
}
ind--;
}
noteHolder.notes.splice(0, 0, newNote);
};
/**
* Helper method to add supplicant states to an object.
*
* @param {Object} supplicantStates Object to get the state update.
* @param {Number} update Object holding supplicant state update information.
*/
logHelper.addSupplicantState = function(supplicantStates, update) {
if (update.time == null) {
supplicantStates.push({'x': 0, 'y': update.newState});
return;
}
if (update.oldState == update.newState) {
// not a new state transition... still check if we have anything stored
if (supplicantStates.length > 0) {
// not a new transition... return
return;
}
supplicantStates.push({'x': update.time, 'y': update.oldState});
return;
}
// now we know the states are different, we need to process this update
if (supplicantStates.length > 0) {
// should check states to make sure they match
var lastUpdate = supplicantStates[supplicantStates.length - 1];
if (lastUpdate.y != update.oldState) {
supplicantStates.push({'x': update.time,
'y': lastUpdate.y});
supplicantStates.push({'x': update.time,
'y': update.oldState});
}
} else {
// need to enter previous state - use update time since we do not have the
// start time.
supplicantStates.push({'x': update.time, 'y': update.oldState});
}
supplicantStates.push({'x': update.time,
'y': update.oldState});
supplicantStates.push({'x': update.time,
'y': update.newState});
};