1#!/usr/bin/env bcc-lua 2--[[ 3Copyright 2016 GitHub, Inc 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 18local program = [[ 19#include <uapi/linux/ptrace.h> 20#include <linux/sched.h> 21 22#define MINBLOCK_US 1 23 24struct key_t { 25 char name[TASK_COMM_LEN]; 26 int stack_id; 27}; 28BPF_HASH(counts, struct key_t); 29BPF_HASH(start, u32); 30BPF_STACK_TRACE(stack_traces, 10240); 31 32int oncpu(struct pt_regs *ctx, struct task_struct *prev) { 33 u32 pid; 34 u64 ts, *tsp; 35 36 // record previous thread sleep time 37 if (FILTER) { 38 pid = prev->pid; 39 ts = bpf_ktime_get_ns(); 40 start.update(&pid, &ts); 41 } 42 43 // calculate current thread's delta time 44 pid = bpf_get_current_pid_tgid(); 45 tsp = start.lookup(&pid); 46 if (tsp == 0) 47 return 0; // missed start or filtered 48 u64 delta = bpf_ktime_get_ns() - *tsp; 49 start.delete(&pid); 50 delta = delta / 1000; 51 if (delta < MINBLOCK_US) 52 return 0; 53 54 // create map key 55 u64 zero = 0, *val; 56 struct key_t key = {}; 57 int stack_flags = 0; 58 59 /* 60 if (!(prev->flags & PF_KTHREAD)) 61 stack_flags |= BPF_F_USER_STACK; 62 */ 63 64 bpf_get_current_comm(&key.name, sizeof(key.name)); 65 key.stack_id = stack_traces.get_stackid(ctx, stack_flags); 66 67 val = counts.lookup_or_try_init(&key, &zero); 68 if (val) { 69 (*val) += delta; 70 } 71 return 0; 72} 73]] 74 75return function(BPF, utils) 76 local ffi = require("ffi") 77 78 local parser = utils.argparse("offcputime", "Summarize off-cpu time") 79 parser:flag("-u --user-only") 80 parser:option("-p --pid"):convert(tonumber) 81 parser:flag("-f --folded") 82 parser:option("-d --duration", "duration to trace for", 9999999):convert(tonumber) 83 84 local args = parser:parse() 85 local ksym = BPF.SymbolCache() 86 local filter = "1" 87 local MAXDEPTH = 20 88 89 if args.pid then 90 filter = "pid == %d" % args.pid 91 elseif args.user_only then 92 filter = "!(prev->flags & PF_KTHREAD)" 93 end 94 95 local text = program:gsub("FILTER", filter) 96 local b = BPF:new{text=text} 97 b:attach_kprobe{event="finish_task_switch", fn_name="oncpu"} 98 99 if BPF.num_open_kprobes() == 0 then 100 print("no functions matched. quitting...") 101 return 102 end 103 104 print("Sleeping for %d seconds..." % args.duration) 105 pcall(utils.posix.sleep, args.duration) 106 print("Tracing...") 107 108 local counts = b:get_table("counts") 109 local stack_traces = b:get_table("stack_traces") 110 111 for k, v in counts:items() do 112 for addr in stack_traces:walk(tonumber(k.stack_id)) do 113 print(" %-16p %s" % {addr, ksym:resolve(addr)}) 114 end 115 print(" %-16s %s" % {"-", ffi.string(k.name)}) 116 print(" %d\n" % tonumber(v)) 117 end 118end 119