/** * https://github.com/google/tracing-framework * Copyright 2013 Google, Inc. All Rights Reserved. * Use of this source code is governed by a BSD-style license that can be * found at https://github.com/google/tracing-framework/blob/master/LICENSE. */ /** * @fileoverview Web Tracing Framework shim. * This file gives compile-time control over the WTF API, allowing it to be * type-checked and extern-free when enabled and completely compiled out when * disabled. * * WTF, when used in release projects, should always be called through this * API. *Never* use the global 'wtf' object directly, as it may not exist or * may change. This file is versioned to prevent such issues. * * This file contains only the tracing-related functions that are exported in * compiled WTF builds. The signatures and descriptions are copied out * verbatim. Any types required to keep the compiler happy when looking at this * file are exposed as either dummy typedefs or mock objects. * * When this file is included in a compiled library all of these methods will * be renamed. By using 'WTF' as a namespace instead of 'wtf' there's no * risk of collision when running uncompiled. * * Original source: https://www.github.com/google/tracing-framework/ * * @author benvanik@google.com (Ben Vanik) */ goog.provide('WTF'); goog.provide('WTF.data.EventFlag'); goog.provide('WTF.io.ByteArray'); goog.provide('WTF.trace'); goog.provide('WTF.trace.Flow'); goog.provide('WTF.trace.Scope'); goog.provide('WTF.trace.TimeRange'); goog.provide('WTF.trace.Zone'); goog.provide('WTF.trace.events'); /** * @define {boolean} True if WTF is enabled. * This should be defined to false in release builds to ensure that WTF is not * compiled in at all. It flips all functions to nullFunction (or some * equivalent) and will allow the compiler to strip them out. */ WTF.ENABLED = true; /** * The API version expected by the shim. * If WTF is present but its {@code wtf.trace.API_VERSION} does not match * this value it will be ignored. This allows code instrumented with older * versions of the API to keep working (without tracing) when a newer version * of the API is present in the page. Since we are a debugging tool no effort is * made to allow differing versions to work together. * @type {number} * @const * @private */ WTF.EXPECTED_API_VERSION_ = 2; /** * Whether WTF is enabled and present in the current global context. * This will only be true if the master enabled flag is true, 'wtf' is in the * global scope, and the version of WTF matches this shim. * If code really wants to see if WTF is present and usable, use this instead of * just {@see #ENABLED}. * @type {boolean} * @const */ WTF.PRESENT = WTF.ENABLED && !!goog.global['wtf'] && (goog.global['wtf']['trace']['API_VERSION'] == WTF.EXPECTED_API_VERSION_); /** * Whether the runtime can provide high-resolution times. * If this is false times are likely in milliseconds and largely useless. * @type {boolean} */ WTF.hasHighResolutionTimes = WTF.PRESENT ? goog.global['wtf']['hasHighResolutionTimes'] : false; /** * Returns the wall time that {@see wtf#now} is relative to. * This is often the page load time. * * @return {number} A time, in ms. */ WTF.timebase = WTF.PRESENT ? goog.global['wtf']['timebase'] : function() { return 0; }; /** * Returns a non-wall time timestamp in milliseconds. * If available this will use a high precision timer. Otherwise it will fall * back to the default browser time. * * The time value is relative to page navigation, not wall time. Only use it for * relative measurements. * * @return {number} A monotonically increasing timer with sub-millisecond * resolution (if supported). */ WTF.now = WTF.PRESENT ? goog.global['wtf']['now'] : function() { return 0; }; /** * @typedef {Array.|Uint8Array} */ WTF.io.ByteArray; /** * @typedef {Object} */ WTF.trace.Zone; /** * @typedef {Object} */ WTF.trace.Scope; /** * @typedef {Object} */ WTF.trace.Flow; /** * @typedef {Object} */ WTF.trace.TimeRange; /** * Event behavior flag bitmask. * Values can be ORed together to indicate different behaviors an event has. * * This is copied from {@code wtf/data/eventflag.js}. * @enum {number} */ WTF.data.EventFlag = { /** * Event is expected to occur at a very high frequency. * High frequency events will be optimized for size more than other event * types. Event arguments may also receive more preprocessing when being * recorded, such as string interning/etc. */ HIGH_FREQUENCY: (1 << 1), /** * Event represents some system event that should not be counted towards user * code. This can include things such as runtime events (GCs/etc) and tracing * framework time (buffer swaps/etc). */ SYSTEM_TIME: (1 << 2), /** * Event represents some internal system event such as flow control events. * If an event has this flag then it will never be shown in the UI and most * parts of the system will ignore it. For some special events they will be * handled at load-time and never even delivered to the database. */ INTERNAL: (1 << 3), /** * Event arguments will be appended to the containing scope's arguments, * overwritting any with the same name. * * If this is combined with the INTERNAL flag then the event is assumed to * be a built-in system append event and will have special handling. */ APPEND_SCOPE_DATA: (1 << 4), /** * Event is a builtin event. * Only events defined by the tracing framework should set this bit. User * events should not have this flag set and may be ignored if they do. */ BUILTIN: (1 << 5), /** * Event arguments will be appended to the given flow's data, overwritting * any with the same name. The first argument must be a flow ID named * 'id' like 'flowId id'. * * If this is combined with the INTERNAL flag then the event is assumed to * be a built-in system append event and will have special handling. */ APPEND_FLOW_DATA: (1 << 6) }; /** * Default zone types. * Any string value is valid, however these are standard ones. * * This is copied from {@code wtf/data/zonetype.js}. * @enum {string} */ WTF.data.ZoneType = { /** * Primary script context. * Usually just user JavaScript scopes. This is the default scope created for * all traces. */ SCRIPT: 'script', /** * Native script context. * Native runtime scopes, such as the C++ calls above the JavaScript. */ NATIVE_SCRIPT: 'native_script', /** * Native GPU thread context. * This is not the GPU itself but instead the thread calling GPU driver * methods. */ NATIVE_GPU: 'native_gpu', /** * Native browser context. * This is the browser thread that usually routes input events and other * global operations. */ NATIVE_BROWSER: 'native_browser' }; /** * Main entry point for the tracing API. * This must be called as soon as possible and preferably before any application * code is executed (or even included on the page). * * This method does not setup a tracing session, but prepares the environment * for one. It can be called many times but the options provided are not updated * once it's been called. * * @param {Object=} opt_options Options overrides. * @return {*} Ignored. */ WTF.trace.prepare = WTF.PRESENT ? goog.global['wtf']['trace']['prepare'] : goog.nullFunction; /** * Shuts down the tracing system. */ WTF.trace.shutdown = WTF.PRESENT ? goog.global['wtf']['trace']['shutdown'] : goog.nullFunction; /** * Starts a new tracing session. * The session mode is determined by the options provided, defaulting to * snapshotting. See {@code wtf.trace.mode} and {@code wtf.trace.target} for * more information. * * {@see WTF.trace#prepare} must have been called prior to calling this * function. * * @param {Object=} opt_options Options overrides. */ WTF.trace.start = WTF.PRESENT ? goog.global['wtf']['trace']['start'] : goog.nullFunction; /** * Takes a snapshot of the current state. * A session must be actively recording. This call is ignored if the session * does not support snapshotting. * @param {*=} opt_targetValue Stream target value. */ WTF.trace.snapshot = WTF.PRESENT ? goog.global['wtf']['trace']['snapshot'] : goog.nullFunction; /** * Asynchronously snapshots all contexts. * This will take a snapshot of the current context as well as any dependent * ones such as servers or worker threads. The results are sent to the callback * when they have all been returned. * If the call is going to be ignored (no active session) or fails the callback * will fire on the next javascript tick with a null value. * * @param {function(this:T, Array.)} callback Function called * when all buffers are available. The value will be null if an error * occurred. * @param {T=} opt_scope Callback scope. * @template T */ WTF.trace.snapshotAll = WTF.PRESENT ? goog.global['wtf']['trace']['snapshotAll'] : goog.nullFunction; /** * Clears all data in the current session by resetting all buffers. * This is only valid in snapshotting sessions. */ WTF.trace.reset = WTF.PRESENT ? goog.global['wtf']['trace']['reset'] : goog.nullFunction; /** * Stops the current session and disposes it. */ WTF.trace.stop = WTF.PRESENT ? goog.global['wtf']['trace']['stop'] : goog.nullFunction; /** * Creates a new execution zone. * Execution zones are used to group regions of code in the trace stream. * For example, one zone may be 'Page' to indicate all page JS and another * 'Worker' to show events from a web worker. * @param {string} name Zone name. * @param {string} type Zone type. This should be one of the * {@see WTF.data.ZoneType} values. * @param {string} location Zone location (such as URI of the script). * @return {WTF.trace.Zone} Zone used for future calls. */ WTF.trace.createZone = WTF.PRESENT ? goog.global['wtf']['trace']['createZone'] : goog.nullFunction; /** * Deletes an execution zone. * The zone ID may be reused. * @param {WTF.trace.Zone} zone Zone returned from {@see #createZone}. */ WTF.trace.deleteZone = WTF.PRESENT ? goog.global['wtf']['trace']['deleteZone'] : goog.nullFunction; /** * Pushes a zone. * @param {WTF.trace.Zone} zone Zone returned from {@see #createZone}. */ WTF.trace.pushZone = WTF.PRESENT ? goog.global['wtf']['trace']['pushZone'] : goog.nullFunction; /** * Pops the active zone. */ WTF.trace.popZone = WTF.PRESENT ? goog.global['wtf']['trace']['popZone'] : goog.nullFunction; /** * This must be matched with a {@see #leaveScope} that takes the return value. * * It is strongly recommended that a custom enter scope event should be used * instead of this, as the overhead required to write the scope name is * non-trivial. Only use this when the name changes many times at runtime or * you're hacking something together. See {@see WTF.trace.events.createScope}. * * Example: * * function myFunction() { * var scope = WTF.trace.enterScope('myFunction'); * var result = ...; * return WTF.trace.leaveScope(scope, result); * } * * * @param {string} name Scope name. * @param {number=} opt_time Time for the enter; omit to use the current time. * @return {WTF.trace.Scope} An initialized scope object. */ WTF.trace.enterScope = WTF.PRESENT ? goog.global['wtf']['trace']['enterScope'] : goog.nullFunction; /** * Enters a tracing implementation overhead scope. * This should only be used by the tracing framework and extension to indicate * time used by non-user tasks. * @param {number=} opt_time Time for the enter; omit to use the current time. * @return {WTF.trace.Scope} An initialized scope object. */ WTF.trace.enterTracingScope = WTF.PRESENT ? goog.global['wtf']['trace']['enterTracingScope'] : goog.nullFunction; /** * Leaves a scope. * @param {WTF.trace.Scope} scope Scope to leave. This is the result of a * previous call to {@see #enterScope} or a custom enter scope function. * @param {T=} opt_result Optional result to chain. * @param {number=} opt_time Time for the leave; omit to use the current time. * @return {T|undefined} The value of the {@code opt_result} parameter. * @template T */ WTF.trace.leaveScope = WTF.PRESENT ? goog.global['wtf']['trace']['leaveScope'] : function(scope, opt_result, opt_time) { return opt_result; }; /** * Appends a named argument of any type to the current scope. * The data added is keyed by name, and existing data with the same name will * be overwritten. * * Repeated calls with the same name and value type will be optimized at * runtime. To ensure predictable performance it's better to use a custom * instance event with the {@see WTF.data.EventFlag#APPEND_SCOPE_DATA} flag set. * * But, in general, you should avoid using this if you can. Appending data * involves additional overhead at runtime and in the file compared to just * passing the arguments to the function. * * No, really, this JSON stringifies whatever is passed to it and will skew * your results. Don't use it. * * Example: * * my.Type.prototype.someMethod = function() { * // This method is traced automatically by traceMethods, but more data * // is needed: * WTF.trace.appendScopeData('bar', 123); * WTF.trace.appendScopeData('foo', { * 'complex': ['data'] * }); * }; * WTF.trace.instrumentType(...my.Type...); * * * @param {string} name Argument name. Must be ASCII. * @param {*} value Value. Will be JSON stringified. If this is a number it * will be converted to an int32. * @param {number=} opt_time Time for the enter; omit to use the current time. */ WTF.trace.appendScopeData = WTF.PRESENT ? goog.global['wtf']['trace']['appendScopeData'] : goog.nullFunction; /** * Branches the flow. * If no parent flow is given then the current scope flow is used. * @param {string} name Flow name. * @param {*=} opt_value Optional data value. * @param {WTF.trace.Flow=} opt_parentFlow Parent flow, if any. * @param {number=} opt_time Time for the branch; omit to use the current time. * @return {!WTF.trace.Flow} An initialized flow object. */ WTF.trace.branchFlow = WTF.PRESENT ? goog.global['wtf']['trace']['branchFlow'] : goog.nullFunction; /** * Extends the flow into the current scope. * @param {WTF.trace.Flow} flow Flow to extend. * @param {string} name Flow stage name. * @param {*=} opt_value Optional data value. * @param {number=} opt_time Time for the extend; omit to use the current time. */ WTF.trace.extendFlow = WTF.PRESENT ? goog.global['wtf']['trace']['extendFlow'] : goog.nullFunction; /** * Terminates a flow. * @param {WTF.trace.Flow} flow Flow to terminate. * @param {*=} opt_value Optional data value. * @param {number=} opt_time Time for the terminate; omit to use the current * time. */ WTF.trace.terminateFlow = WTF.PRESENT ? goog.global['wtf']['trace']['terminateFlow'] : goog.nullFunction; /** * Appends a named argument of any type to the given flow. * This is slow and should only be used for very infrequent appends. * Prefer instead to use a custom instance event with the * {@see WTF.data.EventFlag#APPEND_FLOW_DATA} flag set. * * @param {WTF.trace.Flow} flow Flow to append. * @param {string} name Argument name. Must be ASCII. * @param {*} value Value. Will be JSON stringified. * @param {number=} opt_time Time for the event; omit to use the current time. */ WTF.trace.appendFlowData = WTF.PRESENT ? goog.global['wtf']['trace']['appendFlowData'] : goog.nullFunction; /** * Clears the current scope flow. */ WTF.trace.clearFlow = WTF.PRESENT ? goog.global['wtf']['trace']['clearFlow'] : goog.nullFunction; /** * Spans the flow across processes. * Flows must have been branched before this can be used. * @param {number} flowId Flow ID. * @return {!WTF.trace.Flow} An initialized flow object. */ WTF.trace.spanFlow = WTF.PRESENT ? goog.global['wtf']['trace']['spanFlow'] : goog.nullFunction; /** * Marks the stream with a named bookmark. * This is used by the UI to construct a simple navigation structure. * Each mark is then turned into a navigation point in a table of contents. * This should only be used for modal application state changes, such as * initial load, entry into a modal dialog or mode, etc. There is only ever one * marked range active at a time and if you are calling this more frequently * than 1s you should use something else. * * For high-frequency time stamps instead use {@see #timeStamp} and for async * timers use {@see #beginTimeRange}. * * @param {string} name Marker name. * @param {*=} opt_value Optional data value. * @param {number=} opt_time Time for the mark; omit to use the current time. */ WTF.trace.mark = WTF.PRESENT ? goog.global['wtf']['trace']['mark'] : goog.nullFunction; /** * Adds a timestamped event to the stream. * This is synonymous to {@code console.timeStamp}, and can be used to place * simple arg-less instance events in the timeline. * Prefer using custom events for faster, more flexible events. * @param {string} name Time stamp name. * @param {*=} opt_value Optional data value. * @param {number=} opt_time Time for the stamp; omit to use the current time. */ WTF.trace.timeStamp = WTF.PRESENT ? goog.global['wtf']['trace']['timeStamp'] : goog.nullFunction; /** * Begins an async time range. * This tracks time outside of normal scope flow control, and should be limited * to only those events that span frames or JavaScript ticks. * If you're trying to track call flow instead use {@see #traceMethods}. * * A limited number of active timers will be displayed in the UI. Do not abuse * this feature by adding timers for everything (like network requests). Prefer * to use flows to track complex async operations. * * Example: * * my.Type.startJob = function(actionName) { * var job = {...}; * job.tracingRange = WTF.trace.beginTimeRange('my.Type:job', actionName); * }; * my.Type.endJob = function(job) { * WTF.trace.endTimeRange(job.tracingRange); * }; * * * @param {string} name Time range name. * @param {*=} opt_value Optional data value. * @return {WTF.trace.TimeRange} Time range handle. */ WTF.trace.beginTimeRange = WTF.PRESENT ? goog.global['wtf']['trace']['beginTimeRange'] : goog.nullFunction; /** * Ends an async time range previously started with {@see #beginTimeRange}. * @param {WTF.trace.TimeRange} timeRange Time range handle. * @param {number=} opt_time Time for the stamp; omit to use the current time. */ WTF.trace.endTimeRange = WTF.PRESENT ? goog.global['wtf']['trace']['endTimeRange'] : goog.nullFunction; /** * Marks an event listener as being ignored, meaning that it will not show up * in traces. * This should only be used by debugging code as it will cause weird * gaps in timing data. Alternatively one could use {@see #enterTracingScope} * so that the time is properly shown as inside tracing code. * * Example: * * myElement.onclick = WTF.trace.ignoreListener(function(e) { * // This callback will not be auto-traced. * }); * * * @param {!T} listener Event listener. * @return {!T} The parameter, for chaining. * @template T */ WTF.trace.ignoreListener = WTF.PRESENT ? goog.global['wtf']['trace']['ignoreListener'] : goog.nullFunction; /** * Marks an entire tree of DOM elements as being ignored, meaning that no * events from them will show up in traces. * @param {!Element} el Root DOM element. */ WTF.trace.ignoreDomTree = WTF.PRESENT ? goog.global['wtf']['trace']['ignoreDomTree'] : goog.nullFunction; /** * Initializes on* event properties on the given DOM element and optionally * for all children. * This must be called to ensure the properties work correctly. It can be * called repeatedly on the same elements (but you should avoid that). Try * calling it after any new DOM tree is added recursively on the root of the * tree. * * If this method is not called not all browsers will report events registered * via their on* properties. Events registered with addEventListener will always * be traced. * * @param {!Element} target Target DOM element. * @param {boolean=} opt_recursive Also initialize for all children. */ WTF.trace.initializeDomEventProperties = WTF.PRESENT ? goog.global['wtf']['trace']['initializeDomEventProperties'] : goog.nullFunction; /** * Creates and registers a new event type, returning a function that can be used * to trace the event in the WTF event stream. * Created events should be cached and reused - do *not* redefine events. * * Events are defined by a signature that can be a simple string such as * {@code 'myEvent'} or a reference string like {@code 'namespace.Type#method'} * and can optionally include typed parameters like * {@code 'myEvent(uint32 a, ascii b)'}. * * For more information on this API, see: * https://github.com/google/tracing-framework/blob/master/docs/api.md * * When tracing is disabled {@code goog.nullFunction} will be returned for * all events. * * Example: * * // Create the event once, statically. * my.Type.fooEvent_ = WTF.trace.events.createInstance( * 'my.Type#foo(uint32 a, ascii b)'); * my.Type.prototype.someMethod = function() { * // Trace the event each function call with custom args. * my.Type.fooEvent_(123, 'hello'); * }; * * * @param {string} signature Event signature. * @param {number=} opt_flags A bitmask of {@see WTF.data.EventFlag} values. * @return {Function} New event type. */ WTF.trace.events.createInstance = WTF.PRESENT ? goog.global['wtf']['trace']['events']['createInstance'] : function(signature, opt_flags) { return goog.nullFunction; }; /** * Creates and registers a new event type, returning a function that can be used * to trace the event in the WTF event stream. * Created events should be cached and reused - do *not* redefine events. * * Events are defined by a signature that can be a simple string such as * {@code 'myEvent'} or a reference string like {@code 'namespace.Type#method'} * and can optionally include typed parameters like * {@code 'myEvent(uint32 a, ascii b)'}. * * For more information on this API, see: * https://github.com/google/tracing-framework/blob/master/docs/api.md * * When tracing is disabled {@code goog.nullFunction} will be returned for * all events. * * Example: * * // Create the event once, statically. * my.Type.someMethodEvent_ = WTF.trace.events.createScope( * 'my.Type#foo(uint32 a, ascii b)'); * my.Type.prototype.someMethod = function() { * // Enter and leave each function call with custom args. * var scope = my.Type.someMethodEvent_(123, 'hello'); * var result = 5; // ... * return WTF.trace.leaveScope(scope, result); * }; * * * @param {string} signature Event signature. * @param {number=} opt_flags A bitmask of {@see WTF.data.EventFlag} values. * @return {Function} New event type. */ WTF.trace.events.createScope = WTF.PRESENT ? goog.global['wtf']['trace']['events']['createScope'] : function(signature, opt_flags) { return goog.nullFunction; }; /** * Automatically instruments a method. * This will likely produce code slower than manually instrumenting, but is * much more readable. * * * my.Type.prototype.foo = WTF.trace.instrument(function(a, b) { * return a + b; * }, 'my.Type.foo(uint8 b@1)'); * * * @param {T} value Target function. * @param {string} signature Method signature. * @param {string=} opt_namePrefix String to prepend to the name. * @param {(function(Function, Function):Function)=} opt_generator * A custom function generator that is responsible for taking the given * {@code value} and returning a wrapped function that emits the given * event type. * @param {(function())=} opt_pre Code to execute before the scope is entered. * This is only called if {@code opt_generator} is not provided. * @return {T} The instrumented input value. * @template T */ WTF.trace.instrument = WTF.PRESENT ? goog.global['wtf']['trace']['instrument'] : goog.identityFunction; /** * Automatically instruments an entire type. * * * my.Type = function(a, b) { * goog.base(this); * this.value = a + b; * }; * goog.inherits(my.Type, some.BaseType); * my.Type.prototype.foo = function(a) { return a; }; * my.Type = WTF.trace.instrumentType( * my.Type, 'my.Type(uint8 a, uint8 b)', * goog.reflect.object(my.Type, { * foo: 'foo(uint8 a)' * })); * * * @param {T} value Target type. * @param {string} constructorSignature Type name and constructor signature. * @param {Object|!Object.} methodMap A map of translated method names * to method signatures. Only the methods in this map will be * auto-instrumented. * @return {T} The instrumented input value. * @template T */ WTF.trace.instrumentType = WTF.PRESENT ? goog.global['wtf']['trace']['instrumentType'] : goog.identityFunction; /** * Automatically instruments the given prototype methods. * This is a simple variant of {@see WTF.trace.instrumentType} that does not * provide method arguments or work with overridden methods. * * @param {string} prefix A common prefix to use for all trace labels. * @param {!Object} classPrototype The prototype of the class. * @param {!Object.} methodMap A mapping between method names * and the methods themselves. */ WTF.trace.instrumentTypeSimple = WTF.PRESENT ? goog.global['wtf']['trace']['instrumentTypeSimple'] : goog.nullFunction; /** * @define {boolean} True to replace goog.base with a version that supports * WTF instrumented methods. * * This, when goog.base is present, will enable the replacement of the * goog.base in base.js to support methods instrumented by WTF. Without this, * calls with goog.base will throw exceptions. * * Enabling this should have no side effects in compiled code. */ WTF.REPLACE_GOOG_BASE = true; // Replace goog.base in debug mode. if (!COMPILED && WTF.REPLACE_GOOG_BASE && WTF.PRESENT && goog.global['goog'] && goog.global['goog']['base']) { /** * A variant of {@code goog.base} that supports our method rewriting. * This should only be used in uncompiled builds. * * To use, replace {@code goog.base} with this method as soon as possible. * * @param {!Object} me Should always be "this". * @param {*=} opt_methodName The method name if calling a super method. * @param {...*} var_args The rest of the arguments. * @return {*} The return value of the superclass method. * @suppress {es5Strict} */ goog.global['goog']['base'] = function(me, opt_methodName, var_args) { var caller = arguments.callee.caller; if (caller.superClass_) { // This is a constructor. Call the superclass constructor. return (/** @type {!Function} */ (caller.superClass_)).constructor.apply( me, Array.prototype.slice.call(arguments, 1)); } var args = Array.prototype.slice.call(arguments, 2); var foundCaller = false; for (var ctor = me.constructor; ctor; ctor = ctor.superClass_ && (/** @type {!Function} */ (ctor.superClass_)).constructor) { if (ctor.prototype[opt_methodName] === caller || ctor.prototype[opt_methodName]['uninstrumented'] === caller) { foundCaller = true; } else if (foundCaller) { return ctor.prototype[opt_methodName].apply(me, args); } } // If we did not find the caller in the prototype chain, // then one of two things happened: // 1) The caller is an instance method. // 2) This method was not called by the right caller. if (me[opt_methodName] === caller || me[opt_methodName]['uninstrumented'] === caller) { return me.constructor.prototype[opt_methodName].apply(me, args); } else { throw Error( 'goog.base called from a method of one name ' + 'to a method of a different name'); } }; }