xref: /aosp_15_r20/external/bcc/examples/lua/offcputime.lua (revision 387f9dfdfa2baef462e92476d413c7bc2470293e)
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