xref: /linux/samples/bpf/offwaketime.bpf.c (revision a1c613ae4c322ddd58d5a8539dbfba2a0380a8c0)
14a0ee788SDaniel T. Lee /* Copyright (c) 2016 Facebook
24a0ee788SDaniel T. Lee  *
34a0ee788SDaniel T. Lee  * This program is free software; you can redistribute it and/or
44a0ee788SDaniel T. Lee  * modify it under the terms of version 2 of the GNU General Public
54a0ee788SDaniel T. Lee  * License as published by the Free Software Foundation.
64a0ee788SDaniel T. Lee  */
74a0ee788SDaniel T. Lee #include "vmlinux.h"
84a0ee788SDaniel T. Lee #include <linux/version.h>
94a0ee788SDaniel T. Lee #include <bpf/bpf_helpers.h>
104a0ee788SDaniel T. Lee #include <bpf/bpf_tracing.h>
11*11430421SDaniel T. Lee #include <bpf/bpf_core_read.h>
124a0ee788SDaniel T. Lee 
134a0ee788SDaniel T. Lee #ifndef PERF_MAX_STACK_DEPTH
144a0ee788SDaniel T. Lee #define PERF_MAX_STACK_DEPTH         127
154a0ee788SDaniel T. Lee #endif
164a0ee788SDaniel T. Lee 
174a0ee788SDaniel T. Lee #define MINBLOCK_US	1
184a0ee788SDaniel T. Lee #define MAX_ENTRIES	10000
194a0ee788SDaniel T. Lee 
204a0ee788SDaniel T. Lee struct key_t {
214a0ee788SDaniel T. Lee 	char waker[TASK_COMM_LEN];
224a0ee788SDaniel T. Lee 	char target[TASK_COMM_LEN];
234a0ee788SDaniel T. Lee 	u32 wret;
244a0ee788SDaniel T. Lee 	u32 tret;
254a0ee788SDaniel T. Lee };
264a0ee788SDaniel T. Lee 
274a0ee788SDaniel T. Lee struct {
284a0ee788SDaniel T. Lee 	__uint(type, BPF_MAP_TYPE_HASH);
294a0ee788SDaniel T. Lee 	__type(key, struct key_t);
304a0ee788SDaniel T. Lee 	__type(value, u64);
314a0ee788SDaniel T. Lee 	__uint(max_entries, MAX_ENTRIES);
324a0ee788SDaniel T. Lee } counts SEC(".maps");
334a0ee788SDaniel T. Lee 
344a0ee788SDaniel T. Lee struct {
354a0ee788SDaniel T. Lee 	__uint(type, BPF_MAP_TYPE_HASH);
364a0ee788SDaniel T. Lee 	__type(key, u32);
374a0ee788SDaniel T. Lee 	__type(value, u64);
384a0ee788SDaniel T. Lee 	__uint(max_entries, MAX_ENTRIES);
394a0ee788SDaniel T. Lee } start SEC(".maps");
404a0ee788SDaniel T. Lee 
414a0ee788SDaniel T. Lee struct wokeby_t {
424a0ee788SDaniel T. Lee 	char name[TASK_COMM_LEN];
434a0ee788SDaniel T. Lee 	u32 ret;
444a0ee788SDaniel T. Lee };
454a0ee788SDaniel T. Lee 
464a0ee788SDaniel T. Lee struct {
474a0ee788SDaniel T. Lee 	__uint(type, BPF_MAP_TYPE_HASH);
484a0ee788SDaniel T. Lee 	__type(key, u32);
494a0ee788SDaniel T. Lee 	__type(value, struct wokeby_t);
504a0ee788SDaniel T. Lee 	__uint(max_entries, MAX_ENTRIES);
514a0ee788SDaniel T. Lee } wokeby SEC(".maps");
524a0ee788SDaniel T. Lee 
534a0ee788SDaniel T. Lee struct {
544a0ee788SDaniel T. Lee 	__uint(type, BPF_MAP_TYPE_STACK_TRACE);
554a0ee788SDaniel T. Lee 	__uint(key_size, sizeof(u32));
564a0ee788SDaniel T. Lee 	__uint(value_size, PERF_MAX_STACK_DEPTH * sizeof(u64));
574a0ee788SDaniel T. Lee 	__uint(max_entries, MAX_ENTRIES);
584a0ee788SDaniel T. Lee } stackmap SEC(".maps");
594a0ee788SDaniel T. Lee 
604a0ee788SDaniel T. Lee #define STACKID_FLAGS (0 | BPF_F_FAST_STACK_CMP)
614a0ee788SDaniel T. Lee 
624a0ee788SDaniel T. Lee SEC("kprobe/try_to_wake_up")
waker(struct pt_regs * ctx)634a0ee788SDaniel T. Lee int waker(struct pt_regs *ctx)
644a0ee788SDaniel T. Lee {
65*11430421SDaniel T. Lee 	struct task_struct *p = (void *)PT_REGS_PARM1_CORE(ctx);
66*11430421SDaniel T. Lee 	u32 pid = BPF_CORE_READ(p, pid);
674a0ee788SDaniel T. Lee 	struct wokeby_t woke;
684a0ee788SDaniel T. Lee 
694a0ee788SDaniel T. Lee 	bpf_get_current_comm(&woke.name, sizeof(woke.name));
704a0ee788SDaniel T. Lee 	woke.ret = bpf_get_stackid(ctx, &stackmap, STACKID_FLAGS);
714a0ee788SDaniel T. Lee 
724a0ee788SDaniel T. Lee 	bpf_map_update_elem(&wokeby, &pid, &woke, BPF_ANY);
734a0ee788SDaniel T. Lee 	return 0;
744a0ee788SDaniel T. Lee }
754a0ee788SDaniel T. Lee 
update_counts(void * ctx,u32 pid,u64 delta)764a0ee788SDaniel T. Lee static inline int update_counts(void *ctx, u32 pid, u64 delta)
774a0ee788SDaniel T. Lee {
784a0ee788SDaniel T. Lee 	struct wokeby_t *woke;
794a0ee788SDaniel T. Lee 	u64 zero = 0, *val;
804a0ee788SDaniel T. Lee 	struct key_t key;
814a0ee788SDaniel T. Lee 
824a0ee788SDaniel T. Lee 	__builtin_memset(&key.waker, 0, sizeof(key.waker));
834a0ee788SDaniel T. Lee 	bpf_get_current_comm(&key.target, sizeof(key.target));
844a0ee788SDaniel T. Lee 	key.tret = bpf_get_stackid(ctx, &stackmap, STACKID_FLAGS);
854a0ee788SDaniel T. Lee 	key.wret = 0;
864a0ee788SDaniel T. Lee 
874a0ee788SDaniel T. Lee 	woke = bpf_map_lookup_elem(&wokeby, &pid);
884a0ee788SDaniel T. Lee 	if (woke) {
894a0ee788SDaniel T. Lee 		key.wret = woke->ret;
904a0ee788SDaniel T. Lee 		__builtin_memcpy(&key.waker, woke->name, sizeof(key.waker));
914a0ee788SDaniel T. Lee 		bpf_map_delete_elem(&wokeby, &pid);
924a0ee788SDaniel T. Lee 	}
934a0ee788SDaniel T. Lee 
944a0ee788SDaniel T. Lee 	val = bpf_map_lookup_elem(&counts, &key);
954a0ee788SDaniel T. Lee 	if (!val) {
964a0ee788SDaniel T. Lee 		bpf_map_update_elem(&counts, &key, &zero, BPF_NOEXIST);
974a0ee788SDaniel T. Lee 		val = bpf_map_lookup_elem(&counts, &key);
984a0ee788SDaniel T. Lee 		if (!val)
994a0ee788SDaniel T. Lee 			return 0;
1004a0ee788SDaniel T. Lee 	}
1014a0ee788SDaniel T. Lee 	(*val) += delta;
1024a0ee788SDaniel T. Lee 	return 0;
1034a0ee788SDaniel T. Lee }
1044a0ee788SDaniel T. Lee 
1054a0ee788SDaniel T. Lee #if 1
1064a0ee788SDaniel T. Lee /* taken from /sys/kernel/tracing/events/sched/sched_switch/format */
1074a0ee788SDaniel T. Lee SEC("tracepoint/sched/sched_switch")
oncpu(struct trace_event_raw_sched_switch * ctx)1084a0ee788SDaniel T. Lee int oncpu(struct trace_event_raw_sched_switch *ctx)
1094a0ee788SDaniel T. Lee {
1104a0ee788SDaniel T. Lee 	/* record previous thread sleep time */
1114a0ee788SDaniel T. Lee 	u32 pid = ctx->prev_pid;
1124a0ee788SDaniel T. Lee #else
11302dabc24SDaniel T. Lee SEC("kprobe.multi/finish_task_switch*")
1144a0ee788SDaniel T. Lee int oncpu(struct pt_regs *ctx)
1154a0ee788SDaniel T. Lee {
116*11430421SDaniel T. Lee 	struct task_struct *p = (void *)PT_REGS_PARM1_CORE(ctx);
1174a0ee788SDaniel T. Lee 	/* record previous thread sleep time */
118*11430421SDaniel T. Lee 	u32 pid = BPF_CORE_READ(p, pid);
1194a0ee788SDaniel T. Lee #endif
1204a0ee788SDaniel T. Lee 	u64 delta, ts, *tsp;
1214a0ee788SDaniel T. Lee 
1224a0ee788SDaniel T. Lee 	ts = bpf_ktime_get_ns();
1234a0ee788SDaniel T. Lee 	bpf_map_update_elem(&start, &pid, &ts, BPF_ANY);
1244a0ee788SDaniel T. Lee 
1254a0ee788SDaniel T. Lee 	/* calculate current thread's delta time */
1264a0ee788SDaniel T. Lee 	pid = bpf_get_current_pid_tgid();
1274a0ee788SDaniel T. Lee 	tsp = bpf_map_lookup_elem(&start, &pid);
1284a0ee788SDaniel T. Lee 	if (!tsp)
1294a0ee788SDaniel T. Lee 		/* missed start or filtered */
1304a0ee788SDaniel T. Lee 		return 0;
1314a0ee788SDaniel T. Lee 
1324a0ee788SDaniel T. Lee 	delta = bpf_ktime_get_ns() - *tsp;
1334a0ee788SDaniel T. Lee 	bpf_map_delete_elem(&start, &pid);
1344a0ee788SDaniel T. Lee 	delta = delta / 1000;
1354a0ee788SDaniel T. Lee 	if (delta < MINBLOCK_US)
1364a0ee788SDaniel T. Lee 		return 0;
1374a0ee788SDaniel T. Lee 
1384a0ee788SDaniel T. Lee 	return update_counts(ctx, pid, delta);
1394a0ee788SDaniel T. Lee }
1404a0ee788SDaniel T. Lee char _license[] SEC("license") = "GPL";
1414a0ee788SDaniel T. Lee u32 _version SEC("version") = LINUX_VERSION_CODE;
142