summaryrefslogtreecommitdiffstats
path: root/tools/perf/util/bpf_skel/bperf_follower.bpf.c
blob: 0595063139a3d5555f6e9cc2314769be1a476d18 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
// Copyright (c) 2021 Facebook
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include "bperf_u.h"

#define MAX_ENTRIES 102400

struct {
	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
	__uint(key_size, sizeof(__u32));
	__uint(value_size, sizeof(struct bpf_perf_event_value));
	__uint(max_entries, 1);
} diff_readings SEC(".maps");

struct {
	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
	__uint(key_size, sizeof(__u32));
	__uint(value_size, sizeof(struct bpf_perf_event_value));
	__uint(max_entries, 1);
} accum_readings SEC(".maps");

struct {
	__uint(type, BPF_MAP_TYPE_HASH);
	__uint(key_size, sizeof(__u32));
	__uint(value_size, sizeof(struct bperf_filter_value));
	__uint(max_entries, MAX_ENTRIES);
	__uint(map_flags, BPF_F_NO_PREALLOC);
} filter SEC(".maps");

enum bperf_filter_type type = 0;
int enabled = 0;
int inherit;

SEC("fexit/XXX")
int BPF_PROG(fexit_XXX)
{
	struct bpf_perf_event_value *diff_val, *accum_val;
	__u32 filter_key, zero = 0;
	__u32 accum_key;
	struct bperf_filter_value *fval;

	if (!enabled)
		return 0;

	switch (type) {
	case BPERF_FILTER_GLOBAL:
		accum_key = zero;
		goto do_add;
	case BPERF_FILTER_CPU:
		filter_key = bpf_get_smp_processor_id();
		break;
	case BPERF_FILTER_PID:
		filter_key = bpf_get_current_pid_tgid() & 0xffffffff;
		break;
	case BPERF_FILTER_TGID:
		/* Use pid as the filter_key to exclude new task counts
		 * when inherit is disabled. Don't worry about the existing
		 * children in TGID losing their counts, bpf_counter has
		 * already added them to the filter map via perf_thread_map
		 * before this bpf prog runs.
		 */
		filter_key = inherit ?
			     bpf_get_current_pid_tgid() >> 32 :
			     bpf_get_current_pid_tgid() & 0xffffffff;
		break;
	default:
		return 0;
	}

	fval = bpf_map_lookup_elem(&filter, &filter_key);
	if (!fval)
		return 0;

	accum_key = fval->accum_key;
	if (fval->exited)
		bpf_map_delete_elem(&filter, &filter_key);

do_add:
	diff_val = bpf_map_lookup_elem(&diff_readings, &zero);
	if (!diff_val)
		return 0;

	accum_val = bpf_map_lookup_elem(&accum_readings, &accum_key);
	if (!accum_val)
		return 0;

	accum_val->counter += diff_val->counter;
	accum_val->enabled += diff_val->enabled;
	accum_val->running += diff_val->running;

	return 0;
}

/* The program is only used for PID or TGID filter types. */
SEC("tp_btf/task_newtask")
int BPF_PROG(on_newtask, struct task_struct *task, __u64 clone_flags)
{
	__u32 parent_key, child_key;
	struct bperf_filter_value *parent_fval;
	struct bperf_filter_value child_fval = { 0 };

	if (!enabled)
		return 0;

	switch (type) {
	case BPERF_FILTER_PID:
		parent_key = bpf_get_current_pid_tgid() & 0xffffffff;
		child_key = task->pid;
		break;
	case BPERF_FILTER_TGID:
		parent_key = bpf_get_current_pid_tgid() >> 32;
		child_key = task->tgid;
		if (child_key == parent_key)
			return 0;
		break;
	default:
		return 0;
	}

	/* Check if the current task is one of the target tasks to be counted */
	parent_fval = bpf_map_lookup_elem(&filter, &parent_key);
	if (!parent_fval)
		return 0;

	/* Start counting for the new task by adding it into filter map,
	 * inherit the accum key of its parent task so that they can be
	 * counted together.
	 */
	child_fval.accum_key = parent_fval->accum_key;
	child_fval.exited = 0;
	bpf_map_update_elem(&filter, &child_key, &child_fval, BPF_NOEXIST);

	return 0;
}

/* The program is only used for PID or TGID filter types. */
SEC("tp_btf/sched_process_exit")
int BPF_PROG(on_exittask, struct task_struct *task)
{
	__u32 pid;
	struct bperf_filter_value *fval;

	if (!enabled)
		return 0;

	/* Stop counting for this task by removing it from filter map.
	 * For TGID type, if the pid can be found in the map, it means that
	 * this pid belongs to the leader task. After the task exits, the
	 * tgid of its child tasks (if any) will be 1, so the pid can be
	 * safely removed.
	 */
	pid = task->pid;
	fval = bpf_map_lookup_elem(&filter, &pid);
	if (fval)
		fval->exited = 1;

	return 0;
}

char LICENSE[] SEC("license") = "Dual BSD/GPL";