18c2ecf20Sopenharmony_ci/* Copyright (c) 2016 Facebook 28c2ecf20Sopenharmony_ci * 38c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or 48c2ecf20Sopenharmony_ci * modify it under the terms of version 2 of the GNU General Public 58c2ecf20Sopenharmony_ci * License as published by the Free Software Foundation. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci#include <uapi/linux/bpf.h> 88c2ecf20Sopenharmony_ci#include <uapi/linux/ptrace.h> 98c2ecf20Sopenharmony_ci#include <uapi/linux/perf_event.h> 108c2ecf20Sopenharmony_ci#include <linux/version.h> 118c2ecf20Sopenharmony_ci#include <linux/sched.h> 128c2ecf20Sopenharmony_ci#include <bpf/bpf_helpers.h> 138c2ecf20Sopenharmony_ci#include <bpf/bpf_tracing.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#define _(P) \ 168c2ecf20Sopenharmony_ci ({ \ 178c2ecf20Sopenharmony_ci typeof(P) val; \ 188c2ecf20Sopenharmony_ci bpf_probe_read_kernel(&val, sizeof(val), &(P)); \ 198c2ecf20Sopenharmony_ci val; \ 208c2ecf20Sopenharmony_ci }) 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define MINBLOCK_US 1 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistruct key_t { 258c2ecf20Sopenharmony_ci char waker[TASK_COMM_LEN]; 268c2ecf20Sopenharmony_ci char target[TASK_COMM_LEN]; 278c2ecf20Sopenharmony_ci u32 wret; 288c2ecf20Sopenharmony_ci u32 tret; 298c2ecf20Sopenharmony_ci}; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistruct { 328c2ecf20Sopenharmony_ci __uint(type, BPF_MAP_TYPE_HASH); 338c2ecf20Sopenharmony_ci __type(key, struct key_t); 348c2ecf20Sopenharmony_ci __type(value, u64); 358c2ecf20Sopenharmony_ci __uint(max_entries, 10000); 368c2ecf20Sopenharmony_ci} counts SEC(".maps"); 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistruct { 398c2ecf20Sopenharmony_ci __uint(type, BPF_MAP_TYPE_HASH); 408c2ecf20Sopenharmony_ci __type(key, u32); 418c2ecf20Sopenharmony_ci __type(value, u64); 428c2ecf20Sopenharmony_ci __uint(max_entries, 10000); 438c2ecf20Sopenharmony_ci} start SEC(".maps"); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistruct wokeby_t { 468c2ecf20Sopenharmony_ci char name[TASK_COMM_LEN]; 478c2ecf20Sopenharmony_ci u32 ret; 488c2ecf20Sopenharmony_ci}; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistruct { 518c2ecf20Sopenharmony_ci __uint(type, BPF_MAP_TYPE_HASH); 528c2ecf20Sopenharmony_ci __type(key, u32); 538c2ecf20Sopenharmony_ci __type(value, struct wokeby_t); 548c2ecf20Sopenharmony_ci __uint(max_entries, 10000); 558c2ecf20Sopenharmony_ci} wokeby SEC(".maps"); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_cistruct { 588c2ecf20Sopenharmony_ci __uint(type, BPF_MAP_TYPE_STACK_TRACE); 598c2ecf20Sopenharmony_ci __uint(key_size, sizeof(u32)); 608c2ecf20Sopenharmony_ci __uint(value_size, PERF_MAX_STACK_DEPTH * sizeof(u64)); 618c2ecf20Sopenharmony_ci __uint(max_entries, 10000); 628c2ecf20Sopenharmony_ci} stackmap SEC(".maps"); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define STACKID_FLAGS (0 | BPF_F_FAST_STACK_CMP) 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ciSEC("kprobe/try_to_wake_up") 678c2ecf20Sopenharmony_ciint waker(struct pt_regs *ctx) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct task_struct *p = (void *) PT_REGS_PARM1(ctx); 708c2ecf20Sopenharmony_ci struct wokeby_t woke; 718c2ecf20Sopenharmony_ci u32 pid; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci pid = _(p->pid); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci bpf_get_current_comm(&woke.name, sizeof(woke.name)); 768c2ecf20Sopenharmony_ci woke.ret = bpf_get_stackid(ctx, &stackmap, STACKID_FLAGS); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci bpf_map_update_elem(&wokeby, &pid, &woke, BPF_ANY); 798c2ecf20Sopenharmony_ci return 0; 808c2ecf20Sopenharmony_ci} 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic inline int update_counts(void *ctx, u32 pid, u64 delta) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci struct wokeby_t *woke; 858c2ecf20Sopenharmony_ci u64 zero = 0, *val; 868c2ecf20Sopenharmony_ci struct key_t key; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci __builtin_memset(&key.waker, 0, sizeof(key.waker)); 898c2ecf20Sopenharmony_ci bpf_get_current_comm(&key.target, sizeof(key.target)); 908c2ecf20Sopenharmony_ci key.tret = bpf_get_stackid(ctx, &stackmap, STACKID_FLAGS); 918c2ecf20Sopenharmony_ci key.wret = 0; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci woke = bpf_map_lookup_elem(&wokeby, &pid); 948c2ecf20Sopenharmony_ci if (woke) { 958c2ecf20Sopenharmony_ci key.wret = woke->ret; 968c2ecf20Sopenharmony_ci __builtin_memcpy(&key.waker, woke->name, sizeof(key.waker)); 978c2ecf20Sopenharmony_ci bpf_map_delete_elem(&wokeby, &pid); 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci val = bpf_map_lookup_elem(&counts, &key); 1018c2ecf20Sopenharmony_ci if (!val) { 1028c2ecf20Sopenharmony_ci bpf_map_update_elem(&counts, &key, &zero, BPF_NOEXIST); 1038c2ecf20Sopenharmony_ci val = bpf_map_lookup_elem(&counts, &key); 1048c2ecf20Sopenharmony_ci if (!val) 1058c2ecf20Sopenharmony_ci return 0; 1068c2ecf20Sopenharmony_ci } 1078c2ecf20Sopenharmony_ci (*val) += delta; 1088c2ecf20Sopenharmony_ci return 0; 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci#if 1 1128c2ecf20Sopenharmony_ci/* taken from /sys/kernel/debug/tracing/events/sched/sched_switch/format */ 1138c2ecf20Sopenharmony_cistruct sched_switch_args { 1148c2ecf20Sopenharmony_ci unsigned long long pad; 1158c2ecf20Sopenharmony_ci char prev_comm[16]; 1168c2ecf20Sopenharmony_ci int prev_pid; 1178c2ecf20Sopenharmony_ci int prev_prio; 1188c2ecf20Sopenharmony_ci long long prev_state; 1198c2ecf20Sopenharmony_ci char next_comm[16]; 1208c2ecf20Sopenharmony_ci int next_pid; 1218c2ecf20Sopenharmony_ci int next_prio; 1228c2ecf20Sopenharmony_ci}; 1238c2ecf20Sopenharmony_ciSEC("tracepoint/sched/sched_switch") 1248c2ecf20Sopenharmony_ciint oncpu(struct sched_switch_args *ctx) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci /* record previous thread sleep time */ 1278c2ecf20Sopenharmony_ci u32 pid = ctx->prev_pid; 1288c2ecf20Sopenharmony_ci#else 1298c2ecf20Sopenharmony_ciSEC("kprobe/finish_task_switch") 1308c2ecf20Sopenharmony_ciint oncpu(struct pt_regs *ctx) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci struct task_struct *p = (void *) PT_REGS_PARM1(ctx); 1338c2ecf20Sopenharmony_ci /* record previous thread sleep time */ 1348c2ecf20Sopenharmony_ci u32 pid = _(p->pid); 1358c2ecf20Sopenharmony_ci#endif 1368c2ecf20Sopenharmony_ci u64 delta, ts, *tsp; 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci ts = bpf_ktime_get_ns(); 1398c2ecf20Sopenharmony_ci bpf_map_update_elem(&start, &pid, &ts, BPF_ANY); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci /* calculate current thread's delta time */ 1428c2ecf20Sopenharmony_ci pid = bpf_get_current_pid_tgid(); 1438c2ecf20Sopenharmony_ci tsp = bpf_map_lookup_elem(&start, &pid); 1448c2ecf20Sopenharmony_ci if (!tsp) 1458c2ecf20Sopenharmony_ci /* missed start or filtered */ 1468c2ecf20Sopenharmony_ci return 0; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci delta = bpf_ktime_get_ns() - *tsp; 1498c2ecf20Sopenharmony_ci bpf_map_delete_elem(&start, &pid); 1508c2ecf20Sopenharmony_ci delta = delta / 1000; 1518c2ecf20Sopenharmony_ci if (delta < MINBLOCK_US) 1528c2ecf20Sopenharmony_ci return 0; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci return update_counts(ctx, pid, delta); 1558c2ecf20Sopenharmony_ci} 1568c2ecf20Sopenharmony_cichar _license[] SEC("license") = "GPL"; 1578c2ecf20Sopenharmony_ciu32 _version SEC("version") = LINUX_VERSION_CODE; 158