apps: create state tracking object for WifiStateMachine that holds notes
When processing Android logs, state will be kept in a new
WifiStateMachine object. This object holds an array of notes. The
notes are comprised of flagged messages that were found during
processing.
After processing, the log processor displays a summary for the supplied
log. In this summary, the notes for the WifiStateMachine are displayed.
Each note contains a link to the (highlighted) log line in the displayed
log.
Two tests were also added:
1. Test for proper regular expression matching for the Android log
timestamp.
2. A sample log is processed and the resulting WifiStateMachine object
is confirmed to have the correct number of notes when complete.
BUG=chromium:577353
TEST=manually with Chrome OS, Brillo and Android logs
TEST=ran 'grunt test'
Change-Id: I2935f7bb0408dc8427f3fc52fd96c5beeda2ca56
diff --git a/Gruntfile.js b/Gruntfile.js
index fd4d27e..cc0d119 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -28,7 +28,8 @@
'./manager.js',
'./netlog_summary.js',
'./service.js',
- './syslog_summary.js'
+ './syslog_summary.js',
+ './wifi_state_machine.js'
],
options: {
'specs': 'test/spec/**/*.js'
diff --git a/androidlog_summary.js b/androidlog_summary.js
index d0ead0d..f09fd4b 100644
--- a/androidlog_summary.js
+++ b/androidlog_summary.js
@@ -10,21 +10,6 @@
var androidlogSummary = {};
/**
- * Object used for passing state between main processing loop and
- * regeular expression handler functions.
- * @constructor
- * @type {Object}
- * @param {LogSummary} logSummary object holding log state after processing
- * @param {String} time timestamp of current log line
- * @param {String[]} results results from regex test
- */
-function AndroidlogProcessingState(logSummary, time, results) {
- this.logSummary = logSummary;
- this.time = time;
- this.results = results;
-}
-
-/**
* RegExp for android log line.
* @type {RegExp}
*/
@@ -39,7 +24,9 @@
'supplicant_disconnected': {
re: /wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED /,
handler: function(processingState, result) {
- // TODO: this will be added to a summary notes section
+ logHelper.addNote(processingState.wifiStateMachine,
+ processingState.time,
+ result[0]);
}
}
};
@@ -55,46 +42,81 @@
* was processed, null if the log is not a recognized format.
*/
androidlogSummary.processLogLines = function(logLines, logSummary) {
- var logStart = -1;
- var time;
+
+ /**
+ * Object used for passing state between main processing loop and
+ * regeular expression handler functions.
+ * @constructor
+ * @type {Object}
+ * @param {LogSummary} logSummary object holding log state after processing
+ * @param {String} time timestamp of current log line
+ * @param {String[]} results results from regex test
+ */
+ function AndroidlogProcessingState(logSummary, time, results) {
+ this.logSummary = logSummary;
+ this.time = time;
+ this.results = results;
+ }
+
+ var logLineTime;
var logText = [];
- var timeCheck;
- var result = null;
- var processingState = new ProcessingState(logSummary, null, null, null);
+ var parsedLogLineTime;
+ var processingState = new AndroidlogProcessingState(logSummary, null, null);
var logType = 'android';
for (var i = 0; i < logLines.length; i++) {
console.log('processing log line: ' + logLines[i]);
- time = logLines[i].match(logHelper.ANDROID_TIME_FORMAT);
- processingState.time = time;
- if (time == null) {
- console.log('not a valid time... continue');
+ logLineTime = logLines[i].match(logHelper.ANDROID_TIME_FORMAT);
+ if (logLineTime === null) {
+ console.log('not a valid time... skip the log line');
continue;
}
- timeCheck = Date.parse(time[0]);
+ processingState.time = logLineTime[0];
+ parsedLogLineTime = Date.parse(processingState.time);
+
+ if (logSummary.logStartTime == -1) {
+ logSummary.logStartTime = parsedLogLineTime;
+ // This is the start of our log so we need to add the first
+ // WifiStateMachine instance.
+ processingState.wifiStateMachine =
+ new WifiStateMachine(1,
+ parsedLogLineTime,
+ logHelper.getOffsetMS(processingState.time));
+ logSummary.wifiStateMachines.push(processingState.wifiStateMachine);
+ }
+
+ if (parsedLogLineTime < logSummary.logEndTime) {
+ console.log('warning: log time has rolled back, check timezone');
+ } else {
+ logSummary.logEndTime = parsedLogLineTime;
+ }
var tempLogText = {text: logLines[i],
- tag: time[0],
+ tag: null,
type: 'basic',
file: logType,
- ts: timeCheck};
- result = null;
+ ts: parsedLogLineTime};
+
for (var taggedLine in androidlogSummary.taggedLines) {
var result = logLines[i].match(
androidlogSummary.taggedLines[taggedLine].re);
- if (result != null) {
+ if (result !== null) {
tempLogText.type =
androidlogSummary.taggedLines[taggedLine].handler(processingState,
result);
+ // Since this line does have a match with the flagged log lines, create
+ // a tag that allows the summary notes to link to the displayed log.
+ tempLogText.tag = processingState.time;
break;
}
}
- if (!result) {
- tempLogText.tag = null;
- }
logText.push(tempLogText);
}
+ if (logSummary.wifiStateMachines.length > 0) {
+ logSummary.wifiStateMachines[logSummary.wifiStateMachines.length - 1]
+ .endTime = logSummary.logEndTime;
+ }
return logText;
};
diff --git a/log_summary.js b/log_summary.js
index f52b04d..59f77fc 100644
--- a/log_summary.js
+++ b/log_summary.js
@@ -10,6 +10,7 @@
/** @constructor */
function LogSummary() {
this.managers = [];
+ this.wifiStateMachines = [];
this.logStartTime = -1;
this.logEndTime = -1;
this.prevLogLineTime = -1;
diff --git a/netlog_summary.js b/netlog_summary.js
index fea6bef..977a754 100644
--- a/netlog_summary.js
+++ b/netlog_summary.js
@@ -451,11 +451,27 @@
* was processed, null if the log is not a recognized format.
*/
netlogSummary.processLogLines = function(logLines, logSummary) {
+ /**
+ * Object used for passing state between main processing loop and
+ * regeular expression handler functions.
+ * @constructor
+ * @type {Object}
+ * @param {LogSummary} logSummary object holding log state after processing
+ * @param {Manager} currentManager object of the current manager in log
+ * @param {String} time timestamp of current log line
+ * @param {String[]} results results from regex test
+ */
+ function ProcessingState(logSummary, currentManager, time, results) {
+ this.logSummary = logSummary;
+ this.currentManager = currentManager;
+ this.time = time;
+ this.results = results;
+ }
+
var logStart = -1;
var time;
var logText = [];
var timeCheck;
- var result = null;
var processingState = new ProcessingState(logSummary, null, null, null);
var logType = 'netlog';
@@ -476,7 +492,7 @@
}
logHelper.getOffsetMS(time);
timeCheck = Date.parse(time[0]);
- if (logStart == -1 && time != null) {
+ if (logStart == -1 && time !== null) {
logStart = time[0];
logSummary.logStartTime = timeCheck;
logSummary.prevLogLineTime = logSummary.logStartTime;
@@ -498,23 +514,20 @@
}
console.log('log_line: ' + logLines[i]);
var tempLogText = {text: logLines[i],
- tag: time[0],
+ tag: null,
type: 'basic',
file: logType,
ts: timeCheck};
- result = null;
for (var taggedLine in netlogSummary.taggedLines) {
var result = logLines[i].match(netlogSummary.taggedLines[taggedLine].re);
- if (result != null) {
+ if (result !== null) {
tempLogText.type =
netlogSummary.taggedLines[taggedLine].handler(processingState,
result);
+ tempLogText.tag = time[0];
break;
}
}
- if (!result) {
- tempLogText.tag = null;
- }
logText.push(tempLogText);
}
diff --git a/process_log.js b/process_log.js
index d2850c8..6f0e40a 100644
--- a/process_log.js
+++ b/process_log.js
@@ -80,6 +80,10 @@
displayLogSummary(logSummary);
displayServiceSummary(logSummary);
}
+ if (logHolder.androidlog) {
+ displayAndroidLogSummary(logSummary);
+ }
+
if (logHolder.syslog || logHolder.netlog ||
logHolder.brillolog || logHolder.androidlog) {
displayLog(logHolder);
@@ -107,6 +111,61 @@
}
/**
+ * Method to display log-level summary information after processing the
+ * Android log.
+ *
+ * @param {LogSummary} logSummary Object to hold state read from log.
+ * @private
+ */
+
+function displayAndroidLogSummary(logSummary) {
+ console.log('inside android log summary!');
+ var wsm = document.getElementsByClassName('managers')[0];
+ var logHeader = document.createElement('h2');
+ logHeader.innerText = 'Log Summary';
+ wsm.appendChild(logHeader);
+
+ var timeInfo = document.createElement('p');
+ timeInfo.innerText = 'Total Log Time: ' +
+ logHelper.formatElapsedMS(logSummary.logEndTime -
+ logSummary.logStartTime);
+ wsm.appendChild(timeInfo);
+
+ var wifiStateMachines = logSummary.wifiStateMachines;
+ var wsmInfo;
+ for (var i = 0; i < wifiStateMachines.length; i++) {
+ wsmInfo = document.createElement('p');
+ wsmInfo.innerText = 'WifiStateMachine ' + wifiStateMachines[i].id + ": " +
+ logHelper.formatElapsedMS(wifiStateMachines[i].endTime -
+ wifiStateMachines[i].startTime);
+ wsm.appendChild(wsmInfo);
+
+ //TODO: add post processing for WSM states before graphing
+ }
+
+ //Now add in notes for the WSM
+ var wsmHeader = document.createElement('h2');
+ wsmHeader.innerText = 'WifiStateMachines';
+ wsm.appendChild(wsmHeader);
+
+ var wsmOL = document.createElement('ol');
+ var wifiStateMachineNotes;
+ for (var i = 0; i < wifiStateMachines.length; i++) {
+ wifiStateMachineNotes = document.createElement('li');
+ wifiStateMachineNotes.id = 'wsm_' + i;
+ wifiStateMachineNotes.innerText = 'WifiStateMachine ' +
+ wifiStateMachines[i].id;
+ wifiStateMachineNotes.className = 'wsm';
+ if (wifiStateMachines[i].notes && wifiStateMachines[i].notes.length > 0) {
+ wifiStateMachineNotes.appendChild(
+ displayNotes(logSummary, wifiStateMachines[i].notes));
+ }
+ wsmOL.appendChild(wifiStateMachineNotes);
+ }
+ wsm.appendChild(wsmOL);
+}
+
+/**
* Method to display log-level summary information after processing the log.
*
* @param {LogSummary} logSummary Object to hold state read from log.
diff --git a/service_states.html b/service_states.html
index b27f907..e1cc89d 100644
--- a/service_states.html
+++ b/service_states.html
@@ -14,6 +14,7 @@
<script src="process_log.js"></script>
<script src="syslog_summary.js"></script>
<script src="androidlog_summary.js"></script>
+ <script src="wifi_state_machine.js"></script>
</head>
<body>
<h1>ChromeOS Network Log Processor</h1>
diff --git a/syslog_summary.js b/syslog_summary.js
index 888fd23..9bc7e5c 100644
--- a/syslog_summary.js
+++ b/syslog_summary.js
@@ -10,21 +10,6 @@
var syslogSummary = {};
/**
- * Object used for passing state between main processing loop and
- * regeular expression handler functions.
- * @constructor
- * @type {Object}
- * @param {LogSummary} logSummary object holding log state after processing
- * @param {String} time timestamp of current log line
- * @param {String[]} results results from regex test
- */
-function SyslogProcessingState(logSummary, time, results) {
- this.logSummary = logSummary;
- this.time = time;
- this.results = results;
-}
-
-/**
* RegExp for kernel info logging lines.
* @type {RegExp}
*/
@@ -106,6 +91,21 @@
* was processed, null if the log is not a recognized format.
*/
syslogSummary.processLogLines = function(logLines, logSummary) {
+ /**
+ * Object used for passing state between main processing loop and
+ * regeular expression handler functions.
+ * @constructor
+ * @type {Object}
+ * @param {LogSummary} logSummary object holding log state after processing
+ * @param {String} time timestamp of current log line
+ * @param {String[]} results results from regex test
+ */
+ function SyslogProcessingState(logSummary, time, results) {
+ this.logSummary = logSummary;
+ this.time = time;
+ this.results = results;
+ }
+
var logText = [];
var time;
var processingState = new SyslogProcessingState(logSummary, null, null);
@@ -125,11 +125,11 @@
tag = null;
for (var taggedLine in syslogSummary.taggedLines) {
var result = logLines[i].match(syslogSummary.taggedLines[taggedLine].re);
- if (result != null) {
+ if (result !== null) {
processingState.time = time[0];
processingState.result = result;
type = syslogSummary.taggedLines[taggedLine].handler(processingState);
- if (type == null) {
+ if (type === null) {
tag = null;
} else {
tag = time[0];
diff --git a/test/spec/AndroidlogSummarySpec.js b/test/spec/AndroidlogSummarySpec.js
new file mode 100644
index 0000000..4bbf78c
--- /dev/null
+++ b/test/spec/AndroidlogSummarySpec.js
@@ -0,0 +1,51 @@
+// Copyright 2016 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.
+
+/**
+ * @fileoverview Jasmine test file for androidlog_summary.js.
+ */
+
+describe('AndroidlogSummary', function() {
+ var logSummary;
+ var basicLog;
+ var time1;
+ var time2;
+ var time3;
+
+ beforeEach(function() {
+ logSummary = new LogSummary();
+ time1 = '01-04 14:45:37.720';
+ time2 = '01-04 14:45:37.721';
+ time3 = '01-04 14:45:37.722';
+
+ basicLog = [
+ time1 + ' wpa_supplicant: Deauthentication frame IE(s)' +
+ ' - hexdump(len=0): [NULL]',
+ time2 + ' wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED' +
+ ' bssid=24:de:c6:e0:89:51 reason=3 locally_generated=1',
+ time3 + ' WifiStateMachine: setDetailed state, old =OBTAINING_IPADDR' +
+ ' and new state=DISCONNECTED hidden=false'
+ ];
+
+ });
+
+ it('should properly match the Android log time format', function() {
+ var regexMatchTime = time1.match(logHelper.ANDROID_TIME_FORMAT)[0];
+ expect(regexMatchTime).toEqual(time1);
+ });
+
+ it('should detect wpa_supplicant disconnects and add to notes', function() {
+ androidlogSummary.processLogLines(basicLog, logSummary);
+ expect(logSummary.wifiStateMachines.length).toBe(1);
+ expect(logSummary.wifiStateMachines[0].notes.length).toBe(1);
+ });
+
+ it('should skip a log line without a valid timestamp', function() {
+ var log = [' wpa_supplicant: Deauthentication frame IE(s)' +
+ ' - hexdump(len=0): [NULL]'];
+ androidlogSummary.processLogLines(log, logSummary);
+ expect(logSummary.wifiStateMachines.length).toBe(0);
+ expect(logSummary.logStartTime).toBe(-1);
+ });
+});
diff --git a/wifi_state_machine.js b/wifi_state_machine.js
new file mode 100644
index 0000000..eae4532
--- /dev/null
+++ b/wifi_state_machine.js
@@ -0,0 +1,27 @@
+// Copyright 2016 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 WiFiStateMachine class to represent and hold state for each
+ * instance of an Android WiFiStateMachine for a given log being processed.
+ */
+
+/**
+ * @constructor
+ *
+ * @param {String} id Identifier for new WifiStateMachine instance.
+ * @param {long} ms WifiStateMachine start time in ms.
+ * @param {long} offset WifiStateMachine log time offset in ms.
+ */
+function WifiStateMachine(id, ms, offset) {
+ this.id = id;
+ this.startTime = ms;
+ this.scans = 0;
+ this.scanDetails = [];
+ this.states = [];
+ this.endTime = -1;
+ this.timeOffset = offset;
+ this.supplicantStates = [];
+}