Javascript Profiling: Difference between revisions

From MozillaWiki
Jump to navigation Jump to search
No edit summary
No edit summary
Line 28: Line 28:
</dl>
</dl>


Possibly have attributes or methods to enable/disable profiling specific attributes? (custom/memory/time/etc.)


== The Profiler.Frame object ==
== The Profiler.Frame object ==
Line 127: Line 126:
* Create a generic <tt>Profiler.Statistics</tt> which is an aggregator for stats, kinda like http://sourceware.org/systemtap/langref/Statistics_aggregates.html where you call <tt>stats.insert(4)</tt> and it would automatically track min, max, average, count, etc.
* Create a generic <tt>Profiler.Statistics</tt> which is an aggregator for stats, kinda like http://sourceware.org/systemtap/langref/Statistics_aggregates.html where you call <tt>stats.insert(4)</tt> and it would automatically track min, max, average, count, etc.
* Throw away all data more than N seconds old because it's probably not interesting and wasting memory now
* Throw away all data more than N seconds old because it's probably not interesting and wasting memory now
* Possibly have attributes or methods to enable/disable profiling specific attributes? (memory/time/network/etc.)

Revision as of 23:47, 31 May 2012

Profiling API

The goal of the profiling API is to allow javascript access to profiling information and enable building tools on top of it to display the information in a readable and useful fashion. It should also be flexible and generalized to support different forms of profiling and even custom profiling techniques.

As an idea of what should be possible to build on top of this API, see Unity's profiler,

The Profiler object

There will be a global Profiler object available to instantiate to begin profiling other objects. The Profiler object has the following methods:

new Profiler()
The constructor takes no arguments
startProfiling(context)
Starts profiling the specified context. If the context is currently being profiled this instance, then an error is thrown.
isProfiling(context)
Returns whether the specified context is being profiled or not by this instance.
frame()
Fetch the current Profiler.Frame instance representing the current stack trace of invocation for the invoking context. Properties can be added to this object and will be persisted across invocations of this method so long as each invocation has the same backtrace. If the invoking context is not being profiled by the Profiler instance, then an error is thrown
info(context)
Returns an array of Profiler.Frame instances. Each instance is present for some internal event having been traced at that stack location, or the frame() method was called at that location
clearInfo(context)
Removes all known frame instances for the given context. Throws an error if the context is not being profiled
stopProfiling(context)
Ceases profiling the specified context. Throws an error if the context is not being profiled.


The Profiler.Frame object

A Profiler.Frame object represents an event at a particular stack trace. An instance cannot be directly instantiated, but rather it is created through the profiler's frame() method.

stack
This attribute is a list of hashes with two keys: file and line. Each element in the array is the caller of the previous element. All Profiler.Frame instances are equivalent based on their stack traces. Some frames may be filtered out based on the permissions of the viewing context.

Profiled Statistics

Some internal events are profiled when profiling is turned on, and they are kept track of in the following fields of the corresponding Profiler.Frame instance.

runtime
This is an integer value of the amount of milliseconds that the corresponding stack trace was seen running for
allocs
This is a two-element hash. The total key represents the total amount of allocations that occurred at this trace, and the amt key is the number of times that allocation was triggered at this location

Other statistics can be easily added from either JS or C++.

Use Cases

Here's some example use cases for when profiling is wanted and how they would use the API provided above.

Test Cases

The test suite would initially create a Profiler object and then start/stop profiling the current page on each call to startProfiling/stopProfiling and the information returned from info would be what is displayed. Between each test, the clearInfo method would be used to prevent pollution between runs.

Extension

Opened on a page and creates its own Profiler object. It then profiles the current page via startProfiling (probably a button to be hit). Some time later info is used to get information about the run of profiling (again probably a button being hit) and then all of the information is sorted through and displayed in some fancy fashion.

The page being profiled is unaware that it's being profiled, and there's nothing that it needs to do to help it along.

Implementation

The goals of the implementation of this profiling API are:

  • Works on all platforms
  • Works with IonMonkey, JaegerMonkey, and the interpreter
  • Virtually no overhead when profiling is turned off
  • Very little overhead when profiling is turned on

Time Profiling

Timing information is crucial to gather, and also the most difficult. All other events can most likely be lumped into the category below. The two methods of getting timing information are sampling and instrumentation:

Instrumentation

Using instrumentation it is very easy to walk the stack because corruption is not a problem, and there is existing methods to do this. There is also no problems with locks, allocations, or whatnot. Some decisions which would need to be made:

  • Only sample every Nth function call? (technique similar to https://bugzilla.mozilla.org/show_bug.cgi?id=652535)
  • Worry about the stack trace? Is it useful to know that you spent X% of time in in function foo, or Y% of time in foo called from bar and Z% of time in foo called from baz?
  • Where is the timing information actually stored? (probably part of question above)

Pros

  • Easy idea, simple (no signal handling/validation business)
  • Can achieve fine-tuned results of both timing and lots of other information if necessary.

Cons

  • Can bias timing data due to overhead
  • Starting/stopping profiling affects code generation, which can be expensive. Might not be so great for the test profiling use case described above.
  • More overhead than sampling (instrumentation is per-function)
  • Where to store timing information could get tricky and having a very quick recorder could get fun depending on what degree of context is desired.

Sampling

Sampling is possible in two flavors. One is via just a raw signal handler, and the other is as an "operation callback." The major drawback of using a raw signal handler is that the state of the interrupted world could be corrupt, and everything inspected must be heavily validated. Additionally, the current context is unknown and difficult to get, so stack traversal also gets fairly hairy. On the other hand, an operation callback looks as if it will solve these problems because it is passed the current context and is only called at a "good time." This way it won't require extra validation.

Pros

  • Statistically accurate (not biased against frequently run functions)
  • starting and stopping profiling is as easy as turning on/off the callback.
  • Stack trace context is free because it has to be inspected anyway and a relatively expensive operation isn't run that often

Cons

  • Might still require some wonky code maybe? Not quite sure about this...

Choice

Ideally a hybrid solution of both sampling and instrumentation would be used, but for a first iteration only one should be implemented. So long as an operation callback is cheap and easy to implement, then sampling looks like the way to go.

Generalized Events

There will be a global "stack list" which keeps track of all stack traces which have had events on them. When an event fires, the current stack is looked up in this list, and if found, the event is added to the stack. If not found, the stack trace is serialized and added to the list.

There will be a way to instrument events from C++ as well to track things like GC triggers, GC allocations, type inference failures, etc.

Extensions

  • Create a generic Profiler.Statistics which is an aggregator for stats, kinda like http://sourceware.org/systemtap/langref/Statistics_aggregates.html where you call stats.insert(4) and it would automatically track min, max, average, count, etc.
  • Throw away all data more than N seconds old because it's probably not interesting and wasting memory now
  • Possibly have attributes or methods to enable/disable profiling specific attributes? (memory/time/network/etc.)