| 
									
										
										
										
											2018-07-30 23:08:51 +08:00
										 |  |  | /* | 
					
						
							|  |  |  | 	MIT License http://www.opensource.org/licenses/mit-license.php
 | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | "use strict"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-22 19:05:58 +08:00
										 |  |  | const { Tracer } = require("chrome-trace-event"); | 
					
						
							| 
									
										
										
										
											2018-02-14 15:49:15 +08:00
										 |  |  | const validateOptions = require("schema-utils"); | 
					
						
							|  |  |  | const schema = require("../../schemas/plugins/debug/ProfilingPlugin.json"); | 
					
						
							| 
									
										
										
										
											2019-06-11 19:09:42 +08:00
										 |  |  | const { dirname, mkdirpSync } = require("../util/fs"); | 
					
						
							| 
									
										
										
										
											2018-09-20 16:13:55 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** @typedef {import("../../declarations/plugins/debug/ProfilingPlugin").ProfilingPluginOptions} ProfilingPluginOptions */ | 
					
						
							| 
									
										
										
										
											2019-06-11 19:09:42 +08:00
										 |  |  | /** @typedef {import("../util/fs").IntermediateFileSystem} IntermediateFileSystem */ | 
					
						
							| 
									
										
										
										
											2018-09-20 16:13:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | let inspector = undefined; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | try { | 
					
						
							| 
									
										
										
										
											2018-07-31 17:43:07 +08:00
										 |  |  | 	// eslint-disable-next-line node/no-unsupported-features/node-builtins
 | 
					
						
							| 
									
										
										
										
											2018-06-27 19:48:13 +08:00
										 |  |  | 	inspector = require("inspector"); | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | } catch (e) { | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 	console.log("Unable to CPU profile in < node 8.0"); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Profiler { | 
					
						
							|  |  |  | 	constructor(inspector) { | 
					
						
							|  |  |  | 		this.session = undefined; | 
					
						
							|  |  |  | 		this.inspector = inspector; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	hasSession() { | 
					
						
							|  |  |  | 		return this.session !== undefined; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	startProfiling() { | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		if (this.inspector === undefined) { | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 			return Promise.resolve(); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		try { | 
					
						
							|  |  |  | 			this.session = new inspector.Session(); | 
					
						
							|  |  |  | 			this.session.connect(); | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		} catch (_) { | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 			this.session = undefined; | 
					
						
							|  |  |  | 			return Promise.resolve(); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return Promise.all([ | 
					
						
							|  |  |  | 			this.sendCommand("Profiler.setSamplingInterval", { | 
					
						
							|  |  |  | 				interval: 100 | 
					
						
							|  |  |  | 			}), | 
					
						
							|  |  |  | 			this.sendCommand("Profiler.enable"), | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 			this.sendCommand("Profiler.start") | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 		]); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	sendCommand(method, params) { | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		if (this.hasSession()) { | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 			return new Promise((res, rej) => { | 
					
						
							|  |  |  | 				return this.session.post(method, params, (err, params) => { | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 					if (err !== null) { | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 						rej(err); | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						res(params); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			return Promise.resolve(); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	destroy() { | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		if (this.hasSession()) { | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 			this.session.disconnect(); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return Promise.resolve(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	stopProfiling() { | 
					
						
							|  |  |  | 		return this.sendCommand("Profiler.stop"); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-06-06 11:38:48 +08:00
										 |  |  | /** | 
					
						
							| 
									
										
										
										
											2019-07-17 00:25:45 +08:00
										 |  |  |  * an object that wraps Tracer and Profiler with a counter | 
					
						
							| 
									
										
										
										
											2018-06-06 11:38:48 +08:00
										 |  |  |  * @typedef {Object} Trace | 
					
						
							|  |  |  |  * @property {Tracer} trace instance of Tracer | 
					
						
							|  |  |  |  * @property {number} counter Counter | 
					
						
							|  |  |  |  * @property {Profiler} profiler instance of Profiler | 
					
						
							|  |  |  |  * @property {Function} end the end function | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | /** | 
					
						
							| 
									
										
										
										
											2019-06-11 19:09:42 +08:00
										 |  |  |  * @param {IntermediateFileSystem} fs filesystem used for output | 
					
						
							| 
									
										
										
										
											2018-02-14 16:29:29 +08:00
										 |  |  |  * @param {string} outputPath The location where to write the log. | 
					
						
							| 
									
										
										
										
											2018-06-06 11:38:48 +08:00
										 |  |  |  * @returns {Trace} The trace object | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2019-06-11 19:09:42 +08:00
										 |  |  | const createTrace = (fs, outputPath) => { | 
					
						
							| 
									
										
										
										
											2018-03-22 19:05:58 +08:00
										 |  |  | 	const trace = new Tracer({ | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 		noStream: true | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 	const profiler = new Profiler(inspector); | 
					
						
							| 
									
										
										
										
											2018-12-28 23:21:17 +08:00
										 |  |  | 	if (/\/|\\/.test(outputPath)) { | 
					
						
							| 
									
										
										
										
											2019-06-11 19:09:42 +08:00
										 |  |  | 		const dirPath = dirname(fs, outputPath); | 
					
						
							|  |  |  | 		mkdirpSync(fs, dirPath); | 
					
						
							| 
									
										
										
										
											2018-12-28 07:02:34 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-03-06 05:46:45 +08:00
										 |  |  | 	const fsStream = fs.createWriteStream(outputPath); | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	let counter = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-06 05:46:45 +08:00
										 |  |  | 	trace.pipe(fsStream); | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 	// These are critical events that need to be inserted so that tools like
 | 
					
						
							|  |  |  | 	// chrome dev tools can load the profile.
 | 
					
						
							|  |  |  | 	trace.instantEvent({ | 
					
						
							|  |  |  | 		name: "TracingStartedInPage", | 
					
						
							|  |  |  | 		id: ++counter, | 
					
						
							|  |  |  | 		cat: ["disabled-by-default-devtools.timeline"], | 
					
						
							|  |  |  | 		args: { | 
					
						
							|  |  |  | 			data: { | 
					
						
							|  |  |  | 				sessionId: "-1", | 
					
						
							|  |  |  | 				page: "0xfff", | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 				frames: [ | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						frame: "0xfff", | 
					
						
							|  |  |  | 						url: "webpack", | 
					
						
							|  |  |  | 						name: "" | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				] | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	trace.instantEvent({ | 
					
						
							|  |  |  | 		name: "TracingStartedInBrowser", | 
					
						
							|  |  |  | 		id: ++counter, | 
					
						
							|  |  |  | 		cat: ["disabled-by-default-devtools.timeline"], | 
					
						
							|  |  |  | 		args: { | 
					
						
							|  |  |  | 			data: { | 
					
						
							|  |  |  | 				sessionId: "-1" | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return { | 
					
						
							|  |  |  | 		trace, | 
					
						
							|  |  |  | 		counter, | 
					
						
							| 
									
										
										
										
											2018-03-06 05:46:45 +08:00
										 |  |  | 		profiler, | 
					
						
							| 
									
										
										
										
											2018-06-24 02:27:18 +08:00
										 |  |  | 		end: callback => { | 
					
						
							|  |  |  | 			// Wait until the write stream finishes.
 | 
					
						
							| 
									
										
										
										
											2020-06-02 21:51:45 +08:00
										 |  |  | 			fsStream.on("close", () => { | 
					
						
							| 
									
										
										
										
											2018-06-24 02:27:18 +08:00
										 |  |  | 				callback(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 			// Tear down the readable trace stream.
 | 
					
						
							| 
									
										
										
										
											2018-07-05 16:32:53 +08:00
										 |  |  | 			trace.push(null); | 
					
						
							| 
									
										
										
										
											2018-06-24 02:27:18 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 	}; | 
					
						
							| 
									
										
										
										
											2018-06-27 19:48:13 +08:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-30 07:59:53 +08:00
										 |  |  | const pluginName = "ProfilingPlugin"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | class ProfilingPlugin { | 
					
						
							| 
									
										
										
										
											2018-09-20 16:13:55 +08:00
										 |  |  | 	/** | 
					
						
							| 
									
										
										
										
											2019-08-07 21:55:03 +08:00
										 |  |  | 	 * @param {ProfilingPluginOptions=} options options object | 
					
						
							| 
									
										
										
										
											2018-09-20 16:13:55 +08:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2019-08-07 21:55:03 +08:00
										 |  |  | 	constructor(options = {}) { | 
					
						
							|  |  |  | 		validateOptions(schema, options, { | 
					
						
							|  |  |  | 			name: "Profiling Plugin", | 
					
						
							|  |  |  | 			baseDataPath: "options" | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 		this.outputPath = options.outputPath || "events.json"; | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	apply(compiler) { | 
					
						
							| 
									
										
										
										
											2019-06-11 19:09:42 +08:00
										 |  |  | 		const tracer = createTrace( | 
					
						
							|  |  |  | 			compiler.intermediateFileSystem, | 
					
						
							|  |  |  | 			this.outputPath | 
					
						
							|  |  |  | 		); | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 		tracer.profiler.startProfiling(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// Compiler Hooks
 | 
					
						
							|  |  |  | 		Object.keys(compiler.hooks).forEach(hookName => { | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 			compiler.hooks[hookName].intercept( | 
					
						
							|  |  |  | 				makeInterceptorFor("Compiler", tracer)(hookName) | 
					
						
							|  |  |  | 			); | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		Object.keys(compiler.resolverFactory.hooks).forEach(hookName => { | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 			compiler.resolverFactory.hooks[hookName].intercept( | 
					
						
							|  |  |  | 				makeInterceptorFor("Resolver", tracer)(hookName) | 
					
						
							|  |  |  | 			); | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		compiler.hooks.compilation.tap( | 
					
						
							|  |  |  | 			pluginName, | 
					
						
							|  |  |  | 			(compilation, { normalModuleFactory, contextModuleFactory }) => { | 
					
						
							|  |  |  | 				interceptAllHooksFor(compilation, tracer, "Compilation"); | 
					
						
							|  |  |  | 				interceptAllHooksFor( | 
					
						
							|  |  |  | 					normalModuleFactory, | 
					
						
							|  |  |  | 					tracer, | 
					
						
							|  |  |  | 					"Normal Module Factory" | 
					
						
							|  |  |  | 				); | 
					
						
							|  |  |  | 				interceptAllHooksFor( | 
					
						
							|  |  |  | 					contextModuleFactory, | 
					
						
							|  |  |  | 					tracer, | 
					
						
							|  |  |  | 					"Context Module Factory" | 
					
						
							|  |  |  | 				); | 
					
						
							|  |  |  | 				interceptAllParserHooks(normalModuleFactory, tracer); | 
					
						
							| 
									
										
										
										
											2019-10-02 14:54:21 +08:00
										 |  |  | 				interceptAllJavascriptModulesPluginHooks(compilation, tracer); | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		); | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// We need to write out the CPU profile when we are all done.
 | 
					
						
							| 
									
										
										
										
											2018-03-06 05:46:45 +08:00
										 |  |  | 		compiler.hooks.done.tapAsync( | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 			{ | 
					
						
							|  |  |  | 				name: pluginName, | 
					
						
							|  |  |  | 				stage: Infinity | 
					
						
							|  |  |  | 			}, | 
					
						
							| 
									
										
										
										
											2018-03-06 05:46:45 +08:00
										 |  |  | 			(stats, callback) => { | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 				tracer.profiler.stopProfiling().then(parsedResults => { | 
					
						
							|  |  |  | 					if (parsedResults === undefined) { | 
					
						
							|  |  |  | 						tracer.profiler.destroy(); | 
					
						
							|  |  |  | 						tracer.trace.flush(); | 
					
						
							| 
									
										
										
										
											2018-03-08 03:28:14 +08:00
										 |  |  | 						tracer.end(callback); | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 						return; | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 					const cpuStartTime = parsedResults.profile.startTime; | 
					
						
							|  |  |  | 					const cpuEndTime = parsedResults.profile.endTime; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					tracer.trace.completeEvent({ | 
					
						
							|  |  |  | 						name: "TaskQueueManager::ProcessTaskFromWorkQueue", | 
					
						
							|  |  |  | 						id: ++tracer.counter, | 
					
						
							|  |  |  | 						cat: ["toplevel"], | 
					
						
							|  |  |  | 						ts: cpuStartTime, | 
					
						
							|  |  |  | 						args: { | 
					
						
							|  |  |  | 							src_file: "../../ipc/ipc_moji_bootstrap.cc", | 
					
						
							|  |  |  | 							src_func: "Accept" | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 						} | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 					}); | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 					tracer.trace.completeEvent({ | 
					
						
							|  |  |  | 						name: "EvaluateScript", | 
					
						
							|  |  |  | 						id: ++tracer.counter, | 
					
						
							|  |  |  | 						cat: ["devtools.timeline"], | 
					
						
							|  |  |  | 						ts: cpuStartTime, | 
					
						
							|  |  |  | 						dur: cpuEndTime - cpuStartTime, | 
					
						
							|  |  |  | 						args: { | 
					
						
							|  |  |  | 							data: { | 
					
						
							|  |  |  | 								url: "webpack", | 
					
						
							|  |  |  | 								lineNumber: 1, | 
					
						
							|  |  |  | 								columnNumber: 1, | 
					
						
							|  |  |  | 								frame: "0xFFF" | 
					
						
							|  |  |  | 							} | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 						} | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 					}); | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 					tracer.trace.instantEvent({ | 
					
						
							|  |  |  | 						name: "CpuProfile", | 
					
						
							|  |  |  | 						id: ++tracer.counter, | 
					
						
							|  |  |  | 						cat: ["disabled-by-default-devtools.timeline"], | 
					
						
							|  |  |  | 						ts: cpuEndTime, | 
					
						
							|  |  |  | 						args: { | 
					
						
							|  |  |  | 							data: { | 
					
						
							|  |  |  | 								cpuProfile: parsedResults.profile | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					tracer.profiler.destroy(); | 
					
						
							|  |  |  | 					tracer.trace.flush(); | 
					
						
							| 
									
										
										
										
											2018-03-08 03:28:14 +08:00
										 |  |  | 					tracer.end(callback); | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 				}); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		); | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const interceptAllHooksFor = (instance, tracer, logLabel) => { | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 	if (Reflect.has(instance, "hooks")) { | 
					
						
							| 
									
										
										
										
											2018-01-31 05:23:20 +08:00
										 |  |  | 		Object.keys(instance.hooks).forEach(hookName => { | 
					
						
							| 
									
										
										
										
											2020-05-12 18:16:51 +08:00
										 |  |  | 			const hook = instance.hooks[hookName]; | 
					
						
							|  |  |  | 			if (!hook._fakeHook) { | 
					
						
							|  |  |  | 				hook.intercept(makeInterceptorFor(logLabel, tracer)(hookName)); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2018-01-31 05:23:20 +08:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const interceptAllParserHooks = (moduleFactory, tracer) => { | 
					
						
							|  |  |  | 	const moduleTypes = [ | 
					
						
							|  |  |  | 		"javascript/auto", | 
					
						
							|  |  |  | 		"javascript/dynamic", | 
					
						
							|  |  |  | 		"javascript/esm", | 
					
						
							|  |  |  | 		"json", | 
					
						
							| 
									
										
										
										
											2019-07-15 21:03:29 +08:00
										 |  |  | 		"webassembly/async", | 
					
						
							|  |  |  | 		"webassembly/sync" | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 	]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	moduleTypes.forEach(moduleType => { | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		moduleFactory.hooks.parser | 
					
						
							|  |  |  | 			.for(moduleType) | 
					
						
							|  |  |  | 			.tap("ProfilingPlugin", (parser, parserOpts) => { | 
					
						
							|  |  |  | 				interceptAllHooksFor(parser, tracer, "Parser"); | 
					
						
							|  |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 	}); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-10-02 14:54:21 +08:00
										 |  |  | const interceptAllJavascriptModulesPluginHooks = (compilation, tracer) => { | 
					
						
							|  |  |  | 	interceptAllHooksFor( | 
					
						
							|  |  |  | 		{ | 
					
						
							| 
									
										
										
										
											2019-10-11 21:46:57 +08:00
										 |  |  | 			hooks: require("../javascript/JavascriptModulesPlugin").getCompilationHooks( | 
					
						
							| 
									
										
										
										
											2019-10-02 14:54:21 +08:00
										 |  |  | 				compilation | 
					
						
							|  |  |  | 			) | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 		tracer, | 
					
						
							|  |  |  | 		"JavascriptModulesPlugin" | 
					
						
							|  |  |  | 	); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | const makeInterceptorFor = (instance, tracer) => hookName => ({ | 
					
						
							| 
									
										
										
										
											2018-03-20 17:11:30 +08:00
										 |  |  | 	register: ({ name, type, context, fn }) => { | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 		const newFn = makeNewProfiledTapFn(hookName, tracer, { | 
					
						
							|  |  |  | 			name, | 
					
						
							|  |  |  | 			type, | 
					
						
							|  |  |  | 			fn | 
					
						
							|  |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		return { | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 			name, | 
					
						
							|  |  |  | 			type, | 
					
						
							| 
									
										
										
										
											2018-03-20 17:11:30 +08:00
										 |  |  | 			context, | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 			fn: newFn | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 		}; | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-08 20:31:51 +08:00
										 |  |  | // TODO improve typing
 | 
					
						
							|  |  |  | /** @typedef {(...args: TODO[]) => void | Promise<TODO>} PluginFunction */ | 
					
						
							| 
									
										
										
										
											2018-04-12 16:01:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @param {string} hookName Name of the hook to profile. | 
					
						
							| 
									
										
										
										
											2018-06-06 11:38:48 +08:00
										 |  |  |  * @param {Trace} tracer The trace object. | 
					
						
							| 
									
										
										
										
											2018-04-12 00:53:38 +08:00
										 |  |  |  * @param {object} options Options for the profiled fn. | 
					
						
							| 
									
										
										
										
											2018-04-12 17:05:34 +08:00
										 |  |  |  * @param {string} options.name Plugin name | 
					
						
							|  |  |  |  * @param {string} options.type Plugin type (sync | async | promise) | 
					
						
							| 
									
										
										
										
											2018-04-12 16:01:09 +08:00
										 |  |  |  * @param {PluginFunction} options.fn Plugin function | 
					
						
							| 
									
										
										
										
											2018-05-08 20:31:51 +08:00
										 |  |  |  * @returns {PluginFunction} Chainable hooked function. | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-04-12 00:53:38 +08:00
										 |  |  | const makeNewProfiledTapFn = (hookName, tracer, { name, type, fn }) => { | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 	const defaultCategory = ["blink.user_timing"]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 	switch (type) { | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 		case "promise": | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 			return (...args) => { | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 				const id = ++tracer.counter; | 
					
						
							|  |  |  | 				tracer.trace.begin({ | 
					
						
							|  |  |  | 					name, | 
					
						
							|  |  |  | 					id, | 
					
						
							|  |  |  | 					cat: defaultCategory | 
					
						
							|  |  |  | 				}); | 
					
						
							| 
									
										
										
										
											2018-04-12 17:05:34 +08:00
										 |  |  | 				const promise = /** @type {Promise<*>} */ (fn(...args)); | 
					
						
							|  |  |  | 				return promise.then(r => { | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 					tracer.trace.end({ | 
					
						
							|  |  |  | 						name, | 
					
						
							|  |  |  | 						id, | 
					
						
							|  |  |  | 						cat: defaultCategory | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 					return r; | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}; | 
					
						
							|  |  |  | 		case "async": | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 			return (...args) => { | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 				const id = ++tracer.counter; | 
					
						
							|  |  |  | 				tracer.trace.begin({ | 
					
						
							|  |  |  | 					name, | 
					
						
							|  |  |  | 					id, | 
					
						
							|  |  |  | 					cat: defaultCategory | 
					
						
							|  |  |  | 				}); | 
					
						
							| 
									
										
										
										
											2019-08-13 02:39:23 +08:00
										 |  |  | 				const callback = args.pop(); | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 				fn(...args, (...r) => { | 
					
						
							|  |  |  | 					tracer.trace.end({ | 
					
						
							|  |  |  | 						name, | 
					
						
							|  |  |  | 						id, | 
					
						
							|  |  |  | 						cat: defaultCategory | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 					callback(...r); | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			}; | 
					
						
							|  |  |  | 		case "sync": | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 			return (...args) => { | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 				const id = ++tracer.counter; | 
					
						
							| 
									
										
										
										
											2018-02-26 10:38:03 +08:00
										 |  |  | 				// Do not instrument ourself due to the CPU
 | 
					
						
							| 
									
										
										
										
											2018-01-30 07:59:53 +08:00
										 |  |  | 				// profile needing to be the last event in the trace.
 | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 				if (name === pluginName) { | 
					
						
							| 
									
										
										
										
											2018-01-30 07:59:53 +08:00
										 |  |  | 					return fn(...args); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 				tracer.trace.begin({ | 
					
						
							|  |  |  | 					name, | 
					
						
							|  |  |  | 					id, | 
					
						
							|  |  |  | 					cat: defaultCategory | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 				let r; | 
					
						
							|  |  |  | 				try { | 
					
						
							|  |  |  | 					r = fn(...args); | 
					
						
							| 
									
										
										
										
											2018-02-25 09:00:20 +08:00
										 |  |  | 				} catch (error) { | 
					
						
							| 
									
										
										
										
											2017-12-26 11:12:08 +08:00
										 |  |  | 					tracer.trace.end({ | 
					
						
							|  |  |  | 						name, | 
					
						
							|  |  |  | 						id, | 
					
						
							|  |  |  | 						cat: defaultCategory | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 					throw error; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				tracer.trace.end({ | 
					
						
							|  |  |  | 					name, | 
					
						
							|  |  |  | 					id, | 
					
						
							|  |  |  | 					cat: defaultCategory | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 				return r; | 
					
						
							|  |  |  | 			}; | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.exports = ProfilingPlugin; | 
					
						
							|  |  |  | module.exports.Profiler = Profiler; |