Bug 998989 - upload telemetry logs on Loop ICE failure. r=smaug, r=abr

This commit is contained in:
Jan-Ivar Bruaroey 2014-06-09 18:14:14 -04:00
parent 19033b482e
commit fc48e8c4ac
4 changed files with 132 additions and 15 deletions

View File

@ -9,13 +9,18 @@ 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");
let console = (Cu.import("resource://gre/modules/devtools/Console.jsm", {})).console;
this.EXPORTED_SYMBOLS = ["MozLoopService"];
XPCOMUtils.defineLazyModuleGetter(this, "console",
"resource://gre/modules/devtools/Console.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "injectLoopAPI",
"resource:///modules/loop/MozLoopAPI.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "convertToRTCStatsReport",
"resource://gre/modules/media/RTCStatsReport.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Chat", "resource:///modules/Chat.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils",
@ -33,6 +38,10 @@ XPCOMUtils.defineLazyModuleGetter(this, "deriveHawkCredentials",
XPCOMUtils.defineLazyModuleGetter(this, "MozLoopPushHandler",
"resource:///modules/loop/MozLoopPushHandler.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
"@mozilla.org/uuid-generator;1",
"nsIUUIDGenerator");
/**
* Internal helper methods and state
*
@ -43,6 +52,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "MozLoopPushHandler",
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
@ -303,6 +313,70 @@ let MozLoopServiceInternal = {
return this._localizedStrings = map;
},
/**
* Uploads telemetry logs to telemetryServer
*
* @param {Object} pc The peerConnection in question.
*/
uploadTelemetry: function(window, pc) {
if (!this.telemetryUri) {
return;
}
window.WebrtcGlobalInformation.getAllStats(allStats => {
let internalFormat = allStats.reports[0]; // filtered on pc.id
window.WebrtcGlobalInformation.getLogging('', logs => {
// We have stats and logs.
// Prepare payload from 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);
}
};
xhr.send(JSON.stringify(payload));
console.log("Uploading telemetry logs to " + url);
});
}, pc.id);
},
/**
* Opens the chat window
*
@ -310,9 +384,8 @@ let MozLoopServiceInternal = {
* be null.
* @param {String} title The title of the chat window.
* @param {String} url The page to load in the chat window.
* @param {String} mode May be "minimized" or undefined.
*/
openChatWindow: function(contentWindow, title, url, mode) {
openChatWindow: function(contentWindow, title, url) {
// So I guess the origin is the loop server!?
let origin = this.loopServerUri;
url = url.spec || url;
@ -332,8 +405,30 @@ let MozLoopServiceInternal = {
return;
}
chatbox.removeEventListener("DOMContentLoaded", loaded, true);
injectLoopAPI(chatbox.contentWindow);
}, true);
let window = chatbox.contentWindow;
injectLoopAPI(window);
let ourID = window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
let onPCLifecycleChange = (pc, winID, type) => {
if (winID != ourID) {
return;
}
if (type == "iceconnectionstatechange") {
switch(pc.iceConnectionState) {
case "failed":
case "disconnected":
this.uploadTelemetry(window, pc);
break;
}
}
};
let pc_static = new window.mozRTCPeerConnectionStatic();
pc_static.registerPeerConnectionLifecycleCallback(onPCLifecycleChange);
}.bind(this), true);
};
Chat.open(contentWindow, origin, title, url, undefined, undefined, callback);

View File

@ -11,6 +11,8 @@ Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PeerConnectionIdp",
"resource://gre/modules/media/PeerConnectionIdp.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "convertToRTCStatsReport",
"resource://gre/modules/media/RTCStatsReport.jsm");
const PC_CONTRACT = "@mozilla.org/dom/peerconnection;1";
const PC_OBS_CONTRACT = "@mozilla.org/dom/peerconnectionobserver;1";
@ -187,16 +189,7 @@ function RTCStatsReport(win, dict) {
this._win = win;
this._pcid = dict.pcid;
this._report = {};
appendStats(dict.inboundRTPStreamStats, this._report);
appendStats(dict.outboundRTPStreamStats, this._report);
appendStats(dict.mediaStreamTrackStats, this._report);
appendStats(dict.mediaStreamStats, this._report);
appendStats(dict.transportStats, this._report);
appendStats(dict.iceComponentStats, this._report);
appendStats(dict.iceCandidatePairStats, this._report);
appendStats(dict.iceCandidateStats, this._report);
appendStats(dict.codecStats, this._report);
this._report = convertToRTCStatsReport(dict);
}
RTCStatsReport.prototype = {
classDescription: "RTCStatsReport",

View File

@ -0,0 +1,28 @@
/* 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/. */
"use strict";
this.EXPORTED_SYMBOLS = ["convertToRTCStatsReport"];
function convertToRTCStatsReport(dict) {
function appendStats(stats, report) {
stats.forEach(function(stat) {
report[stat.id] = stat;
});
}
let report = {};
appendStats(dict.inboundRTPStreamStats, report);
appendStats(dict.outboundRTPStreamStats, report);
appendStats(dict.mediaStreamTrackStats, report);
appendStats(dict.mediaStreamStats, report);
appendStats(dict.transportStats, report);
appendStats(dict.iceComponentStats, report);
appendStats(dict.iceCandidatePairStats, report);
appendStats(dict.iceCandidateStats, report);
appendStats(dict.codecStats, report);
return report;
}
this.convertToRTCStatsReport = convertToRTCStatsReport;

View File

@ -50,6 +50,7 @@ JS_MODULES_PATH = 'modules/media'
EXTRA_JS_MODULES += [
'IdpProxy.jsm',
'PeerConnectionIdp.jsm',
'RTCStatsReport.jsm',
]
if CONFIG['MOZ_B2G']: