1#!/usr/bin/env bcc-lua 2--[[ 3Copyright 2016 Marek Vavrusa <mvavrusa@cloudflare.com> 4 5Licensed under the Apache License, Version 2.0 (the "License"); 6you may not use this file except in compliance with the License. 7You may obtain a copy of the License at 8 9http://www.apache.org/licenses/LICENSE-2.0 10 11Unless required by applicable law or agreed to in writing, software 12distributed under the License is distributed on an "AS IS" BASIS, 13WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14See the License for the specific language governing permissions and 15limitations under the License. 16]] 17-- Summarize off-CPU time by stack trace 18-- Related tool: https://github.com/iovisor/bcc/blob/master/tools/offcputime.py 19local ffi = require('ffi') 20local bpf = require('bpf') 21local S = require('syscall') 22-- Create BPF maps 23-- TODO: made smaller to fit default memory limits 24local key_t = 'struct { char name[16]; int32_t stack_id; }' 25local starts = assert(bpf.map('hash', 128, ffi.typeof('uint32_t'), ffi.typeof('uint64_t'))) 26local counts = assert(bpf.map('hash', 128, ffi.typeof(key_t), ffi.typeof('uint64_t'))) 27local stack_traces = assert(bpf.map('stack_trace', 16)) 28-- Open tracepoint and attach BPF program 29-- The 'arg' parses tracepoint format automatically 30local tp = bpf.tracepoint('sched/sched_switch', function (arg) 31 -- Update previous thread sleep time 32 local pid = arg.prev_pid 33 local now = time() 34 starts[pid] = now 35 -- Calculate current thread's delta time 36 pid = arg.next_pid 37 local from = starts[pid] 38 if not from then 39 return 0 40 end 41 local delta = (now - from) / 1000 42 starts[pid] = nil 43 -- Check if the delta is below 1us 44 if delta < 1 then 45 return 46 end 47 -- Create key for this thread 48 local key = ffi.new(key_t) 49 comm(key.name) 50 key.stack_id = stack_id(stack_traces, BPF.F_FAST_STACK_CMP) 51 -- Update current thread off cpu time with delta 52 local val = counts[key] 53 if not val then 54 counts[key] = 0 55 end 56 xadd(counts[key], delta) 57end, 0, -1) 58-- Helper: load kernel symbols 59ffi.cdef 'unsigned long long strtoull(const char *, char **, int);' 60local ksyms = {} 61for l in io.lines('/proc/kallsyms') do 62 local addr, sym = l:match '(%w+) %w (%S+)' 63 if addr then ksyms[ffi.C.strtoull(addr, nil, 16)] = sym end 64end 65-- User-space part of the program 66while true do 67 for k,v in counts.pairs,counts,nil do 68 local s = '' 69 local traces = stack_traces[k.stack_id] 70 if traces then 71 for i, ip in ipairs(traces) do 72 s = s .. string.format(" %-16p %s", ip, ksyms[ip]) 73 end 74 end 75 s = s .. string.format(" %-16s %s", "-", ffi.string(k.name)) 76 s = s .. string.format(" %d", tonumber(v)) 77 print(s) 78 end 79 S.sleep(1) 80end 81