Permalink
Browse files

subsecond offset spectrogram

1 parent d0eeac1 commit 2f21604dce0b561407accb9dba869aa19c365952 @brendangregg committed Feb 9, 2015
Showing with 169 additions and 0 deletions.
  1. +169 −0 userspace/sysdig/chisels/subsecoffset.lua
@@ -0,0 +1,169 @@
+--[[
+Copyright (C) 2013-2014 Draios inc.
+Copyright (C) 2015 Brendan Gregg.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 2 as
+published by the Free Software Foundation.
+
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+--]]
+
+-- Chisel description
+description = "This visualizes the subsecond offset time of system call execution. This allows repetitive patterns to be identified, which are lost when averaging at a one second granularity. The Y axis (vertical) shows the passage of time. The X axis (horizontal) shows the passage of time within whole or fractions of a second. By default, the X axis range is 1000 milliseconds; this can be specified as an argument (try 100). Each bucket of the heat map, or spectrogram, is shown as a colored rectangle. The color shows how many syscalls fell into that time and subsecond offset range. It can be black (no calls), green (tens of calls/s), yellow (hundreds of calls/s) or red (Thousands of calls/s). Use this chisel in conjunction with filters to visualize latencies for certain processes, types of I/O activity, file systems, etc."
+short_description = "Visualize subsecond offset execution time."
+category = "CPU Usage"
+
+-- Chisel argument list
+args = {
+ {
+ name = "refresh_time",
+ description = "chart refresh time in milliseconds",
+ argtype = "int",
+ optional = true
+ },
+}
+
+require "common"
+terminal = require "ansiterminal"
+terminal.enable_color(true)
+
+refresh_time = 1000 * 1000 * 1000
+refresh_per_sec = 1 * 1000 * 1000 * 1000 / refresh_time
+max_label_len = 0
+frequencies = {}
+colpalette = {22, 28, 64, 34, 2, 76, 46, 118, 154, 191, 227, 226, 11, 220, 209, 208, 202, 197, 9, 1}
+
+function on_set_arg(name, val)
+ if name == "refresh_time" then
+ refresh_time = parse_numeric_input(val, name) * 1 * 1000 * 1000
+ refresh_per_sec = 1 * 1000 * 1000 * 1000 / refresh_time
+ return true
+ end
+
+ return false
+end
+
+function on_init()
+ is_tty = sysdig.is_tty()
+
+ if not is_tty then
+ print("This chisel only works on ANSI terminals. Aborting.")
+ return false
+ end
+
+ tinfo = sysdig.get_terminal_info()
+ w = tinfo.width
+ h = tinfo.height
+ max_label_len = string.len("|" .. (0.9 * refresh_time / 10000000) .. "ms")
+
+ -- trace syscall entry
+ chisel.set_filter("evt.dir=>")
+
+ rawtime = chisel.request_field("evt.rawtime")
+
+ terminal.hidecursor()
+
+ print("Tracing syscalls... red (hot) == high frequency <-> green == low frequency.\n")
+
+ return true
+end
+
+function on_capture_start()
+ chisel.set_interval_ns(refresh_time)
+ return true
+end
+
+function on_event()
+ local subsec = evt.field(rawtime)
+
+ -- subsec is normalized to terminal column location
+ subsec = math.floor((((subsec * 1000 / refresh_time) % 1000) / 1000) * w)
+
+ if frequencies[subsec] == nil then
+ frequencies[subsec] = 1
+ else
+ frequencies[subsec] = frequencies[subsec] + 1
+ end
+
+ return true
+end
+
+function mkcol(n)
+ local col = math.floor(math.log10(n * refresh_per_sec + 1) / math.log10(1.6))
+
+ if col < 1 then
+ col = 1
+ end
+
+ if col > #colpalette then
+ col = #colpalette
+ end
+
+ return colpalette[col]
+end
+
+function on_interval(ts_s, ts_ns, delta)
+ terminal.moveup(1)
+
+ for x = 1, w do
+ local fr = frequencies[x]
+
+ if fr == nil or fr == 0 then
+ terminal.setbgcol(0)
+ else
+ terminal.setbgcol(mkcol(fr))
+ end
+
+ io.write(" ")
+ end
+
+ io.write(terminal.reset .. "\n")
+
+ local x = 0
+ while true do
+ if x >= w then
+ break
+ end
+
+ local curtime = math.floor(x * 10 / w)
+ local prevtime = math.floor((x - 1) * 10 / w)
+
+ if curtime ~= prevtime then
+ if (x <= w - max_label_len) then
+ local tstr = "|" .. (math.floor(10 * x / w) * refresh_time / 10000000) .. "ms"
+ io.write(tstr)
+ x = x + string.len(tstr)
+ else
+ io.write(" ")
+ x = x + 1
+ end
+ else
+ io.write(" ")
+ x = x + 1
+ end
+ end
+
+ io.write("\n")
+
+ frequencies = {}
+
+ return true
+end
+
+function on_capture_end(ts_s, ts_ns, delta)
+ if is_tty then
+ print(terminal.reset)
+ terminal.showcursor()
+ end
+
+ return true
+end
+

0 comments on commit 2f21604

Please sign in to comment.