Skip to content

Commit a9f9769

Browse files
ethercflowyonghong-song
authored andcommitted
libbpf-tools: add cpufreq
Signed-off-by: Wenbo Zhang <[email protected]>
1 parent 0d9b5b8 commit a9f9769

8 files changed

+346
-7
lines changed

libbpf-tools/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
/biostacks
66
/bitesize
77
/cpudist
8+
/cpufreq
89
/drsnoop
910
/execsnoop
1011
/filelife

libbpf-tools/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ APPS = \
1616
biostacks \
1717
bitesize \
1818
cpudist \
19+
cpufreq \
1920
drsnoop \
2021
execsnoop \
2122
filelife \

libbpf-tools/cpufreq.bpf.c

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
// Copyright (c) 2020 Wenbo Zhang
3+
#include "vmlinux.h"
4+
#include <bpf/bpf_helpers.h>
5+
#include <bpf/bpf_tracing.h>
6+
#include "cpufreq.h"
7+
#include "maps.bpf.h"
8+
9+
__u32 freqs_mhz[MAX_CPU_NR] = {};
10+
static struct hist zero;
11+
struct hist syswide = {};
12+
13+
struct {
14+
__uint(type, BPF_MAP_TYPE_HASH);
15+
__uint(max_entries, MAX_ENTRIES);
16+
__type(key, struct hkey);
17+
__type(value, struct hist);
18+
} hists SEC(".maps");
19+
20+
SEC("tp_btf/cpu_frequency")
21+
int BPF_PROG(cpu_frequency, unsigned int state, unsigned int cpu_id)
22+
{
23+
if (cpu_id >= MAX_CPU_NR)
24+
return 0;
25+
freqs_mhz[cpu_id] = state / 1000;
26+
return 0;
27+
}
28+
29+
SEC("perf_event")
30+
int do_sample(struct bpf_perf_event_data *ctx)
31+
{
32+
u32 freq_mhz, pid = bpf_get_current_pid_tgid();
33+
u64 slot, cpu = bpf_get_smp_processor_id();
34+
struct hist *hist;
35+
struct hkey hkey;
36+
37+
if (cpu >= MAX_CPU_NR)
38+
return 0;
39+
freq_mhz = freqs_mhz[cpu];
40+
if (!freq_mhz)
41+
return 0;
42+
/*
43+
* The range of the linear histogram is 0 ~ 5000mhz,
44+
* and the step size is 200.
45+
*/
46+
slot = freq_mhz / HIST_STEP_SIZE;
47+
if (slot >= MAX_SLOTS)
48+
slot = MAX_SLOTS - 1;
49+
__sync_fetch_and_add(&syswide.slots[slot], 1);
50+
if (!pid)
51+
return 0;
52+
bpf_get_current_comm(&hkey.comm, sizeof(hkey.comm));
53+
hist = bpf_map_lookup_or_try_init(&hists, &hkey, &zero);
54+
if (!hist)
55+
return 0;
56+
__sync_fetch_and_add(&hist->slots[slot], 1);
57+
return 0;
58+
}
59+
60+
char LICENSE[] SEC("license") = "GPL";

libbpf-tools/cpufreq.c

+253
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
2+
// Copyright (c) 2020 Wenbo Zhang
3+
//
4+
// Based on cpufreq(8) from BPF-Perf-Tools-Book by Brendan Gregg.
5+
// 10-OCT-2020 Wenbo Zhang Created this.
6+
#include <argp.h>
7+
#include <signal.h>
8+
#include <stdio.h>
9+
#include <stdlib.h>
10+
#include <string.h>
11+
#include <unistd.h>
12+
#include <linux/perf_event.h>
13+
#include <asm/unistd.h>
14+
#include <bpf/libbpf.h>
15+
#include <bpf/bpf.h>
16+
#include "cpufreq.h"
17+
#include "cpufreq.skel.h"
18+
#include "trace_helpers.h"
19+
20+
static struct env {
21+
int duration;
22+
int freq;
23+
bool verbose;
24+
} env = {
25+
.duration = -1,
26+
.freq = 99,
27+
};
28+
29+
const char *argp_program_version = "cpufreq 0.1";
30+
const char *argp_program_bug_address = "<[email protected]>";
31+
const char argp_program_doc[] =
32+
"Sampling CPU freq system-wide & by process. Ctrl-C to end.\n"
33+
"\n"
34+
"USAGE: cpufreq [--help] [-d DURATION] [-f FREQUENCY]\n"
35+
"\n"
36+
"EXAMPLES:\n"
37+
" cpufreq # sample CPU freq at 99HZ (default)\n"
38+
" cpufreq -d 5 # sample for 5 seconds only\n"
39+
" cpufreq -f 199 # sample CPU freq at 199HZ\n";
40+
41+
static const struct argp_option opts[] = {
42+
{ "duration", 'd', "DURATION", 0, "Duration to sample in seconds" },
43+
{ "frequency", 'f', "FREQUENCY", 0, "Sample with a certain frequency" },
44+
{ "verbose", 'v', NULL, 0, "Verbose debug output" },
45+
{},
46+
};
47+
48+
static error_t parse_arg(int key, char *arg, struct argp_state *state)
49+
{
50+
switch (key) {
51+
case 'v':
52+
env.verbose = true;
53+
break;
54+
case 'd':
55+
errno = 0;
56+
env.duration = strtol(arg, NULL, 10);
57+
if (errno || env.duration <= 0) {
58+
fprintf(stderr, "Invalid duration: %s\n", arg);
59+
argp_usage(state);
60+
}
61+
break;
62+
case 'f':
63+
errno = 0;
64+
env.freq = strtol(arg, NULL, 10);
65+
if (errno || env.freq <= 0) {
66+
fprintf(stderr, "Invalid freq (in HZ): %s\n", arg);
67+
argp_usage(state);
68+
}
69+
break;
70+
default:
71+
return ARGP_ERR_UNKNOWN;
72+
}
73+
return 0;
74+
}
75+
76+
static int nr_cpus;
77+
78+
static int open_and_attach_perf_event(int freq, struct bpf_program *prog,
79+
struct bpf_link *links[])
80+
{
81+
struct perf_event_attr attr = {
82+
.type = PERF_TYPE_SOFTWARE,
83+
.freq = 1,
84+
.sample_period = freq,
85+
.config = PERF_COUNT_SW_CPU_CLOCK,
86+
};
87+
int i, fd;
88+
89+
for (i = 0; i < nr_cpus; i++) {
90+
fd = syscall(__NR_perf_event_open, &attr, -1, i, -1, 0);
91+
if (fd < 0) {
92+
/* Ignore CPU that is offline */
93+
if (errno == ENODEV)
94+
continue;
95+
fprintf(stderr, "failed to init perf sampling: %s\n",
96+
strerror(errno));
97+
return -1;
98+
}
99+
links[i] = bpf_program__attach_perf_event(prog, fd);
100+
if (libbpf_get_error(links[i])) {
101+
fprintf(stderr, "failed to attach perf event on cpu: "
102+
"%d\n", i);
103+
links[i] = NULL;
104+
close(fd);
105+
return -1;
106+
}
107+
}
108+
109+
return 0;
110+
}
111+
112+
int libbpf_print_fn(enum libbpf_print_level level,
113+
const char *format, va_list args)
114+
{
115+
if (level == LIBBPF_DEBUG && !env.verbose)
116+
return 0;
117+
return vfprintf(stderr, format, args);
118+
}
119+
120+
static void sig_handler(int sig)
121+
{
122+
}
123+
124+
static int init_freqs_hmz(__u32 *freqs_mhz, int nr_cpus)
125+
{
126+
char path[64];
127+
FILE *f;
128+
int i;
129+
130+
for (i = 0; i < nr_cpus; i++) {
131+
snprintf(path, sizeof(path),
132+
"/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq",
133+
i);
134+
135+
f = fopen(path, "r");
136+
if (!f) {
137+
fprintf(stderr, "failed to open '%s': %s\n", path,
138+
strerror(errno));
139+
return -1;
140+
}
141+
if (fscanf(f, "%u\n", &freqs_mhz[i]) != 1) {
142+
fprintf(stderr, "failed to parse '%s': %s\n", path,
143+
strerror(errno));
144+
fclose(f);
145+
return -1;
146+
}
147+
fclose(f);
148+
}
149+
150+
return 0;
151+
}
152+
153+
static void print_linear_hists(struct bpf_map *hists,
154+
struct cpufreq_bpf__bss *bss)
155+
{
156+
struct hkey lookup_key = {}, next_key;
157+
int err, fd = bpf_map__fd(hists);
158+
struct hist hist;
159+
160+
while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) {
161+
err = bpf_map_lookup_elem(fd, &next_key, &hist);
162+
if (err < 0) {
163+
fprintf(stderr, "failed to lookup hist: %d\n", err);
164+
return;
165+
}
166+
print_linear_hist(hist.slots, MAX_SLOTS, 0, HIST_STEP_SIZE,
167+
next_key.comm);
168+
printf("\n");
169+
lookup_key = next_key;
170+
}
171+
172+
printf("\n");
173+
print_linear_hist(bss->syswide.slots, MAX_SLOTS, 0, HIST_STEP_SIZE,
174+
"syswide");
175+
}
176+
177+
int main(int argc, char **argv)
178+
{
179+
static const struct argp argp = {
180+
.options = opts,
181+
.parser = parse_arg,
182+
.doc = argp_program_doc,
183+
};
184+
struct bpf_link *links[MAX_CPU_NR] = {};
185+
struct cpufreq_bpf *obj;
186+
int err, i;
187+
188+
err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
189+
if (err)
190+
return err;
191+
192+
libbpf_set_print(libbpf_print_fn);
193+
194+
err = bump_memlock_rlimit();
195+
if (err) {
196+
fprintf(stderr, "failed to increase rlimit: %d\n", err);
197+
return 1;
198+
}
199+
200+
nr_cpus = libbpf_num_possible_cpus();
201+
if (nr_cpus < 0) {
202+
fprintf(stderr, "failed to get # of possible cpus: '%s'!\n",
203+
strerror(-nr_cpus));
204+
return 1;
205+
}
206+
if (nr_cpus > MAX_CPU_NR) {
207+
fprintf(stderr, "the number of cpu cores is too big, please "
208+
"increase MAX_CPU_NR's value and recompile");
209+
return 1;
210+
}
211+
212+
obj = cpufreq_bpf__open_and_load();
213+
if (!obj) {
214+
fprintf(stderr, "failed to open and/or load BPF object\n");
215+
return 1;
216+
}
217+
218+
err = init_freqs_hmz(obj->bss->freqs_mhz, nr_cpus);
219+
if (err) {
220+
fprintf(stderr, "failed to init freqs\n");
221+
goto cleanup;
222+
}
223+
224+
err = open_and_attach_perf_event(env.freq, obj->progs.do_sample, links);
225+
if (err)
226+
goto cleanup;
227+
228+
err = cpufreq_bpf__attach(obj);
229+
if (err) {
230+
fprintf(stderr, "failed to attach BPF programs\n");
231+
goto cleanup;
232+
}
233+
234+
printf("Sampling CPU freq system-wide & by process. Ctrl-C to end.\n");
235+
236+
signal(SIGINT, sig_handler);
237+
238+
/*
239+
* We'll get sleep interrupted when someone presses Ctrl-C (which will
240+
* be "handled" with noop by sig_handler).
241+
*/
242+
sleep(env.duration);
243+
printf("\n");
244+
245+
print_linear_hists(obj->maps.hists, obj->bss);
246+
247+
cleanup:
248+
for (i = 0; i < nr_cpus; i++)
249+
bpf_link__destroy(links[i]);
250+
cpufreq_bpf__destroy(obj);
251+
252+
return err != 0;
253+
}

libbpf-tools/cpufreq.h

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
2+
#ifndef __CPUFREQ_H
3+
#define __CPUFREQ_H
4+
5+
#define MAX_ENTRIES 1024
6+
#define MAX_CPU_NR 128
7+
#define MAX_SLOTS 26
8+
#define TASK_COMM_LEN 16
9+
#define HIST_STEP_SIZE 200
10+
11+
struct hkey {
12+
char comm[TASK_COMM_LEN];
13+
};
14+
15+
struct hist {
16+
__u32 slots[MAX_SLOTS];
17+
};
18+
19+
#endif /* __CPUFREQ_H */

libbpf-tools/runqlen.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ static void print_linear_hists(struct runqlen_bpf__bss *bss)
192192
bss->hists[i] = zero;
193193
if (env.per_cpu)
194194
printf("cpu = %d\n", i);
195-
print_linear_hist(hist.slots, MAX_SLOTS, "runqlen");
195+
print_linear_hist(hist.slots, MAX_SLOTS, 0, 1, "runqlen");
196196
} while (env.per_cpu && ++i < nr_cpus);
197197
}
198198

libbpf-tools/trace_helpers.c

+9-5
Original file line numberDiff line numberDiff line change
@@ -327,15 +327,19 @@ void print_log2_hist(unsigned int *vals, int vals_size, const char *val_type)
327327
}
328328
}
329329

330-
void print_linear_hist(unsigned int *vals, int vals_size, const char *val_type)
330+
void print_linear_hist(unsigned int *vals, int vals_size, unsigned int base,
331+
unsigned int step, const char *val_type)
331332
{
332-
int i, stars_max = 40, idx_max = -1;
333+
int i, stars_max = 40, idx_min = -1, idx_max = -1;
333334
unsigned int val, val_max = 0;
334335

335336
for (i = 0; i < vals_size; i++) {
336337
val = vals[i];
337-
if (val > 0)
338+
if (val > 0) {
338339
idx_max = i;
340+
if (idx_min < 0)
341+
idx_min = i;
342+
}
339343
if (val > val_max)
340344
val_max = val;
341345
}
@@ -344,9 +348,9 @@ void print_linear_hist(unsigned int *vals, int vals_size, const char *val_type)
344348
return;
345349

346350
printf(" %-13s : count distribution\n", val_type);
347-
for (i = 0; i <= idx_max; i++) {
351+
for (i = idx_min; i <= idx_max; i++) {
348352
val = vals[i];
349-
printf(" %-10d : %-8d |", i, val);
353+
printf(" %-10d : %-8d |", base + i * step, val);
350354
print_stars(val, val_max, stars_max);
351355
printf("|\n");
352356
}

0 commit comments

Comments
 (0)