You can follow along and play with Fondue in your browser using The Fondue playground.
Fondue is a real-time program tracing library for JavaScript. You use it if you want to:
Get the code with git clone https://github.com/adobe-research/fondue.git or use it in your Node.js project with npm install fondue.
Call require('fondue').instrument(src, options). You pass it the JavaScript code you want to trace as a string, and it returns the same code, but modified so that if you run it, it will store trace data in a global object called __tracer.
Fondue comes with a script bin/fondue that calls instrument(src, { path: ... }) for you automatically. Try saving the following code as example01.js:
// example01.js var nodesHandle = __tracer.trackNodes(); __tracer.newNodes(nodesHandle).forEach(printNode); function printNode(node) { if (node.type === 'function') { console.log('found a function: ' + node.name + ' at ' + node.path + ':' + node.start.line + ' with id ' + node.id); } }
Then execute the following commands:
It should print out something like this:
found a function: [log] at [built-in]:0 with id log found a function: printNode at /Users/tom/src/example01.js:6 with id /Users/tom/src/example01.js-function-6-0-10-1
(log is a special function representing console.log. You can ignore it in your scripts if you want.)
If you add more functions to example01.js and re-run the commands, you should see that it prints out all the functions you add.
You can find basically the same example in the Fondue playground. Just hit "Run". You might find it easier to experiment there than on the command-line.
__tracer uses a polling data model. As you saw in the example, to get all nodes, first you call trackNodes() to get a handle object, then you call newNodes(handle) to get all the nodes that have been instrumented since the last call to newNodes.
As another example, trackHits() gives you a handle for tracking the hit counts of all functions. hitCountDeltas(handle) gives you an object containing the counts of all function calls that occurred since the last call to hitCountDeltas(handle) for that handle:
var hitsHandle = __tracer.trackHits(); var hits1 = __tracer.hitCountDeltas(hitsHandle); // { 'func-id-1' : 1, 'func-id-2' : 3 } var hits2 = __tracer.hitCountDeltas(hitsHandle); // {} var hits3 = __tracer.hitCountDeltas(hitsHandle); // {} var hits4 = __tracer.hitCountDeltas(hitsHandle); // { 'func-id-2' : 1 } var hits5 = __tracer.hitCountDeltas(hitsHandle); // {} // ...
I apologize for inconsistencies. The only consumer of this API until recently was my own project, Theseus.
require('fondue').instrument(src, options) fondue.instrument(src, options) |
Use the first version in Node.js (after npm install fondue) and the second version if you include fondue.browser.js on a web page, which creates a global fondue object. These functions take JavaScript source code in the src argument and return a string containing an instrumented version of that source code. options is an object (or null) with any the following keys:
|
---|
Fondue-instrumented code creates a global __tracer object that you can also access to get information about that code's execution.
__tracer.version() | Returns a string like "0.6.1" with the version of Fondue being used. | ||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
handle = __tracer.trackNodes() nodes = __tracer.newNodes(handle) |
Get all known trace points in the program (functions, function call sites, etc).
|
||||||||||||||||||||||||||||||||||||||||||||
handle = __tracer.trackHits() hits = __tracer.hitCountDeltas(handle) |
Get a map from node id to the number of times the node was executed since the last call to hitCountDeltas with the given handle. Only the ids of functions that actually executed will be present. So, if no code has been executed, hitCountDeltas will return an empty object. Example return value of hitCountDeltas: { 'func-id-1' : 1, 'func-id-2' : 3 }. |
||||||||||||||||||||||||||||||||||||||||||||
handle = __tracer.trackExceptions() hits = __tracer.newExceptions(handle) |
Like trackHits and hitCountDeltas, except it only includes counts for nodes that threw an exception. The return value is also slightly different. Example return value of newExceptions: { counts: { 'func-id-1' : 1, 'func-id-2' : 3 }}. |
||||||||||||||||||||||||||||||||||||||||||||
handle = __tracer.trackLogs(query) invocations = __tracer.logDelta(handle, maxResults) |
invocations will be a chronological array of invocations that match the given query. For example, if your query matches one function, then the results will be a list of all the times that function was called, including the values of the arguments. If your query matches multiple functions, you'll get a list of the times that either function was called. query is an object describing which invocations you want to be returned. You will get a union of all matched invocations. query accepts the following keys:
maxResults is the number of invocations to return. It's optional, and if you don't provide it, a default number is used. Each object in the invocations array has the following keys:
|
||||||||||||||||||||||||||||||||||||||||||||
handle = __tracer.trackEpochs() something = __tracer.epochDelta(handle) |
Insert documentation here. For now, try it out in the Fondue playground. E-mail me to bug me about writing this section. | ||||||||||||||||||||||||||||||||||||||||||||
handle = __tracer.trackFileCallGraph() something = __tracer.fileCallGraphDelta(handle) |
Insert documentation here. For now, try it out in the Fondue playground. E-mail me to bug me about writing this section. |
Instrumented code runs in | Visualization code runs in | Strategy |
---|---|---|
Browser | Browser | Pre-processed. Pre-process the code with bin/fondue (as in the example above) or with require('fondue').instrument(src, options) in Node.js, then execute the output on a web page with a <script> tag. That creates a global __tracer on which you can call the above methods directly from the same page. The "script that inspects itself" example on this page works this way. |
Node.js | Node.js | Pre-processed. Same as with the Browser/Browser case above. eval() code that has been run through the instrument() function and check the global __tracer object. |
Browser | Browser | On-the-fly, browser-side. Process the code with fondue.instrument(src, options) on a web page itself after including fondue.browser.js, the browser build of Fondue. Execute the output with eval(src). That also creates a global __tracer on which you can call the above methods directly. The Fondue playground works this way. |
Browser | Browser | On-the-fly, server-side. Drop fondue-middleware into a Node.js server. When it detects that you're serving a JavaScript file, or an HTML file with embedded <script> tags, it will automatically process the scripts with Fondue. When that code executes on the web page, that creates a global __tracer object on which you can call the above methods directly. |
Node.js | Browser | Remote Node.js instrumentation. Run the Node.js app with node-theseus instead of node (first, npm install -g node-theseus), which starts a WebSocket server that exposes the __tracer object via a simple RPC API. Here's an example project that displays information about a running Node.js process with d3. |