blob: e54817c215d39e039f10dea336e0a2a319b60ca2 [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
Joel Fernandes (Google)4134dbb2019-02-12 12:47:25 -050017#include <bpf_helpers.h>
Connor O'Brien8acde7e2019-09-03 13:16:25 -070018#include <bpf_timeinstate.h>
Joel Fernandesc5c05162018-10-16 13:26:56 -070019
Rafal Slawik3f140462021-01-05 18:58:08 +000020DEFINE_BPF_MAP_GRW(total_time_in_state_map, PERCPU_ARRAY, uint32_t, uint64_t, MAX_FREQS_FOR_TOTAL,
21 AID_SYSTEM)
22
Connor O'Brien12a2fca2020-02-14 21:09:29 -080023DEFINE_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 -070024
Connor O'Brien12a2fca2020-02-14 21:09:29 -080025DEFINE_BPF_MAP_GRW(uid_concurrent_times_map, PERCPU_HASH, time_key_t, concurrent_val_t, 1024, AID_SYSTEM)
26DEFINE_BPF_MAP_GRW(uid_last_update_map, HASH, uint32_t, uint64_t, 1024, AID_SYSTEM)
Connor O'Brien0dd29b82019-06-11 12:50:57 -070027
Maciej Żenczykowski3d1cf832020-02-18 14:28:56 -080028DEFINE_BPF_MAP_GWO(cpu_last_update_map, PERCPU_ARRAY, uint32_t, uint64_t, 1, AID_SYSTEM)
Joel Fernandesc5c05162018-10-16 13:26:56 -070029
Maciej Żenczykowski3d1cf832020-02-18 14:28:56 -080030DEFINE_BPF_MAP_GWO(cpu_policy_map, ARRAY, uint32_t, uint32_t, 1024, AID_SYSTEM)
31DEFINE_BPF_MAP_GWO(policy_freq_idx_map, ARRAY, uint32_t, uint8_t, 1024, AID_SYSTEM)
Connor O'Brien55aea862019-06-07 16:58:16 -070032
Maciej Żenczykowski3d1cf832020-02-18 14:28:56 -080033DEFINE_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 -070034
Maciej Żenczykowski3d1cf832020-02-18 14:28:56 -080035DEFINE_BPF_MAP_GWO(nr_active_map, ARRAY, uint32_t, uint32_t, 1, AID_SYSTEM)
36DEFINE_BPF_MAP_GWO(policy_nr_active_map, ARRAY, uint32_t, uint32_t, 1024, AID_SYSTEM)
Connor O'Brien0dd29b82019-06-11 12:50:57 -070037
Dmitri Plotnikov6a651a32020-11-23 12:31:55 -080038DEFINE_BPF_MAP_GWO(pid_tracked_hash_map, HASH, uint32_t, pid_t, MAX_TRACKED_PIDS, AID_SYSTEM)
39DEFINE_BPF_MAP_GWO(pid_tracked_map, ARRAY, uint32_t, tracked_pid_t, MAX_TRACKED_PIDS, AID_SYSTEM)
Dmitri Plotnikov9e581d52020-10-17 21:32:16 -070040DEFINE_BPF_MAP_GWO(pid_task_aggregation_map, HASH, pid_t, uint16_t, 1024, AID_SYSTEM)
41DEFINE_BPF_MAP_GRO(pid_time_in_state_map, PERCPU_HASH, aggregated_task_tis_key_t, tis_val_t, 1024,
42 AID_SYSTEM)
43
Joel Fernandesc5c05162018-10-16 13:26:56 -070044struct switch_args {
45 unsigned long long ignore;
46 char prev_comm[16];
47 int prev_pid;
48 int prev_prio;
49 long long prev_state;
50 char next_comm[16];
51 int next_pid;
52 int next_prio;
53};
54
Connor O'Brien12a2fca2020-02-14 21:09:29 -080055DEFINE_BPF_PROG("tracepoint/sched/sched_switch", AID_ROOT, AID_SYSTEM, tp_sched_switch)
56(struct switch_args* args) {
Yabin Cuif1880342020-02-27 17:11:58 -080057 const int ALLOW = 1; // return 1 to avoid blocking simpleperf from receiving events.
Joel Fernandesc5c05162018-10-16 13:26:56 -070058 uint32_t zero = 0;
Maciej Żenczykowski44ad5cb2019-04-19 21:46:26 -070059 uint64_t* last = bpf_cpu_last_update_map_lookup_elem(&zero);
Yabin Cuif1880342020-02-27 17:11:58 -080060 if (!last) return ALLOW;
Joel Fernandesc5c05162018-10-16 13:26:56 -070061 uint64_t old_last = *last;
62 uint64_t time = bpf_ktime_get_ns();
63 *last = time;
Connor O'Brien1c7b9c92019-06-06 17:36:52 -070064
Connor O'Brien0dd29b82019-06-11 12:50:57 -070065 uint32_t* active = bpf_nr_active_map_lookup_elem(&zero);
Yabin Cuif1880342020-02-27 17:11:58 -080066 if (!active) return ALLOW;
Connor O'Brien1c7b9c92019-06-06 17:36:52 -070067
Joel Fernandesc5c05162018-10-16 13:26:56 -070068 uint32_t cpu = bpf_get_smp_processor_id();
Connor O'Brien1c7b9c92019-06-06 17:36:52 -070069 uint32_t* policyp = bpf_cpu_policy_map_lookup_elem(&cpu);
Yabin Cuif1880342020-02-27 17:11:58 -080070 if (!policyp) return ALLOW;
Connor O'Brien1c7b9c92019-06-06 17:36:52 -070071 uint32_t policy = *policyp;
Connor O'Brien0dd29b82019-06-11 12:50:57 -070072
73 uint32_t* policy_active = bpf_policy_nr_active_map_lookup_elem(&policy);
Yabin Cuif1880342020-02-27 17:11:58 -080074 if (!policy_active) return ALLOW;
Connor O'Brien0dd29b82019-06-11 12:50:57 -070075
76 uint32_t nactive = *active - 1;
77 uint32_t policy_nactive = *policy_active - 1;
78
79 if (!args->prev_pid || (!old_last && args->next_pid)) {
80 __sync_fetch_and_add(active, 1);
81 __sync_fetch_and_add(policy_active, 1);
82 }
83
84 // Return here in 2 scenarios:
85 // 1) prev_pid == 0, so we're exiting idle. No UID stats need updating, and active CPUs can't be
86 // decreasing.
87 // 2) old_last == 0, so this is the first time we've seen this CPU. Any delta will be invalid,
88 // and our active CPU counts don't include this CPU yet so we shouldn't decrement them even
89 // if we're going idle.
Yabin Cuif1880342020-02-27 17:11:58 -080090 if (!args->prev_pid || !old_last) return ALLOW;
Connor O'Brien0dd29b82019-06-11 12:50:57 -070091
92 if (!args->next_pid) {
93 __sync_fetch_and_add(active, -1);
94 __sync_fetch_and_add(policy_active, -1);
95 }
96
Connor O'Brien55aea862019-06-07 16:58:16 -070097 uint8_t* freq_idxp = bpf_policy_freq_idx_map_lookup_elem(&policy);
Yabin Cuif1880342020-02-27 17:11:58 -080098 if (!freq_idxp || !*freq_idxp) return ALLOW;
Connor O'Brien55aea862019-06-07 16:58:16 -070099 // freq_to_idx_map uses 1 as its minimum index so that *freq_idxp == 0 only when uninitialized
100 uint8_t freq_idx = *freq_idxp - 1;
Connor O'Brien1c7b9c92019-06-06 17:36:52 -0700101
102 uint32_t uid = bpf_get_current_uid_gid();
Connor O'Brien8acde7e2019-09-03 13:16:25 -0700103 time_key_t key = {.uid = uid, .bucket = freq_idx / FREQS_PER_ENTRY};
104 tis_val_t* val = bpf_uid_time_in_state_map_lookup_elem(&key);
Connor O'Brien55aea862019-06-07 16:58:16 -0700105 if (!val) {
Connor O'Brien8acde7e2019-09-03 13:16:25 -0700106 tis_val_t zero_val = {.ar = {0}};
Connor O'Brien0dd29b82019-06-11 12:50:57 -0700107 bpf_uid_time_in_state_map_update_elem(&key, &zero_val, BPF_NOEXIST);
108 val = bpf_uid_time_in_state_map_lookup_elem(&key);
Connor O'Brien55aea862019-06-07 16:58:16 -0700109 }
Connor O'Brien1c7b9c92019-06-06 17:36:52 -0700110 uint64_t delta = time - old_last;
Connor O'Brien55aea862019-06-07 16:58:16 -0700111 if (val) val->ar[freq_idx % FREQS_PER_ENTRY] += delta;
Connor O'Brien0dd29b82019-06-11 12:50:57 -0700112
Rafal Slawik3f140462021-01-05 18:58:08 +0000113 // Add delta to total.
114 const uint32_t total_freq_idx = freq_idx < MAX_FREQS_FOR_TOTAL ? freq_idx :
115 MAX_FREQS_FOR_TOTAL - 1;
116 uint64_t* total = bpf_total_time_in_state_map_lookup_elem(&total_freq_idx);
117 if (total) *total += delta;
118
Dmitri Plotnikov9e581d52020-10-17 21:32:16 -0700119 const int pid = args->prev_pid;
120 const pid_t tgid = bpf_get_current_pid_tgid() >> 32;
Dmitri Plotnikov6a651a32020-11-23 12:31:55 -0800121 bool is_tgid_tracked = false;
122
123 // eBPF verifier does not currently allow loops.
124 // Instruct the C compiler to unroll the loop into a series of steps.
125 #pragma unroll
126 for (uint32_t index = 0; index < MAX_TRACKED_PIDS; index++) {
127 const uint32_t key = index;
128 tracked_pid_t* tracked_pid = bpf_pid_tracked_map_lookup_elem(&key);
129 if (!tracked_pid) continue;
130 if (tracked_pid->state == TRACKED_PID_STATE_UNUSED) {
131 // Reached the end of the list
132 break;
133 }
134
135 if (tracked_pid->state == TRACKED_PID_STATE_ACTIVE && tracked_pid->pid == tgid) {
136 is_tgid_tracked = true;
137 break;
138 }
139 }
140
141 if (is_tgid_tracked) {
Dmitri Plotnikov9e581d52020-10-17 21:32:16 -0700142 // If this process is marked for time-in-state tracking, aggregate the CPU time-in-state
143 // with other threads sharing the same TGID and aggregation key.
144 uint16_t* aggregation_key = bpf_pid_task_aggregation_map_lookup_elem(&pid);
145 aggregated_task_tis_key_t task_key = {
146 .tgid = tgid,
147 .aggregation_key = aggregation_key ? *aggregation_key : 0,
148 .bucket = freq_idx / FREQS_PER_ENTRY};
149 tis_val_t* task_val = bpf_pid_time_in_state_map_lookup_elem(&task_key);
150 if (!task_val) {
151 tis_val_t zero_val = {.ar = {0}};
152 bpf_pid_time_in_state_map_update_elem(&task_key, &zero_val, BPF_NOEXIST);
153 task_val = bpf_pid_time_in_state_map_lookup_elem(&task_key);
154 }
155 if (task_val) task_val->ar[freq_idx % FREQS_PER_ENTRY] += delta;
156 }
Connor O'Brien0dd29b82019-06-11 12:50:57 -0700157 key.bucket = nactive / CPUS_PER_ENTRY;
Connor O'Brien8acde7e2019-09-03 13:16:25 -0700158 concurrent_val_t* ct = bpf_uid_concurrent_times_map_lookup_elem(&key);
Connor O'Brien0dd29b82019-06-11 12:50:57 -0700159 if (!ct) {
Connor O'Brien8acde7e2019-09-03 13:16:25 -0700160 concurrent_val_t zero_val = {.active = {0}, .policy = {0}};
Connor O'Brien0dd29b82019-06-11 12:50:57 -0700161 bpf_uid_concurrent_times_map_update_elem(&key, &zero_val, BPF_NOEXIST);
162 ct = bpf_uid_concurrent_times_map_lookup_elem(&key);
163 }
164 if (ct) ct->active[nactive % CPUS_PER_ENTRY] += delta;
165
166 if (policy_nactive / CPUS_PER_ENTRY != key.bucket) {
167 key.bucket = policy_nactive / CPUS_PER_ENTRY;
168 ct = bpf_uid_concurrent_times_map_lookup_elem(&key);
169 if (!ct) {
Connor O'Brien8acde7e2019-09-03 13:16:25 -0700170 concurrent_val_t zero_val = {.active = {0}, .policy = {0}};
Connor O'Brien0dd29b82019-06-11 12:50:57 -0700171 bpf_uid_concurrent_times_map_update_elem(&key, &zero_val, BPF_NOEXIST);
172 ct = bpf_uid_concurrent_times_map_lookup_elem(&key);
173 }
174 }
175 if (ct) ct->policy[policy_nactive % CPUS_PER_ENTRY] += delta;
Connor O'Brien210aab52020-01-31 19:23:59 -0800176 uint64_t* uid_last_update = bpf_uid_last_update_map_lookup_elem(&uid);
177 if (uid_last_update) {
178 *uid_last_update = time;
179 } else {
180 bpf_uid_last_update_map_update_elem(&uid, &time, BPF_NOEXIST);
181 }
Yabin Cuif1880342020-02-27 17:11:58 -0800182 return ALLOW;
Joel Fernandesc5c05162018-10-16 13:26:56 -0700183}
184
185struct cpufreq_args {
186 unsigned long long ignore;
187 unsigned int state;
188 unsigned int cpu_id;
189};
190
Connor O'Brien12a2fca2020-02-14 21:09:29 -0800191DEFINE_BPF_PROG("tracepoint/power/cpu_frequency", AID_ROOT, AID_SYSTEM, tp_cpufreq)
192(struct cpufreq_args* args) {
Maciej Żenczykowski44ad5cb2019-04-19 21:46:26 -0700193 uint32_t cpu = args->cpu_id;
Joel Fernandesc5c05162018-10-16 13:26:56 -0700194 unsigned int new = args->state;
Connor O'Brien1c7b9c92019-06-06 17:36:52 -0700195 uint32_t* policyp = bpf_cpu_policy_map_lookup_elem(&cpu);
196 if (!policyp) return 0;
197 uint32_t policy = *policyp;
Connor O'Brien8acde7e2019-09-03 13:16:25 -0700198 freq_idx_key_t key = {.policy = policy, .freq = new};
Connor O'Brien55aea862019-06-07 16:58:16 -0700199 uint8_t* idxp = bpf_freq_to_idx_map_lookup_elem(&key);
200 if (!idxp) return 0;
201 uint8_t idx = *idxp;
202 bpf_policy_freq_idx_map_update_elem(&policy, &idx, BPF_ANY);
Joel Fernandesc5c05162018-10-16 13:26:56 -0700203 return 0;
204}
205
Dmitri Plotnikov9e581d52020-10-17 21:32:16 -0700206// The format of the sched/sched_process_free event is described in
207// adb shell cat /d/tracing/events/sched/sched_process_free/format
208struct sched_process_free_args {
209 unsigned long long ignore;
210 char comm[16];
211 pid_t pid;
212 int prio;
213};
214
215DEFINE_BPF_PROG("tracepoint/sched/sched_process_free", AID_ROOT, AID_SYSTEM, tp_sched_process_free)
216(struct sched_process_free_args* args) {
217 const int ALLOW = 1;
Dmitri Plotnikov6a651a32020-11-23 12:31:55 -0800218
Dmitri Plotnikov9e581d52020-10-17 21:32:16 -0700219 int pid = args->pid;
Dmitri Plotnikov6a651a32020-11-23 12:31:55 -0800220 bool is_last = true;
221
222 // eBPF verifier does not currently allow loops.
223 // Instruct the C compiler to unroll the loop into a series of steps.
224 #pragma unroll
225 for (uint32_t index = 0; index < MAX_TRACKED_PIDS; index++) {
226 const uint32_t key = MAX_TRACKED_PIDS - index - 1;
227 tracked_pid_t* tracked_pid = bpf_pid_tracked_map_lookup_elem(&key);
228 if (!tracked_pid) continue;
229 if (tracked_pid->pid == pid) {
230 tracked_pid->pid = 0;
231 tracked_pid->state = is_last ? TRACKED_PID_STATE_UNUSED : TRACKED_PID_STATE_EXITED;
232 bpf_pid_tracked_hash_map_delete_elem(&key);
233 break;
234 }
235 if (tracked_pid->state == TRACKED_PID_STATE_ACTIVE) {
236 is_last = false;
237 }
238 }
239
Dmitri Plotnikov9e581d52020-10-17 21:32:16 -0700240 bpf_pid_task_aggregation_map_delete_elem(&pid);
241 return ALLOW;
242}
243
Maciej Żenczykowskifb07bdc2020-02-21 11:07:14 -0800244LICENSE("GPL");