Home | History | Annotate | Download | only in lua
      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