blob: fccf0b79da824b2173578d4c99a447c361a280ca [file] [log] [blame]
#!/usr/bin/env bcc-lua
--[[
Copyright 2016 Marek Vavrusa <mvavrusa@cloudflare.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
]]
-- Summarize off-CPU time by stack trace
-- Related tool: https://github.com/iovisor/bcc/blob/master/tools/offcputime.py
local ffi = require('ffi')
local bpf = require('bpf')
local S = require('syscall')
-- Create BPF maps
-- TODO: made smaller to fit default memory limits
local key_t = 'struct { char name[16]; int32_t stack_id; }'
local starts = assert(bpf.map('hash', 128, ffi.typeof('uint32_t'), ffi.typeof('uint64_t')))
local counts = assert(bpf.map('hash', 128, ffi.typeof(key_t), ffi.typeof('uint64_t')))
local stack_traces = assert(bpf.map('stack_trace', 16))
-- Open tracepoint and attach BPF program
-- The 'arg' parses tracepoint format automatically
local tp = bpf.tracepoint('sched/sched_switch', function (arg)
-- Update previous thread sleep time
local pid = arg.prev_pid
local now = time()
starts[pid] = now
-- Calculate current thread's delta time
pid = arg.next_pid
local from = starts[pid]
if not from then
return 0
end
local delta = (now - from) / 1000
starts[pid] = nil
-- Check if the delta is below 1us
if delta < 1 then
return
end
-- Create key for this thread
local key = ffi.new(key_t)
comm(key.name)
key.stack_id = stack_id(stack_traces, BPF.F_FAST_STACK_CMP)
-- Update current thread off cpu time with delta
local val = counts[key]
if not val then
counts[key] = 0
end
xadd(counts[key], delta)
end, 0, -1)
-- Helper: load kernel symbols
ffi.cdef 'unsigned long long strtoull(const char *, char **, int);'
local ksyms = {}
for l in io.lines('/proc/kallsyms') do
local addr, sym = l:match '(%w+) %w (%S+)'
if addr then ksyms[ffi.C.strtoull(addr, nil, 16)] = sym end
end
-- User-space part of the program
while true do
for k,v in counts.pairs,counts,nil do
local s = ''
local traces = stack_traces[k.stack_id]
if traces then
for i, ip in ipairs(traces) do
s = s .. string.format(" %-16p %s", ip, ksyms[ip])
end
end
s = s .. string.format(" %-16s %s", "-", ffi.string(k.name))
s = s .. string.format(" %d", tonumber(v))
print(s)
end
S.sleep(1)
end