ChildCrashHandler.sys.mjs (3635B)
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 file, 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 import { GeckoViewUtils } from "resource://gre/modules/GeckoViewUtils.sys.mjs"; 6 import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs"; 7 8 const lazy = {}; 9 10 ChromeUtils.defineESModuleGetters(lazy, { 11 EventDispatcher: "resource://gre/modules/Messaging.sys.mjs", 12 }); 13 14 const { debug, warn } = GeckoViewUtils.initLogging("ChildCrashHandler"); 15 16 function getDir(name) { 17 const uAppDataPath = Services.dirsvc.get("UAppData", Ci.nsIFile).path; 18 return PathUtils.join(uAppDataPath, "Crash Reports", name); 19 } 20 21 var getPendingDir = function () { 22 return getDir("pending"); 23 }; 24 25 var getPendingMinidump = function (id) { 26 const pendingDir = getPendingDir(); 27 28 return [".dmp", ".extra"].map(suffix => { 29 return PathUtils.join(pendingDir, `${id}${suffix}`); 30 }); 31 }; 32 33 export var crashPullCallback = function (matches, requestedByDevs) { 34 lazy.EventDispatcher.instance.sendRequest({ 35 type: "GeckoView:RemoteSettingsCrashPull", 36 crashIDs: matches.map(id => PathUtils.join(getPendingDir(), `${id}.dmp`)), 37 }); 38 }; 39 40 export var ChildCrashHandler = { 41 // Map a child ID to a remote type. 42 childMap: new Map(), 43 44 // The event listener for this is hooked up in GeckoViewStartup.sys.mjs 45 observe(aSubject, aTopic, aData) { 46 const childID = aData; 47 48 switch (aTopic) { 49 case "process-type-set": 50 // Intentional fall-through 51 case "ipc:content-created": { 52 const pp = aSubject.QueryInterface(Ci.nsIDOMProcessParent); 53 this.childMap.set(childID, pp.remoteType); 54 break; 55 } 56 57 case "ipc:content-shutdown": 58 // Intentional fall-through 59 case "compositor:process-aborted": { 60 aSubject.QueryInterface(Ci.nsIPropertyBag2); 61 62 const disableReporting = Services.env.get( 63 "MOZ_CRASHREPORTER_NO_REPORT" 64 ); 65 66 if ( 67 !aSubject.get("abnormal") || 68 !AppConstants.MOZ_CRASHREPORTER || 69 disableReporting 70 ) { 71 return; 72 } 73 74 // If dumpID is empty the process was likely killed by the system and 75 // we therefore do not want to report the crash. This includes most 76 // "expected" extensions process crashes on Android. 77 const dumpID = aSubject.get("dumpID"); 78 if (!dumpID) { 79 return; 80 } 81 82 debug`Notifying child process crash, dump ID ${dumpID}`; 83 const [minidumpPath, extrasPath] = getPendingMinidump(dumpID); 84 85 let remoteType = this.childMap.get(childID); 86 this.childMap.delete(childID); 87 88 if (remoteType?.length) { 89 // Only send the remote type prefix since everything after a "=" is 90 // dynamic, and used to control the process pool to use. 91 remoteType = remoteType.split("=")[0]; 92 } 93 94 // Report GPU and extension process crashes as occuring in a background 95 // process, and others as foreground. 96 const processVisibility = 97 aTopic === "compositor:process-aborted" || remoteType === "extension" 98 ? "BACKGROUND_CHILD" 99 : "FOREGROUND_CHILD"; 100 101 const processType = aSubject.get("processType"); 102 103 lazy.EventDispatcher.instance.sendRequest({ 104 type: "GeckoView:ChildCrashReport", 105 minidumpPath, 106 extrasPath, 107 success: true, 108 fatal: false, 109 processVisibility, 110 processType, 111 remoteType, 112 }); 113 114 break; 115 } 116 } 117 }, 118 };