1 #!/usr/bin/env bcc-lua 2 --[[ 3 Copyright 2016 GitHub, Inc 4 5 Licensed under the Apache License, Version 2.0 (the "License"); 6 you may not use this file except in compliance with the License. 7 You may obtain a copy of the License at 8 9 http://www.apache.org/licenses/LICENSE-2.0 10 11 Unless required by applicable law or agreed to in writing, software 12 distributed under the License is distributed on an "AS IS" BASIS, 13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 See the License for the specific language governing permissions and 15 limitations under the License. 16 ]] 17 18 local program = [[ 19 #include <uapi/linux/ptrace.h> 20 #include <linux/sched.h> 21 22 #define MINBLOCK_US 1 23 24 struct key_t { 25 char name[TASK_COMM_LEN]; 26 int stack_id; 27 }; 28 BPF_HASH(counts, struct key_t); 29 BPF_HASH(start, u32); 30 BPF_STACK_TRACE(stack_traces, 10240); 31 32 int 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 = BPF_F_REUSE_STACKID; 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_init(&key, &zero); 68 (*val) += delta; 69 return 0; 70 } 71 ]] 72 73 return function(BPF, utils) 74 local ffi = require("ffi") 75 76 local parser = utils.argparse("offcputime", "Summarize off-cpu time") 77 parser:flag("-u --user-only") 78 parser:option("-p --pid"):convert(tonumber) 79 parser:flag("-f --folded") 80 parser:option("-d --duration", "duration to trace for", 9999999):convert(tonumber) 81 82 local args = parser:parse() 83 local ksym = BPF.SymbolCache() 84 local filter = "1" 85 local MAXDEPTH = 20 86 87 if args.pid then 88 filter = "pid == %d" % args.pid 89 elseif args.user_only then 90 filter = "!(prev->flags & PF_KTHREAD)" 91 end 92 93 local text = program:gsub("FILTER", filter) 94 local b = BPF:new{text=text} 95 b:attach_kprobe{event="finish_task_switch", fn_name="oncpu"} 96 97 if BPF.num_open_kprobes() == 0 then 98 print("no functions matched. quitting...") 99 return 100 end 101 102 print("Sleeping for %d seconds..." % args.duration) 103 pcall(utils.posix.sleep, args.duration) 104 print("Tracing...") 105 106 local counts = b:get_table("counts") 107 local stack_traces = b:get_table("stack_traces") 108 109 for k, v in counts:items() do 110 for addr in stack_traces:walk(tonumber(k.stack_id)) do 111 print(" %-16p %s" % {addr, ksym:resolve(addr)}) 112 end 113 print(" %-16s %s" % {"-", ffi.string(k.name)}) 114 print(" %d\n" % tonumber(v)) 115 end 116 end 117