From d9fd5d40236bd2e5389dd40b88fe780d1086f804 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hubert=20Figui=C3=A8re?= Date: Thu, 19 Dec 2013 14:58:36 -0500 Subject: [PATCH 1/2] Bug 917717 - Collect the memory usage for the process after running the start test. --- package.json | 1 + tests/performance/app.js | 10 +- tests/performance/getappname.js | 14 +++ tests/performance/getappname_atom.js | 6 ++ tests/performance/meminfo.js | 117 ++++++++++++++++++++++++ tests/performance/performance_helper.js | 52 ++++++++++- tests/performance/startup_test.js | 4 + tests/reporters/jsonmozperf.js | 8 +- 8 files changed, 208 insertions(+), 4 deletions(-) create mode 100644 tests/performance/getappname.js create mode 100644 tests/performance/getappname_atom.js create mode 100644 tests/performance/meminfo.js diff --git a/package.json b/package.json index 4f8249f3bcc6..488caad6c3b0 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "devDependencies": { "debug": "*", "empty-port": "~0.0.1", + "exec-sync": "*", "jshint": "2.4.1", "mail-fakeservers": "~0.0.11", "marionette-apps": "~0.3.3", diff --git a/tests/performance/app.js b/tests/performance/app.js index 1d785822e4cf..94ae278f9788 100644 --- a/tests/performance/app.js +++ b/tests/performance/app.js @@ -31,6 +31,9 @@ PerfTestApp.prototype = { selectors: {}, + /** the Webapp instance. */ + instance: null, + PERFORMANCE_ATOM: 'window.wrappedJSObject.PerformanceHelperAtom', defaultCallback: function() { @@ -40,7 +43,12 @@ PerfTestApp.prototype = { * Launches app, switches to frame, and waits for it to be loaded. */ launch: function() { - this.client.apps.launch(this.origin, this.entryPoint); + var self = this; + this.client.apps.launch(this.origin, this.entryPoint, function(err, app) { + if (app) { + self.instance = app; + } + }); this.client.apps.switchToApp(this.origin); this.client.helper.waitForElement('body'); }, diff --git a/tests/performance/getappname.js b/tests/performance/getappname.js new file mode 100644 index 000000000000..79fc895f652b --- /dev/null +++ b/tests/performance/getappname.js @@ -0,0 +1,14 @@ +var fs = require('fs'); + +function GetAppName(app) +{ + var client = app.client.scope({ context: 'chrome' }); + var appName = client.executeScript( + 'var manifestUrl = \'' + + app.instance.manifestURL + '\';\n' + + fs.readFileSync('./tests/performance/getappname_atom.js') + ); + return appName; +} + +module.exports = GetAppName; diff --git a/tests/performance/getappname_atom.js b/tests/performance/getappname_atom.js new file mode 100644 index 000000000000..19212cb99e72 --- /dev/null +++ b/tests/performance/getappname_atom.js @@ -0,0 +1,6 @@ + +return (function () { + Cu.import('resource://gre/modules/Webapps.jsm'); + let app = DOMApplicationRegistry.getAppByManifestURL(manifestUrl); + return app ? app.name : null; +})(); diff --git a/tests/performance/meminfo.js b/tests/performance/meminfo.js new file mode 100644 index 000000000000..6ea3a716a4ab --- /dev/null +++ b/tests/performance/meminfo.js @@ -0,0 +1,117 @@ +'use strict'; + +/* + * Run b2g-info in the device *synchronously*. + * + * This is because all the tests are synchronous. + */ + +var fs = require('fs'), + execSync = require('exec-sync') + +function b2ginfo() { + var adb = execSync('adb shell b2g-info', true); + return adb; +} + +function isNotEmpty(str) { + return str != ''; +} + +var MemInfo = { + + _meminfo: [], + + /* keys for fields */ + _keys: [], + + _currentLineNumber: 0, + /* state in parser */ + _state: 'none', // 'ps', 'done' + + _parsePsLine: function MemInfo_parsePsLine(line, keys) { + var fields = line.split(' ').filter(isNotEmpty); + if (fields.length < keys.length) { + console.error('Not enough fields given the number of keys.'); + return null; + } + + var numKeys = keys.length; + var last = fields.length - 1; + var idx = last; + var ps = {}; + while(Object.keys(ps).length < numKeys) { + ps[keys[numKeys - 1 - (last - idx)]] = fields[idx]; + idx--; + } + + // now fix the process name as we have some with spaces in it + // oh the joy of parsing text output. + if(idx >= 0) { + fields.splice(idx + 1, fields.length); + var s = ''; + fields.forEach(function (e) { + s += (s.length ? ' ' : '') + e; + }); + ps[keys[0]] = s + ' ' + ps[keys[0]]; + } + return ps; + }, + + _processLine: function(line) { + this._currentLineNumber++; + if (this._state == 'done') + return; + + if (this._currentLineNumber == 1) + return; // skip first line. XXX maybe parse it to know the units. + + if (this._currentLineNumber == 2) { + this._keys = line.split(' ').filter(isNotEmpty); + this._state = 'ps'; + return; + } + + if (this._state == 'ps') { + if (line == '') { + // an empty line means we are done with ps. + // if we need more info change the state to something else. + this._state = 'done'; + return; + } + + var ps = this._parsePsLine(line, this._keys); + if(ps != null) { + this._meminfo.push(ps); + } + } + }, + + meminfo: function MemInfo_meminfo() { + + var output = b2ginfo().stdout; + + var i = 0; + while (i < output.length) { + // EOL is CRLF. + var j = output.indexOf('\r\n', i); + if (j == -1) { + j = output.length; + } + this._processLine(output.substr(i, j - i)); + // skip 2 (CRLF) + i = j + 2; + } + + return this._meminfo; + } + +}; + +module.exports = MemInfo; + +if(require.main === module) { + console.log('Tested MemInfo'); + console.log(MemInfo.meminfo()); +} + diff --git a/tests/performance/performance_helper.js b/tests/performance/performance_helper.js index cf99a12e21dd..77d5020ac216 100644 --- a/tests/performance/performance_helper.js +++ b/tests/performance/performance_helper.js @@ -1,6 +1,8 @@ 'use strict'; var MarionetteHelper = requireGaia('/tests/js-marionette/helper.js'); +var GetAppName = requireGaia('/tests/performance/getappname.js'); +var MemInfo = requireGaia('/tests/performance/meminfo.js'); // Function to send results to the reporter that is OOP // Basically writing to the mocha-json-proxy @@ -86,10 +88,17 @@ function PerformanceHelper(opts) { var mozPerfDurations = {}; mozPerfDurations[title] = values; sendResults('mozPerfDuration', mozPerfDurations); + }, + + reportMemory: function(values, title) { + title = title || ''; + var mozPerfMemory = {}; + mozPerfMemory[title] = values; + sendResults('mozPerfMemory', mozPerfMemory); } }); - PerformanceHelper.prototype = { +PerformanceHelper.prototype = { reportRunDurations: function(runResults) { var start = runResults.start || 0; @@ -174,8 +183,47 @@ function PerformanceHelper(opts) { waitForPerfEvent: function(callback) { this.app.waitForPerfEvents(this.opts.lastEvent, callback); + }, + + /* + * Get the memory stats for the specified app + * as well as the main b2g. + * See bug 917717. + */ + getMemoryUsage: function(app) { + var appName = GetAppName(app); + var meminfo = MemInfo.meminfo(); + var info = null; + var system = null; + meminfo.some(function(element) { + if(element.NAME == appName) { + info = element; + } else if(element.NAME == 'b2g') { + system = element; + } + return info && system; + }); + if (info) { + return { + app: { + name: info.NAME, + uss: parseFloat(info.USS), + pss: parseFloat(info.PSS), + rss: parseFloat(info.RSS), + vsize: parseFloat(info.VSIZE) + }, + system: { + name: system.NAME, + uss: parseFloat(system.USS), + pss: parseFloat(system.PSS), + rss: parseFloat(system.RSS), + vsize: parseFloat(system.VSIZE) + } + }; } - }; + return null; + } +}; module.exports = PerformanceHelper; diff --git a/tests/performance/startup_test.js b/tests/performance/startup_test.js index 3be65343908e..50e7c55a1940 100644 --- a/tests/performance/startup_test.js +++ b/tests/performance/startup_test.js @@ -41,8 +41,11 @@ marionette('startup test > ' + mozTestInfo.appPath + ' >', function() { PerformanceHelper.registerLoadTimeListener(client); + var mem_stats = []; performanceHelper.repeatWithDelay(function(app, next) { app.launch(); + var mem_usage = performanceHelper.getMemoryUsage(app); + mem_stats.push(mem_usage); app.close(); }); @@ -61,6 +64,7 @@ marionette('startup test > ' + mozTestInfo.appPath + ' >', function() { }); PerformanceHelper.reportDuration(results); + PerformanceHelper.reportMemory(mem_stats); PerformanceHelper.unregisterLoadTimeListener(client); }); diff --git a/tests/reporters/jsonmozperf.js b/tests/reporters/jsonmozperf.js index 31620f64e81d..a86815416b59 100644 --- a/tests/reporters/jsonmozperf.js +++ b/tests/reporters/jsonmozperf.js @@ -18,6 +18,7 @@ function JSONMozPerfReporter(runner) { var failures = []; var passes = []; var mozPerfDurations; + var mozPerfMemory; runner.on('test', function(test) { mozPerfDurations = []; @@ -27,6 +28,10 @@ function JSONMozPerfReporter(runner) { mozPerfDurations = content; }); + runner.on('mozPerfMemory', function(content) { + mozPerfMemory = content; + }); + runner.on('pass', function(test) { if (mozPerfDurations === null) { @@ -43,7 +48,8 @@ function JSONMozPerfReporter(runner) { fullTitle: test.fullTitle() + ' ' + title, duration: test.duration, mozPerfDurations: mozPerfDurations[title], - mozPerfDurationsAverage: average(mozPerfDurations[title]) + mozPerfDurationsAverage: average(mozPerfDurations[title]), + mozPerfMemory: mozPerfMemory[title], }); } }); From d0ed4dc16fcd7edcbcce9fd5ad90b4ce9daa8f72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hubert=20Figui=C3=A8re?= Date: Mon, 13 Jan 2014 11:48:46 -0500 Subject: [PATCH 2/2] Bug 917717 - Part 2: nits. --- tests/performance/performance_helper.js | 38 +++++++++++++------------ tests/performance/startup_test.js | 8 +++--- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/tests/performance/performance_helper.js b/tests/performance/performance_helper.js index 77d5020ac216..e38b5c884f59 100644 --- a/tests/performance/performance_helper.js +++ b/tests/performance/performance_helper.js @@ -203,25 +203,27 @@ PerformanceHelper.prototype = { } return info && system; }); - if (info) { - return { - app: { - name: info.NAME, - uss: parseFloat(info.USS), - pss: parseFloat(info.PSS), - rss: parseFloat(info.RSS), - vsize: parseFloat(info.VSIZE) - }, - system: { - name: system.NAME, - uss: parseFloat(system.USS), - pss: parseFloat(system.PSS), - rss: parseFloat(system.RSS), - vsize: parseFloat(system.VSIZE) - } - }; + + if (!info) { + return null; } - return null; + + return { + app: { + name: info.NAME, + uss: parseFloat(info.USS), + pss: parseFloat(info.PSS), + rss: parseFloat(info.RSS), + vsize: parseFloat(info.VSIZE) + }, + system: { + name: system.NAME, + uss: parseFloat(system.USS), + pss: parseFloat(system.PSS), + rss: parseFloat(system.RSS), + vsize: parseFloat(system.VSIZE) + } + }; } }; diff --git a/tests/performance/startup_test.js b/tests/performance/startup_test.js index 50e7c55a1940..786b29657690 100644 --- a/tests/performance/startup_test.js +++ b/tests/performance/startup_test.js @@ -41,11 +41,11 @@ marionette('startup test > ' + mozTestInfo.appPath + ' >', function() { PerformanceHelper.registerLoadTimeListener(client); - var mem_stats = []; + var memStats = []; performanceHelper.repeatWithDelay(function(app, next) { app.launch(); - var mem_usage = performanceHelper.getMemoryUsage(app); - mem_stats.push(mem_usage); + var memUsage = performanceHelper.getMemoryUsage(app); + memStats.push(memUsage); app.close(); }); @@ -64,7 +64,7 @@ marionette('startup test > ' + mozTestInfo.appPath + ' >', function() { }); PerformanceHelper.reportDuration(results); - PerformanceHelper.reportMemory(mem_stats); + PerformanceHelper.reportMemory(memStats); PerformanceHelper.unregisterLoadTimeListener(client); });