Skip to content

Instantly share code, notes, and snippets.

@d3noob
Last active January 13, 2022 22:59
Show Gist options
  • Save d3noob/9692795 to your computer and use it in GitHub Desktop.
Save d3noob/9692795 to your computer and use it in GitHub Desktop.
Interactive Linux process tree using d3.js
license: mit

This is a chart as used as an example to demonstrate the relationships between Linux processes as a tree using d3.js.

The nodes can be collapsed (since they tend to get crowded) by clicking on them and they include a tool tip feature to provide additional information about each process. It is used as an example and described in the book D3 Tips and Tricks.

The csv file used here was taken by running the command...

ps -eo pid,ppid,pcpu,size,comm,ruser,s

...and converting the resultant output to a csv file. I manually added the 'start' line (0, ,0,0,start,nul,u) to include the root node and removed the '%' sign from the '%CPU' label to reduce chance of errors. In theory this process of formatting the data file could be automated (indeed, there may be a much better way to gather and include it!).

While I have included a tool tip feature for displaying more data from each node, ultimately, I would have grand visions of this being more useful by encoding the node/link colour according to CPU usage or memory use.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Linux Process Tree</title>
<style>
div.tooltip {
position: absolute;
text-align: left;
width: 180px;
height: 80px;
padding: 2px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 3px;
}
.node text { font: 12px sans-serif; }
.link {
fill: none;
stroke: #ccc;
stroke-width: 2px;
}
</style>
</head>
<body>
<!-- load the d3.js library -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<script>
// ************** Generate the tree diagram *****************
var margin = {top: 20, right: 120, bottom: 20, left: 120},
width = 1200 - margin.right - margin.left,
height = 900 - margin.top - margin.bottom;
var i = 0;
duration = 750;
var tree = d3.layout.tree()
.size([height, width]);
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });
var svg = d3.select("body").append("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," +
margin.top + ")");
// load the external data
d3.csv("ps.csv", function(error, data) {
// *********** Convert flat data into a nice tree ***************
// create a name: node map
var dataMap = data.reduce(function(map, node) {
map[node.name] = node;
return map;
}, {});
// create the tree array
var treeData = [];
data.forEach(function(node) {
// add to parent
var parent = dataMap[node.parent];
if (parent) {
// create child array if it doesn't exist
(parent.children || (parent.children = []))
// add node to child array
.push(node);
} else {
// parent is null or missing
treeData.push(node);
}
});
root = treeData[0];
root.x0 = height / 2;
root.y0 = 0;
update(root);
});
d3.select(self.frameElement).style("height", "500px");
function update(source) {
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse(),
links = tree.links(nodes);
// Normalize for fixed-depth.
nodes.forEach(function(d) { d.y = d.depth * 180; });
// Update the nodes…
var node = svg.selectAll("g.node")
.data(nodes, function(d) { return d.id || (d.id = ++i); });
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) {
return "translate(" + source.y0 + "," + source.x0 + ")"; })
.on("click", click)
// add tool tip for ps -eo pid,ppid,pcpu,size,comm,ruser,s
.on("mouseover", function(d) {
div.transition()
.duration(200)
.style("opacity", .9);
div .html(
"PID: " + d.name + "<br/>" +
"Command: " + d.COMMAND + "<br/>" +
"User: " + d.RUSER + "<br/>" +
"%CPU: " + d.CPU + "<br/>" +
"Memory: " + d.SIZE
)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
nodeEnter.append("circle")
.attr("r", 1e-6)
.style("fill", function(d) {
return d._children ? "lightsteelblue" : "#fff"; });
nodeEnter.append("text")
.attr("x", function(d) {
return d.children || d._children ? -13 : 13; })
.attr("dy", ".35em")
.attr("text-anchor", function(d) {
return d.children || d._children ? "end" : "start"; })
.text(function(d) { return d.COMMAND; })
.style("fill-opacity", 1e-6);
// add the tool tip
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + d.y + "," + d.x + ")";
});
nodeUpdate.select("circle")
.attr("r", 10)
.style("fill", function(d) {
return d._children ? "lightsteelblue" : "#fff"; });
nodeUpdate.select("text")
.style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + source.y +
"," + source.x + ")"; })
.remove();
nodeExit.select("circle")
.attr("r", 1e-6);
nodeExit.select("text")
.style("fill-opacity", 1e-6);
// Update the links…
var link = svg.selectAll("path.link")
.data(links, function(d) { return d.target.id; });
// Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {x: source.x0, y: source.y0};
return diagonal({source: o, target: o});
});
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {x: source.x, y: source.y};
return diagonal({source: o, target: o});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
// Toggle children on click.
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
</script>
</body>
</html>
name parent CPU SIZE COMMAND RUSER S
0 0 0 start nul u
1 0 0.0 1140 init root S
2 0 0.0 0 kthreadd root S
3 2 0.0 0 ksoftirqd/0 root S
5 2 0.0 0 kworker/0:0H root S
6 2 0.0 0 kworker/u2:0 root S
7 2 0.0 0 migration/0 root S
8 2 0.0 0 rcu_bh root S
9 2 0.0 0 rcuob/0 root S
10 2 0.0 0 rcu_sched root S
11 2 0.0 0 rcuos/0 root S
12 2 0.0 0 watchdog/0 root S
13 2 0.0 0 khelper root S
14 2 0.0 0 kdevtmpfs root S
15 2 0.0 0 netns root S
16 2 0.0 0 writeback root S
17 2 0.0 0 kintegrityd root S
18 2 0.0 0 bioset root S
19 2 0.0 0 kworker/u3:0 root S
20 2 0.0 0 kblockd root S
21 2 0.0 0 ata_sff root S
22 2 0.0 0 khubd root S
23 2 0.0 0 md root S
24 2 0.0 0 devfreq_wq root S
25 2 0.0 0 kworker/0:1 root S
26 2 0.0 0 khungtaskd root S
27 2 0.0 0 kswapd0 root S
28 2 0.0 0 ksmd root S
29 2 0.0 0 khugepaged root S
30 2 0.0 0 fsnotify_mark root S
31 2 0.0 0 ecryptfs-kthrea root S
32 2 0.0 0 crypto root S
44 2 0.0 0 kthrotld root S
45 2 0.0 0 kworker/u2:1 root S
46 2 0.0 0 scsi_eh_0 root S
47 2 0.0 0 scsi_eh_1 root S
49 2 0.0 0 dm_bufio_cache root S
69 2 0.0 0 deferwq root S
70 2 0.0 0 charger_manager root S
209 2 0.0 0 scsi_eh_2 root S
225 2 0.0 0 jbd2/sda1-8 root S
226 2 0.0 0 ext4-rsv-conver root S
227 2 0.0 0 ext4-unrsv-conv root S
318 1 0.0 340 upstart-udev-br root S
322 1 0.0 772 udevd root S
464 1 0.0 1520 dbus-daemon 102 S
467 1 0.0 221752 rsyslogd syslog S
494 1 0.0 348 bluetoothd root S
509 1 0.0 484 avahi-daemon avahi S
513 509 0.0 352 avahi-daemon avahi S
527 2 0.0 0 krfcommd root S
532 1 0.0 996 cupsd root S
547 2 0.0 0 kworker/u3:1 root S
550 2 0.0 0 kpsmoused root S
605 322 0.0 768 udevd root S
630 322 0.0 768 udevd root S
679 1 0.0 340 upstart-socket- root S
726 1 0.0 720 modem-manager root S
757 1 0.0 148388 NetworkManager root S
781 1 0.0 148616 polkitd root S
814 757 0.0 660 dhclient root S
850 1 0.0 332 getty root S
857 1 0.0 332 getty root S
868 1 0.0 332 getty root S
869 1 0.0 332 getty root S
872 1 0.0 332 getty root S
875 757 0.0 348 dnsmasq nobody S
878 1 0.0 452 acpid root S
879 1 0.0 344 cron root S
880 1 0.0 328 atd root S
914 1 0.0 74760 whoopsie whoopsie S
965 1 0.0 221816 lightdm root S
1029 965 0.9 60716 Xorg root S
1190 965 0.0 74728 lightdm root S
1202 1 0.0 74516 accounts-daemon root S
1207 1 0.0 332 getty root S
1231 1 0.0 545536 console-kit-dae root S
1306 1190 0.0 223184 gnome-session ubuntu S
1359 1306 0.0 344 ssh-agent ubuntu S
1362 1 0.0 348 dbus-launch ubuntu S
1363 1 0.0 4472 dbus-daemon ubuntu S
1372 1306 0.0 308512 gnome-settings- ubuntu S
1373 1 0.0 377624 gnome-keyring-d ubuntu S
1384 1 0.0 148324 upowerd root S
1428 1 0.0 384 gvfsd ubuntu S
1438 1 0.0 222092 gvfs-fuse-daemo ubuntu S
1491 1306 0.0 224240 metacity ubuntu S
1493 1 0.0 225952 colord colord S
1505 1 0.0 1448 gconfd-2 ubuntu S
1508 1 0.0 148932 pulseaudio ubuntu S
1510 1 0.0 147808 rtkit-daemon rtkit S
1513 1306 0.0 227208 unity-2d-panel ubuntu S
1514 1306 0.3 657176 unity-2d-shell ubuntu S
1519 1508 0.0 444 gconf-helper ubuntu S
1525 1306 0.0 225224 nm-applet ubuntu S
1526 1306 0.0 149152 polkit-gnome-au ubuntu S
1527 1306 0.0 296700 gnome-fallback- ubuntu S
1530 1 0.0 224220 bamfdaemon ubuntu S
1536 1306 0.1 383184 nautilus ubuntu S
1539 1306 0.0 224300 bluetooth-apple ubuntu S
1566 1 0.0 148336 gvfs-gdu-volume ubuntu S
1569 1 0.0 148164 udisks-daemon root S
1571 1569 0.0 388 udisks-daemon root S
1574 1 0.0 74140 gvfs-afc-volume ubuntu S
1579 1 0.0 912 gvfs-gphoto2-vo ubuntu S
1590 1 0.0 305000 unity-panel-ser ubuntu S
1598 1 0.0 369736 indicator-messa ubuntu S
1599 1 0.0 295824 indicator-datet ubuntu S
1601 1 0.0 231268 indicator-print ubuntu S
1603 1 0.0 295904 indicator-sound ubuntu S
1605 1 0.0 369500 indicator-sessi ubuntu S
1607 1 0.0 221788 indicator-appli ubuntu S
1609 1 0.0 221580 dconf-service ubuntu S
1622 1 0.0 788 gvfsd-trash ubuntu S
1649 1 0.0 492 geoclue-master ubuntu S
1661 1 0.0 222152 ubuntu-geoip-pr ubuntu S
1663 1 0.0 384 gvfsd-burn ubuntu S
1676 1306 0.0 149260 gdu-notificatio ubuntu S
1689 1 0.0 575672 hud-service ubuntu S
1691 1 0.0 297988 unity-applicati ubuntu S
1693 1 0.0 574488 unity-files-dae ubuntu S
1696 1 0.0 441808 unity-lens-vide ubuntu S
1697 1 0.0 304968 unity-music-dae ubuntu S
1731 1 0.0 296040 zeitgeist-daemo ubuntu S
1738 1 0.0 149680 zeitgeist-fts ubuntu S
1740 1 0.0 295732 zeitgeist-datah ubuntu S
1746 1738 0.0 316 cat ubuntu S
1748 1306 0.0 223064 telepathy-indic ubuntu S
1770 1 0.0 222152 mission-control ubuntu S
1775 1 0.0 148528 goa-daemon ubuntu S
1779 1 0.0 238216 unity-musicstor ubuntu S
1797 1 0.0 387796 unity-scope-vid ubuntu S
1813 1306 0.0 149472 gnome-screensav ubuntu S
1836 1 0.0 520 gvfsd-metadata ubuntu S
1838 1306 0.0 223956 update-notifier ubuntu S
1867 1 0.1 300688 gnome-terminal ubuntu S
1876 1867 0.0 324 gnome-pty-helpe ubuntu S
1877 1867 0.0 2504 bash ubuntu S
1933 1306 0.0 221832 deja-dup-monito ubuntu S
1953 1 0.0 221928 gvfsd-network ubuntu S
1962 1 0.0 524 gvfsd-dnssd ubuntu S
1965 1 0.0 222092 gvfsd-smb-brows ubuntu S
1973 1 0.0 222212 gvfsd-smb ubuntu S
1979 1 0.0 222212 gvfsd-smb ubuntu S
1990 1 0.2 466640 gedit ubuntu S
2041 2 0.0 0 kworker/0:0 root S
2069 1877 0.0 664 ps ubuntu R
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment