npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

d3-flame-graphs

v3.1.1

Published

D3.js plugin for rendering flame graphs

Downloads

334

Readme

What is this?

This is a d3.js plugin that renders flame graphs from hierarchical data.

Flame graphs are a visualization of profiled software, allowing the most frequent code-paths to be identified quickly and accurately. They can be generated using my open source programs on github.com/brendangregg/FlameGraph, which create interactive SVGs. See the Updates section for other implementations.

-- Flame Graphs, Brendan Gregg

See the demo!

Flame Graph Representation

Build status

Circle CI npm version bower version

Features

  • Efficient rendering of large profiles - large profiles may use up a lot of CPU and memory to render if all samples get represented on the DOM. This plugin only draws samples that would be visible to the user. The performance improvement in the case of very large profiles is in the range of 10x-20x.
  • Zooming - on click, the container re-renders the subgraph associated with the clicked node. The previous roots are rendered at the bottom of the graph and are clickable - you can revert to a previous state. An optional callback can be provided if you want to trigger other changes when zooming.
  • Tooltips - when hovering over nodes a tooltip can be displayed. The tooltip's contents are parametrizable.
  • Filtering - nodes can be selected by name using regex. This enables name-based navigation, highlighting or adding other custom behaviour to certain nodes. See the demo for examples.
  • Hiding across the stack - some call paterns only add noise to a graph (like Object.wait or Unsafe.park, for example). The plugin offers the capability to hide node selections across the stack (their value is subtracted from the parent nodes and their children are hidden). This leads to clearer views of the state of the world.

How was this made?

This plugin was built using gulp and coffeescript. CircleCI runs tests on every push and manages releasing the demo page and the library to npm and bower. The demo page is hosted on GitHub pages.

API Reference

The methods on the flame graph plugin follow the d3.js conventions on method chaining and accessors.

# d3.flameGraph(selector, data)

Constructs a new flame graph.

The selector value is required, it defines the DOM element to which the SVG will be appended. Prior to rendering, any svg elements present in the given container will be cleared.

The data value is also required, it should be the root of the profile under analysis. There is no need to partition the data prior to feeding in to the plugin as partitioning is done internally. Any operation on the data (zooming, selecting, hiding) will use this value as start of traversal.

# flameGraph.size([[width, height]])

If [width, height] are specified, sets the svg rendering width and height to the pixel values provided in the array. If [width, height] is not specified, returns the current width. Defaults to [1200, 600].

# flameGraph.margin([{top: , right: , bottom:, left: }]])

If the values are specified, follows the d3 conventions on margins when rendering the chart. If the values are not specified, returns the current margins object. Defaults to { top: 0, right: 0, bottom: 0, left: 0}.

# flameGraph.cellHeight([cellHeight])

If cellHeight is specified, sets the height of the rectangles in the flame graph to the provided value. If cellHeight is not specified, returns the current value. Defaults to 20. The graph height should be divisible by the cell height so the nodes align properly.

# flameGraph.color([[color(d)]])

If the color function is specified, it will be used when determining the color for a particular node. The function should expect one parameter, the data element associated with the node. If color is not specified, returns the current function.

The default function uses a hash of the node's short name to generate the color. The letters are weighted (first letters matter more), the hash only uses the first six characters of the name.

# flameGraph.data([data])

The data the flame graph is rendered from. It expects nested data in the form:

{
      "name": "<name from which the label is derived>",
      "value": <number representing the sample count of the node>,
      "children": [<child object>, <child object>, ...]
}

The data is augmented with 'filler nodes' by the plugin, due to the fact that D3 considers the value of a node to be the sum of its children rather than its explicit value. More details in this issue. This should be transparent to clients as the filler node augmentation is done internally.

# flameGraph.zoomEnabled(enabled)

If enabled is truthy, zooming will be enabled - clicking a node or calling the zoom method programatically will re-render the graph with that node as root. The default value is true.

# flameGraph.zoomAction(function)

If a function is provided, on every zoom - clicking a node or calling the zoom method programatically - the function will be called after the graph is re-rendered. The function receives a data node as its parameter and its return value is ignored.

# flameGraph.zoom(node)

If zoom is enabled, re-renders the graph with the given node as root. The previous roots are drawn at the bottom of the graph, by clicking on it them you can revert back to previous states. Prior to zooming, any svg elements present in the given container will be cleared.

If zoom is disabled, this method will throw an error.

See the demo code for an example.

# flameGraph.tooltip(function)

If a function is provided, a tooltip will be shown on mouseover for each cell. Ancestor nodes do not get a tooltip. The function receives a data node as its parameter and needs to return an HTML string that will be rendered inside the tooltip. The d3-tip plugin is responsible for rendering the tooltip. If set to false or not called, the tooltip is disabled and nothing is rendered on mouseover.

# flameGraph.select(predicate, [isDisplayed])

Selects the elements from the current dataset which match the given predicate function. If isDisplayed is set to false, it will search all the nodes starting from the root passed to the flame graph constructor and return an array of data nodes. isDisplayed defaults to true, in that case it will only search the currently displayed elements and returns a d3 selection of DOM elements.

The demo code contains a usage example.

# flameGraph.hide(predicate, [unhide])

Hides elements that match the given predicate function from the current dataset. The targeted elements and their children are hidden. The value of the target is subtracted from its ancestors so that it's effect is removed across the stack.

If unhide is set to true, it will perform the reverse operation and re-add the previously subtracted values. unhide defaults to false.

As this operation needs to traverse the subtree for all matched items, it can potentially be slow on generic queries over large datasets.

The demo code contains a usage example.

# flameGraph.render()

Triggers a repaint of the flame graph, using the values previously fed in as parameters. This is the only method besides zoom and hide that triggers repaint so you need to call it after changing other parameters like size or cell-height in order to see the changes take effect.

Sample usage:

The example below is taken from the demo source. Although it is written in CoffeeScript, the plugin can be used from vanilla JS without any issues.

d3.json "data/profile.json", (err, data) ->
  profile = convert(data.profile)
  tooltip = (d) -> "#{d.name} <br /><br />
    #{d.value} samples<br />
    #{((d.value / profile.value) * 100).toFixed(2)}% of total"
    flameGraph = d3.flameGraph('#d3-flame-graph', profile)
      .size([1200, 600])
      .cellHeight(20)
      .zoomEnabled(true)
      .zoomAction((d) -> console.log(d))
      .tooltip(tooltip)
      .render()

  d3.select('#highlight')
    .on 'click', () ->
      nodes = flameGraph.select((d) -> /java\.util.*/.test(d.name))
      nodes.classed("highlight", (d, i) -> not d3.select(@).classed("highlight"))

  d3.select('#zoom')
    .on 'click', () ->
      # jump to the first java.util.concurrent method we can find
      node = flameGraph.select(((d) -> /CountDownLatch\.await$/.test(d.name)), false)[0]
      flameGraph.zoom(node)

  unhide = false
  d3.select('#hide')
    .on 'click', () ->
      flameGraph.hide ((d) -> /Unsafe\.park$/.test(d.name) or /Object\.wait$/.test(d.name)), unhide
      unhide = !unhide