blob: 2e1f5af7f56554f58908889c950fd71f0715be8a [file] [log] [blame]
Joel Fernandesc5c05162018-10-16 13:26:56 -07001/*
2 * time_in_state eBPF program
3 *
4 * Copyright (C) 2018 Google
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License version
8 * 2 as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 */
16
Dmitri Plotnikov266d5a22020-10-20 11:11:15 -070017#ifdef MOCK_BPF
18#include <test/mock_bpf_helpers.h>
19#else
Joel Fernandes (Google)4134dbb2019-02-12 12:47:25 -050020#include <bpf_helpers.h>
Dmitri Plotnikov266d5a22020-10-20 11:11:15 -070021#endif
22
Connor O'Brien8acde7e2019-09-03 13:16:25 -070023#include <bpf_timeinstate.h>
Joel Fernandesc5c05162018-10-16 13:26:56 -070024
Rafal Slawik3f140462021-01-05 18:58:08 +000025DEFINE_BPF_MAP_GRW(total_time_in_state_map, PERCPU_ARRAY, uint32_t, uint64_t, MAX_FREQS_FOR_TOTAL,
26 AID_SYSTEM)
27
Connor O'Brien12a2fca2020-02-14 21:09:29 -080028DEFINE_BPF_MAP_GRW(uid_time_in_state_map, PERCPU_HASH, time_key_t, tis_val_t, 1024, AID_SYSTEM)
Connor O'Brien55aea862019-06-07 16:58:16 -070029
Dmitri Plotnikov266d5a22020-10-20 11:11:15 -070030DEFINE_BPF_MAP_GRW(uid_concurrent_times_map, PERCPU_HASH, time_key_t, concurrent_val_t, 1024,
31 AID_SYSTEM)
Connor O'Brien12a2fca2020-02-14 21:09:29 -080032DEFINE_BPF_MAP_GRW(uid_last_update_map, HASH, uint32_t, uint64_t, 1024, AID_SYSTEM)
Connor O'Brien0dd29b82019-06-11 12:50:57 -070033
Maciej Żenczykowski3d1cf832020-02-18 14:28:56 -080034DEFINE_BPF_MAP_GWO(cpu_last_update_map, PERCPU_ARRAY, uint32_t, uint64_t, 1, AID_SYSTEM)
Joel Fernandesc5c05162018-10-16 13:26:56 -070035
Maciej Żenczykowski3d1cf832020-02-18 14:28:56 -080036DEFINE_BPF_MAP_GWO(cpu_policy_map, ARRAY, uint32_t, uint32_t, 1024, AID_SYSTEM)
37DEFINE_BPF_MAP_GWO(policy_freq_idx_map, ARRAY, uint32_t, uint8_t, 1024, AID_SYSTEM)
Connor O'Brien55aea862019-06-07 16:58:16 -070038
Maciej Żenczykowski3d1cf832020-02-18 14:28:56 -080039DEFINE_BPF_MAP_GWO(freq_to_idx_map, HASH, freq_idx_key_t, uint8_t, 2048, AID_SYSTEM)
Connor O'Brien55aea862019-06-07 16:58:16 -070040
Maciej Żenczykowski3d1cf832020-02-18 14:28:56 -080041DEFINE_BPF_MAP_GWO(nr_active_map, ARRAY, uint32_t, uint32_t, 1, AID_SYSTEM)
42DEFINE_BPF_MAP_GWO(policy_nr_active_map, ARRAY, uint32_t, uint32_t, 1024, AID_SYSTEM)
Connor O'Brien0dd29b82019-06-11 12:50:57 -070043
Dmitri Plotnikov6a651a32020-11-23 12:31:55 -080044DEFINE_BPF_MAP_GWO(pid_tracked_hash_map, HASH, uint32_t, pid_t, MAX_TRACKED_PIDS, AID_SYSTEM)
45DEFINE_BPF_MAP_GWO(pid_tracked_map, ARRAY, uint32_t, tracked_pid_t, MAX_TRACKED_PIDS, AID_SYSTEM)
Dmitri Plotnikov9e581d52020-10-17 21:32:16 -070046DEFINE_BPF_MAP_GWO(pid_task_aggregation_map, HASH, pid_t, uint16_t, 1024, AID_SYSTEM)
47DEFINE_BPF_MAP_GRO(pid_time_in_state_map, PERCPU_HASH, aggregated_task_tis_key_t, tis_val_t, 1024,
48 AID_SYSTEM)
49
Joel Fernandesc5c05162018-10-16 13:26:56 -070050struct switch_args {
51 unsigned long long ignore;
52 char prev_comm[16];
53 int prev_pid;
54 int prev_prio;
55 long long prev_state;
56 char next_comm[16];
57 int next_pid;
58 int next_prio;
59};
60
Connor O'Brien12a2fca2020-02-14 21:09:29 -080061DEFINE_BPF_PROG("tracepoint/sched/sched_switch", AID_ROOT, AID_SYSTEM, tp_sched_switch)
62(struct switch_args* args) {
Yabin Cuif1880342020-02-27 17:11:58 -080063 const int ALLOW = 1; // return 1 to avoid blocking simpleperf from receiving events.
Joel Fernandesc5c05162018-10-16 13:26:56 -070064 uint32_t zero = 0;
Maciej Żenczykowski44ad5cb2019-04-19 21:46:26 -070065 uint64_t* last = bpf_cpu_last_update_map_lookup_elem(&zero);
Yabin Cuif1880342020-02-27 17:11:58 -080066 if (!last) return ALLOW;
Joel Fernandesc5c05162018-10-16 13:26:56 -070067 uint64_t old_last = *last;
68 uint64_t time = bpf_ktime_get_ns();
69 *last = time;
Connor O'Brien1c7b9c92019-06-06 17:36:52 -070070
Connor O'Brien0dd29b82019-06-11 12:50:57 -070071 uint32_t* active = bpf_nr_active_map_lookup_elem(&zero);
Yabin Cuif1880342020-02-27 17:11:58 -080072 if (!active) return ALLOW;
Connor O'Brien1c7b9c92019-06-06 17:36:52 -070073
Joel Fernandesc5c05162018-10-16 13:26:56 -070074 uint32_t cpu = bpf_get_smp_processor_id();
Connor O'Brien1c7b9c92019-06-06 17:36:52 -070075 uint32_t* policyp = bpf_cpu_policy_map_lookup_elem(&cpu);
Yabin Cuif1880342020-02-27 17:11:58 -080076 if (!policyp) return ALLOW;
Connor O'Brien1c7b9c92019-06-06 17:36:52 -070077 uint32_t policy = *policyp;
Connor O'Brien0dd29b82019-06-11 12:50:57 -070078
79 uint32_t* policy_active = bpf_policy_nr_active_map_lookup_elem(&policy);
Yabin Cuif1880342020-02-27 17:11:58 -080080 if (!policy_active) return ALLOW;
Connor O'Brien0dd29b82019-06-11 12:50:57 -070081
82 uint32_t nactive = *active - 1;
83 uint32_t policy_nactive = *policy_active - 1;
84
85 if (!args->prev_pid || (!old_last && args->next_pid)) {
86 __sync_fetch_and_add(active, 1);
87 __sync_fetch_and_add(policy_active, 1);
88 }
89
90 // Return here in 2 scenarios:
91 // 1) prev_pid == 0, so we're exiting idle. No UID stats need updating, and active CPUs can't be
92 // decreasing.
93 // 2) old_last == 0, so this is the first time we've seen this CPU. Any delta will be invalid,
94 // and our active CPU counts don't include this CPU yet so we shouldn't decrement them even
95 // if we're going idle.
Yabin Cuif1880342020-02-27 17:11:58 -080096 if (!args->prev_pid || !old_last) return ALLOW;
Connor O'Brien0dd29b82019-06-11 12:50:57 -070097
98 if (!args->next_pid) {
99 __sync_fetch_and_add(active, -1);
100 __sync_fetch_and_add(policy_active, -1);
101 }
102
Connor O'Brien55aea862019-06-07 16:58:16 -0700103 uint8_t* freq_idxp = bpf_policy_freq_idx_map_lookup_elem(&policy);
Yabin Cuif1880342020-02-27 17:11:58 -0800104 if (!freq_idxp || !*freq_idxp) return ALLOW;
Connor O'Brien55aea862019-06-07 16:58:16 -0700105 // freq_to_idx_map uses 1 as its minimum index so that *freq_idxp == 0 only when uninitialized
106 uint8_t freq_idx = *freq_idxp - 1;
Connor O'Brien1c7b9c92019-06-06 17:36:52 -0700107
108 uint32_t uid = bpf_get_current_uid_gid();
Connor O'Brien8acde7e2019-09-03 13:16:25 -0700109 time_key_t key = {.uid = uid, .bucket = freq_idx / FREQS_PER_ENTRY};
110 tis_val_t* val = bpf_uid_time_in_state_map_lookup_elem(&key);
Connor O'Brien55aea862019-06-07 16:58:16 -0700111 if (!val) {
Connor O'Brien8acde7e2019-09-03 13:16:25 -0700112 tis_val_t zero_val = {.ar = {0}};
Connor O'Brien0dd29b82019-06-11 12:50:57 -0700113 bpf_uid_time_in_state_map_update_elem(&key, &zero_val, BPF_NOEXIST);
114 val = bpf_uid_time_in_state_map_lookup_elem(&key);
Connor O'Brien55aea862019-06-07 16:58:16 -0700115 }
Connor O'Brien1c7b9c92019-06-06 17:36:52 -0700116 uint64_t delta = time - old_last;
Connor O'Brien55aea862019-06-07 16:58:16 -0700117 if (val) val->ar[freq_idx % FREQS_PER_ENTRY] += delta;
Connor O'Brien0dd29b82019-06-11 12:50:57 -0700118
Rafal Slawik3f140462021-01-05 18:58:08 +0000119 // Add delta to total.
120 const uint32_t total_freq_idx = freq_idx < MAX_FREQS_FOR_TOTAL ? freq_idx :
121 MAX_FREQS_FOR_TOTAL - 1;
122 uint64_t* total = bpf_total_time_in_state_map_lookup_elem(&total_freq_idx);
123 if (total) *total += delta;
124
Dmitri Plotnikov9e581d52020-10-17 21:32:16 -0700125 const int pid = args->prev_pid;
126 const pid_t tgid = bpf_get_current_pid_tgid() >> 32;
Dmitri Plotnikov6a651a32020-11-23 12:31:55 -0800127 bool is_tgid_tracked = false;
128
129 // eBPF verifier does not currently allow loops.
130 // Instruct the C compiler to unroll the loop into a series of steps.
131 #pragma unroll
132 for (uint32_t index = 0; index < MAX_TRACKED_PIDS; index++) {
133 const uint32_t key = index;
134 tracked_pid_t* tracked_pid = bpf_pid_tracked_map_lookup_elem(&key);
135 if (!tracked_pid) continue;
136 if (tracked_pid->state == TRACKED_PID_STATE_UNUSED) {
137 // Reached the end of the list
138 break;
139 }
140
141 if (tracked_pid->state == TRACKED_PID_STATE_ACTIVE && tracked_pid->pid == tgid) {
142 is_tgid_tracked = true;
143 break;
144 }
145 }
146
147 if (is_tgid_tracked) {
Dmitri Plotnikov9e581d52020-10-17 21:32:16 -0700148 // If this process is marked for time-in-state tracking, aggregate the CPU time-in-state
149 // with other threads sharing the same TGID and aggregation key.
150 uint16_t* aggregation_key = bpf_pid_task_aggregation_map_lookup_elem(&pid);
151 aggregated_task_tis_key_t task_key = {
152 .tgid = tgid,
153 .aggregation_key = aggregation_key ? *aggregation_key : 0,
154 .bucket = freq_idx / FREQS_PER_ENTRY};
155 tis_val_t* task_val = bpf_pid_time_in_state_map_lookup_elem(&task_key);
156 if (!task_val) {
157 tis_val_t zero_val = {.ar = {0}};
158 bpf_pid_time_in_state_map_update_elem(&task_key, &zero_val, BPF_NOEXIST);
159 task_val = bpf_pid_time_in_state_map_lookup_elem(&task_key);
160 }
161 if (task_val) task_val->ar[freq_idx % FREQS_PER_ENTRY] += delta;
162 }
Connor O'Brien0dd29b82019-06-11 12:50:57 -0700163 key.bucket = nactive / CPUS_PER_ENTRY;
Connor O'Brien8acde7e2019-09-03 13:16:25 -0700164 concurrent_val_t* ct = bpf_uid_concurrent_times_map_lookup_elem(&key);
Connor O'Brien0dd29b82019-06-11 12:50:57 -0700165 if (!ct) {
Connor O'Brien8acde7e2019-09-03 13:16:25 -0700166 concurrent_val_t zero_val = {.active = {0}, .policy = {0}};
Connor O'Brien0dd29b82019-06-11 12:50:57 -0700167 bpf_uid_concurrent_times_map_update_elem(&key, &zero_val, BPF_NOEXIST);
168 ct = bpf_uid_concurrent_times_map_lookup_elem(&key);
169 }
170 if (ct) ct->active[nactive % CPUS_PER_ENTRY] += delta;
171
172 if (policy_nactive / CPUS_PER_ENTRY != key.bucket) {
173 key.bucket = policy_nactive / CPUS_PER_ENTRY;
174 ct = bpf_uid_concurrent_times_map_lookup_elem(&key);
175 if (!ct) {
Connor O'Brien8acde7e2019-09-03 13:16:25 -0700176 concurrent_val_t zero_val = {.active = {0}, .policy = {0}};
Connor O'Brien0dd29b82019-06-11 12:50:57 -0700177 bpf_uid_concurrent_times_map_update_elem(&key, &zero_val, BPF_NOEXIST);
178 ct = bpf_uid_concurrent_times_map_lookup_elem(&key);
179 }
180 }
181 if (ct) ct->policy[policy_nactive % CPUS_PER_ENTRY] += delta;
Connor O'Brien210aab52020-01-31 19:23:59 -0800182 uint64_t* uid_last_update = bpf_uid_last_update_map_lookup_elem(&uid);
183 if (uid_last_update) {
184 *uid_last_update = time;
185 } else {
186 bpf_uid_last_update_map_update_elem(&uid, &time, BPF_NOEXIST);
187 }
Yabin Cuif1880342020-02-27 17:11:58 -0800188 return ALLOW;
Joel Fernandesc5c05162018-10-16 13:26:56 -0700189}
190
191struct cpufreq_args {
192 unsigned long long ignore;
193 unsigned int state;
194 unsigned int cpu_id;
195};
196
Connor O'Brien12a2fca2020-02-14 21:09:29 -0800197DEFINE_BPF_PROG("tracepoint/power/cpu_frequency", AID_ROOT, AID_SYSTEM, tp_cpufreq)
198(struct cpufreq_args* args) {
Maciej Żenczykowski44ad5cb2019-04-19 21:46:26 -0700199 uint32_t cpu = args->cpu_id;
Joel Fernandesc5c05162018-10-16 13:26:56 -0700200 unsigned int new = args->state;
Connor O'Brien1c7b9c92019-06-06 17:36:52 -0700201 uint32_t* policyp = bpf_cpu_policy_map_lookup_elem(&cpu);
202 if (!policyp) return 0;
203 uint32_t policy = *policyp;
Connor O'Brien8acde7e2019-09-03 13:16:25 -0700204 freq_idx_key_t key = {.policy = policy, .freq = new};
Connor O'Brien55aea862019-06-07 16:58:16 -0700205 uint8_t* idxp = bpf_freq_to_idx_map_lookup_elem(&key);
206 if (!idxp) return 0;
207 uint8_t idx = *idxp;
208 bpf_policy_freq_idx_map_update_elem(&policy, &idx, BPF_ANY);
Joel Fernandesc5c05162018-10-16 13:26:56 -0700209 return 0;
210}
211
Dmitri Plotnikov9e581d52020-10-17 21:32:16 -0700212// The format of the sched/sched_process_free event is described in
213// adb shell cat /d/tracing/events/sched/sched_process_free/format
214struct sched_process_free_args {
215 unsigned long long ignore;
216 char comm[16];
217 pid_t pid;
218 int prio;
219};
220
221DEFINE_BPF_PROG("tracepoint/sched/sched_process_free", AID_ROOT, AID_SYSTEM, tp_sched_process_free)
222(struct sched_process_free_args* args) {
223 const int ALLOW = 1;
Dmitri Plotnikov6a651a32020-11-23 12:31:55 -0800224
Dmitri Plotnikov9e581d52020-10-17 21:32:16 -0700225 int pid = args->pid;
Dmitri Plotnikov6a651a32020-11-23 12:31:55 -0800226 bool is_last = true;
227
228 // eBPF verifier does not currently allow loops.
229 // Instruct the C compiler to unroll the loop into a series of steps.
230 #pragma unroll
231 for (uint32_t index = 0; index < MAX_TRACKED_PIDS; index++) {
232 const uint32_t key = MAX_TRACKED_PIDS - index - 1;
233 tracked_pid_t* tracked_pid = bpf_pid_tracked_map_lookup_elem(&key);
234 if (!tracked_pid) continue;
235 if (tracked_pid->pid == pid) {
236 tracked_pid->pid = 0;
237 tracked_pid->state = is_last ? TRACKED_PID_STATE_UNUSED : TRACKED_PID_STATE_EXITED;
238 bpf_pid_tracked_hash_map_delete_elem(&key);
239 break;
240 }
241 if (tracked_pid->state == TRACKED_PID_STATE_ACTIVE) {
242 is_last = false;
243 }
244 }
245
Dmitri Plotnikov9e581d52020-10-17 21:32:16 -0700246 bpf_pid_task_aggregation_map_delete_elem(&pid);
247 return ALLOW;
248}
249
Maciej Żenczykowskifb07bdc2020-02-21 11:07:14 -0800250LICENSE("GPL");