author | Jan-Ivar Bruaroey <jib@mozilla.com> |
Thu, 10 Jul 2014 12:33:13 -0400 | |
changeset 193551 | f0e6af66a4cf4ae4180cb7385d9736504d2c1b15 |
parent 193550 | b336c6598bf14dc67ccc9c2d8989964adcbde5a8 |
child 193552 | ff281663a545ba282d171e1b6709d2c23b83153c |
push id | 27123 |
push user | ryanvm@gmail.com |
push date | Fri, 11 Jul 2014 20:35:05 +0000 |
treeherder | mozilla-central@84bd8d9f4256 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | vladan |
bugs | 998989 |
milestone | 33.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/browser/components/loop/MozLoopService.jsm +++ b/browser/components/loop/MozLoopService.jsm @@ -4,16 +4,17 @@ "use strict"; const { classes: Cc, interfaces: Ci, utils: Cu } = Components; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/Promise.jsm"); +Cu.import("resource://gre/modules/osfile.jsm", this); this.EXPORTED_SYMBOLS = ["MozLoopService"]; XPCOMUtils.defineLazyModuleGetter(this, "console", "resource://gre/modules/devtools/Console.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "injectLoopAPI", "resource:///modules/loop/MozLoopAPI.jsm"); @@ -47,17 +48,16 @@ XPCOMUtils.defineLazyServiceGetter(this, * * The registration is a two-part process. First we need to connect to * and register with the push server. Then we need to take the result of that * and register with the Loop server. */ let MozLoopServiceInternal = { // The uri of the Loop server. loopServerUri: Services.prefs.getCharPref("loop.server"), - telemetryUri: Services.prefs.getCharPref("loop.telemetryURL"), // The current deferred for the registration process. This is set if in progress // or the registration was successful. This is null if a registration attempt was // unsuccessful. _registeredDeferred: null, /** * The initial delay for push registration. This ensures we don't start @@ -309,75 +309,77 @@ let MozLoopServiceInternal = { map[key] = {}; map[key][property] = string.value; } return this._localizedStrings = map; }, /** - * Uploads telemetry logs to telemetryServer + * Saves loop logs to the saved-telemetry-pings folder. * * @param {Object} pc The peerConnection in question. */ - uploadTelemetry: function(window, pc) { - if (!this.telemetryUri) { - return; - } + stageForTelemetryUpload: function(window, pc) { window.WebrtcGlobalInformation.getAllStats(allStats => { let internalFormat = allStats.reports[0]; // filtered on pc.id window.WebrtcGlobalInformation.getLogging('', logs => { + let report = convertToRTCStatsReport(internalFormat); + let logStr = ""; + logs.forEach(s => { logStr += s + "\n"; }); // We have stats and logs. - // Prepare payload from https://wiki.mozilla.org/Loop/Telemetry + + // Create worker job. ping = saved telemetry ping file header + payload + // + // Prepare payload according to https://wiki.mozilla.org/Loop/Telemetry let ai = Services.appinfo; - let report = convertToRTCStatsReport(internalFormat); - - let payload = { - ver: 1, - info: { - appUpdateChannel: ai.defaultUpdateChannel, - appBuildID: ai.appBuildID, - appName: ai.name, - appVersion: ai.version, - reason: "loop", - OS: ai.OS, - version: Services.sysinfo.getProperty("version") - }, - report: "ice failure", - connectionstate: pc.iceConnectionState, - stats: report, - localSdp: internalFormat.localSdp, - remoteSdp: internalFormat.remoteSdp, - log: "" - }; - logs.forEach(s => { payload.log += s + "\n"; }); - let uuid = uuidgen.generateUUID().toString(); uuid = uuid.substr(1,uuid.length-2); // remove uuid curly braces - let url = this.telemetryUri; - url += ((url.substr(-1) == "/")? "":"/") + uuid + "/loop/" + - ai.OS + "/" + ai.version + "/" + ai.defaultUpdateChannel + "/" + - ai.appBuildID; - - // Send payload. - //TODO: gzip! - - let xhr = new window.XMLHttpRequest(); - xhr.open("POST", url, true); - xhr.setRequestHeader("Content-Type", 'application/json'); - xhr.onreadystatechange = function() { - if (xhr.readyState == 4 && xhr.status == 200) { - console.log("Failed to upload telemetry logs: " + xhr.responseText); + let directory = OS.Path.join(OS.Constants.Path.profileDir, + "saved-telemetry-pings"); + let job = { + directory: directory, + filename: uuid + ".json", + ping: { + reason: "loop", + slug: uuid, + payload: { + ver: 1, + info: { + appUpdateChannel: ai.defaultUpdateChannel, + appBuildID: ai.appBuildID, + appName: ai.name, + appVersion: ai.version, + reason: "loop", + OS: ai.OS, + version: Services.sysinfo.getProperty("version") + }, + report: "ice failure", + connectionstate: pc.iceConnectionState, + stats: report, + localSdp: internalFormat.localSdp, + remoteSdp: internalFormat.remoteSdp, + log: logStr + } } }; - xhr.send(JSON.stringify(payload)); - console.log("Uploading telemetry logs to " + url); + + // Send job to worker to do saving to + // disk for pickup by telemetry on next startup, which then uploads it. + + let worker = new ChromeWorker("MozLoopWorker.js"); + worker.onmessage = function(e) { + console.log(e.data.ok ? + "Successfully staged loop report for telemetry upload." : + ("Failed to stage loop report. Error: " + e.data.fail)); + } + worker.postMessage(job); }); }, pc.id); }, /** * Opens the chat window * * @param {Object} contentWindow The window to open the chat window in, may @@ -415,17 +417,20 @@ let MozLoopServiceInternal = { let onPCLifecycleChange = (pc, winID, type) => { if (winID != ourID) { return; } if (type == "iceconnectionstatechange") { switch(pc.iceConnectionState) { case "failed": case "disconnected": - this.uploadTelemetry(window, pc); + if (Services.telemetry.canSend || + Services.prefs.getBoolPref("toolkit.telemetry.test")) { + this.stageForTelemetryUpload(window, pc); + } break; } } }; let pc_static = new window.mozRTCPeerConnectionStatic(); pc_static.registerPeerConnectionLifecycleCallback(onPCLifecycleChange); }.bind(this), true);
new file mode 100644 --- /dev/null +++ b/browser/components/loop/MozLoopWorker.js @@ -0,0 +1,54 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * A worker dedicated to loop-report sanitation and writing for MozLoopService. + */ + +"use strict"; + +importScripts("resource://gre/modules/osfile.jsm"); + +let File = OS.File; +let Encoder = new TextEncoder(); +let Counter = 0; + +const MAX_LOOP_LOGS = 5; +/** + * Communications with the controller. + * + * Accepts messages: + * { path: filepath, ping: data } + * + * Sends messages: + * { ok: true } + * { fail: serialized_form_of_OS.File.Error } + */ + +onmessage = function(e) { + if (++Counter > MAX_LOOP_LOGS) { + postMessage({ + fail: "Maximum " + MAX_LOOP_LOGS + "loop reports reached for this session" + }); + return; + } + + let directory = e.data.directory; + let filename = e.data.filename; + let ping = e.data.ping; + + let pingStr = JSON.stringify(ping); + + // Save to disk + let array = Encoder.encode(pingStr); + try { + File.makeDir(directory, + { unixMode: OS.Constants.S_IRWXU, ignoreExisting: true }); + File.writeAtomic(OS.Path.join(directory, filename), array); + postMessage({ ok: true }); + } catch (ex if ex instanceof File.Error) { + // Instances of OS.File.Error know how to serialize themselves + postMessage({fail: File.Error.toMsg(ex)}); + } +};
--- a/browser/components/loop/moz.build +++ b/browser/components/loop/moz.build @@ -9,9 +9,10 @@ JAR_MANIFESTS += ['jar.mn'] JS_MODULES_PATH = 'modules/loop' XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell/xpcshell.ini'] EXTRA_JS_MODULES += [ 'MozLoopAPI.jsm', 'MozLoopPushHandler.jsm', 'MozLoopService.jsm', + 'MozLoopWorker.js', ]