1/* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5"use strict"; 6 7const { XPCOMUtils } = ChromeUtils.import( 8 "resource://gre/modules/XPCOMUtils.jsm" 9); 10 11ChromeUtils.defineModuleGetter( 12 this, 13 "Preferences", 14 "resource://gre/modules/Preferences.jsm" 15); 16ChromeUtils.defineModuleGetter(this, "Log", "resource://gre/modules/Log.jsm"); 17ChromeUtils.defineModuleGetter( 18 this, 19 "TelemetryController", 20 "resource://gre/modules/TelemetryController.jsm" 21); 22ChromeUtils.defineModuleGetter( 23 this, 24 "AppConstants", 25 "resource://gre/modules/AppConstants.jsm" 26); 27ChromeUtils.defineModuleGetter( 28 this, 29 "Services", 30 "resource://gre/modules/Services.jsm" 31); 32 33XPCOMUtils.defineLazyServiceGetter( 34 this, 35 "gUpdateTimerManager", 36 "@mozilla.org/updates/timer-manager;1", 37 "nsIUpdateTimerManager" 38); 39 40var EXPORTED_SYMBOLS = ["TelemetryModules"]; 41 42const LOGGER_NAME = "Toolkit.Telemetry"; 43const LOGGER_PREFIX = "TelemetryModules::"; 44 45// The default is 1 week. 46const MODULES_PING_INTERVAL_SECONDS = 7 * 24 * 60 * 60; 47const MODULES_PING_INTERVAL_PREFERENCE = 48 "toolkit.telemetry.modulesPing.interval"; 49 50const MAX_MODULES_NUM = 512; 51const MAX_NAME_LENGTH = 64; 52const TRUNCATION_DELIMITER = "\u2026"; 53 54var TelemetryModules = Object.freeze({ 55 _log: Log.repository.getLoggerWithMessagePrefix(LOGGER_NAME, LOGGER_PREFIX), 56 57 start() { 58 // The list of loaded modules is obtainable only when the profiler is enabled. 59 // If it isn't, we don't want to send the ping at all. 60 if (!AppConstants.MOZ_GECKO_PROFILER) { 61 return; 62 } 63 64 // Use nsIUpdateTimerManager for a long-duration timer that survives across sessions. 65 let interval = Preferences.get( 66 MODULES_PING_INTERVAL_PREFERENCE, 67 MODULES_PING_INTERVAL_SECONDS 68 ); 69 gUpdateTimerManager.registerTimer( 70 "telemetry_modules_ping", 71 this, 72 interval, 73 interval != 0 // only skip the first interval if the interval is non-0 74 ); 75 }, 76 77 /** 78 * Called when the 'telemetry_modules_ping' timer fires. 79 */ 80 notify() { 81 try { 82 Services.telemetry.getLoadedModules().then( 83 modules => { 84 modules = modules.filter(module => !!module.name.length); 85 86 // Cut the list of modules to MAX_MODULES_NUM entries. 87 if (modules.length > MAX_MODULES_NUM) { 88 modules = modules.slice(0, MAX_MODULES_NUM); 89 } 90 91 // Cut the file names of the modules to MAX_NAME_LENGTH characters. 92 for (let module of modules) { 93 if (module.name.length > MAX_NAME_LENGTH) { 94 module.name = 95 module.name.substr(0, MAX_NAME_LENGTH - 1) + 96 TRUNCATION_DELIMITER; 97 } 98 99 if ( 100 module.debugName !== null && 101 module.debugName.length > MAX_NAME_LENGTH 102 ) { 103 module.debugName = 104 module.debugName.substr(0, MAX_NAME_LENGTH - 1) + 105 TRUNCATION_DELIMITER; 106 } 107 108 if ( 109 module.certSubject !== undefined && 110 module.certSubject.length > MAX_NAME_LENGTH 111 ) { 112 module.certSubject = 113 module.certSubject.substr(0, MAX_NAME_LENGTH - 1) + 114 TRUNCATION_DELIMITER; 115 } 116 } 117 118 TelemetryController.submitExternalPing( 119 "modules", 120 { 121 version: 1, 122 modules, 123 }, 124 { 125 addClientId: true, 126 addEnvironment: true, 127 } 128 ); 129 }, 130 err => this._log.error("notify - promise failed", err) 131 ); 132 } catch (ex) { 133 this._log.error("notify - caught exception", ex); 134 } 135 }, 136}); 137