forked from iovisor/bcc
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request iovisor#3797 from Rtoax/patch-8
add slabratetop libbpf-tools
- Loading branch information
Showing
5 changed files
with
375 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -39,6 +39,7 @@ | |
/runqlat | ||
/runqlen | ||
/runqslower | ||
/slabratetop | ||
/softirqs | ||
/solisten | ||
/statsnoop | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -52,6 +52,7 @@ APPS = \ | |
runqlat \ | ||
runqlen \ | ||
runqslower \ | ||
slabratetop \ | ||
softirqs \ | ||
solisten \ | ||
statsnoop \ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ | ||
/* Copyright (c) 2022 Rong Tao */ | ||
#include <vmlinux.h> | ||
#include <bpf/bpf_helpers.h> | ||
#include <bpf/bpf_core_read.h> | ||
#include <bpf/bpf_tracing.h> | ||
#include "slabratetop.h" | ||
|
||
#define MAX_ENTRIES 10240 | ||
|
||
const volatile pid_t target_pid = 0; | ||
|
||
static struct slabrate_info slab_zero_value = {}; | ||
|
||
struct { | ||
__uint(type, BPF_MAP_TYPE_HASH); | ||
__uint(max_entries, MAX_ENTRIES); | ||
__type(key, char *); | ||
__type(value, struct slabrate_info); | ||
} slab_entries SEC(".maps"); | ||
|
||
static int probe_entry(struct kmem_cache *cachep) | ||
{ | ||
__u64 pid_tgid = bpf_get_current_pid_tgid(); | ||
__u32 pid = pid_tgid >> 32; | ||
struct slabrate_info *valuep; | ||
const char *name = BPF_CORE_READ(cachep, name); | ||
|
||
if (target_pid && target_pid != pid) | ||
return 0; | ||
|
||
valuep = bpf_map_lookup_elem(&slab_entries, &name); | ||
if (!valuep) { | ||
bpf_map_update_elem(&slab_entries, &name, &slab_zero_value, BPF_ANY); | ||
valuep = bpf_map_lookup_elem(&slab_entries, &name); | ||
if (!valuep) | ||
return 0; | ||
bpf_probe_read_kernel(&valuep->name, sizeof(valuep->name), name); | ||
} | ||
|
||
valuep->count++; | ||
valuep->size += BPF_CORE_READ(cachep, size); | ||
|
||
return 0; | ||
} | ||
|
||
SEC("kprobe/kmem_cache_alloc") | ||
int BPF_KPROBE(kmem_cache_alloc, struct kmem_cache *cachep) | ||
{ | ||
return probe_entry(cachep); | ||
} | ||
|
||
char LICENSE[] SEC("license") = "Dual BSD/GPL"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,307 @@ | ||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ | ||
|
||
/* | ||
* slabratetop Trace slab kmem_cache_alloc by process. | ||
* Copyright (c) 2022 Rong Tao | ||
* | ||
* Based on slabratetop(8) from BCC by Brendan Gregg. | ||
* 07-Jan-2022 Rong Tao Created this. | ||
*/ | ||
#include <argp.h> | ||
#include <errno.h> | ||
#include <signal.h> | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include <time.h> | ||
#include <unistd.h> | ||
|
||
#include <bpf/libbpf.h> | ||
#include <bpf/bpf.h> | ||
#include "slabratetop.h" | ||
#include "slabratetop.skel.h" | ||
#include "trace_helpers.h" | ||
|
||
#define warn(...) fprintf(stderr, __VA_ARGS__) | ||
#define OUTPUT_ROWS_LIMIT 10240 | ||
|
||
enum SORT_BY { | ||
SORT_BY_CACHE_NAME, | ||
SORT_BY_CACHE_COUNT, | ||
SORT_BY_CACHE_SIZE, | ||
}; | ||
|
||
static volatile sig_atomic_t exiting = 0; | ||
|
||
static pid_t target_pid = 0; | ||
static bool clear_screen = true; | ||
static int output_rows = 20; | ||
static int sort_by = SORT_BY_CACHE_SIZE; | ||
static int interval = 1; | ||
static int count = 99999999; | ||
static bool verbose = false; | ||
|
||
const char *argp_program_version = "slabratetop 0.1"; | ||
const char *argp_program_bug_address = | ||
"https://github.com/iovisor/bcc/tree/master/libbpf-tools"; | ||
const char argp_program_doc[] = | ||
"Trace slab kmem cache alloc by process.\n" | ||
"\n" | ||
"USAGE: slabratetop [-h] [-p PID] [interval] [count]\n" | ||
"\n" | ||
"EXAMPLES:\n" | ||
" slabratetop # slab rate top, refresh every 1s\n" | ||
" slabratetop -p 181 # only trace PID 181\n" | ||
" slabratetop -s count # sort columns by count\n" | ||
" slabratetop -r 100 # print 100 rows\n" | ||
" slabratetop 5 10 # 5s summaries, 10 times\n"; | ||
|
||
static const struct argp_option opts[] = { | ||
{ "pid", 'p', "PID", 0, "Process ID to trace" }, | ||
{ "noclear", 'C', NULL, 0, "Don't clear the screen" }, | ||
{ "sort", 's', "SORT", 0, "Sort columns, default size [name, count, size]" }, | ||
{ "rows", 'r', "ROWS", 0, "Maximum rows to print, default 20" }, | ||
{ "verbose", 'v', NULL, 0, "Verbose debug output" }, | ||
{ NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help" }, | ||
{}, | ||
}; | ||
|
||
static error_t parse_arg(int key, char *arg, struct argp_state *state) | ||
{ | ||
long pid, rows; | ||
static int pos_args; | ||
|
||
switch (key) { | ||
case 'p': | ||
errno = 0; | ||
pid = strtol(arg, NULL, 10); | ||
if (errno || pid <= 0) { | ||
warn("invalid PID: %s\n", arg); | ||
argp_usage(state); | ||
} | ||
target_pid = pid; | ||
break; | ||
case 'C': | ||
clear_screen = false; | ||
break; | ||
case 's': | ||
if (!strcmp(arg, "name")) { | ||
sort_by = SORT_BY_CACHE_NAME; | ||
} else if (!strcmp(arg, "count")) { | ||
sort_by = SORT_BY_CACHE_COUNT; | ||
} else if (!strcmp(arg, "size")) { | ||
sort_by = SORT_BY_CACHE_SIZE; | ||
} else { | ||
warn("invalid sort method: %s\n", arg); | ||
argp_usage(state); | ||
} | ||
break; | ||
case 'r': | ||
errno = 0; | ||
rows = strtol(arg, NULL, 10); | ||
if (errno || rows <= 0) { | ||
warn("invalid rows: %s\n", arg); | ||
argp_usage(state); | ||
} | ||
output_rows = rows; | ||
if (output_rows > OUTPUT_ROWS_LIMIT) | ||
output_rows = OUTPUT_ROWS_LIMIT; | ||
break; | ||
case 'v': | ||
verbose = true; | ||
break; | ||
case 'h': | ||
argp_state_help(state, stderr, ARGP_HELP_STD_HELP); | ||
break; | ||
case ARGP_KEY_ARG: | ||
errno = 0; | ||
if (pos_args == 0) { | ||
interval = strtol(arg, NULL, 10); | ||
if (errno || interval <= 0) { | ||
warn("invalid interval\n"); | ||
argp_usage(state); | ||
} | ||
} else if (pos_args == 1) { | ||
count = strtol(arg, NULL, 10); | ||
if (errno || count <= 0) { | ||
warn("invalid count\n"); | ||
argp_usage(state); | ||
} | ||
} else { | ||
warn("unrecognized positional argument: %s\n", arg); | ||
argp_usage(state); | ||
} | ||
pos_args++; | ||
break; | ||
default: | ||
return ARGP_ERR_UNKNOWN; | ||
} | ||
return 0; | ||
} | ||
|
||
static int libbpf_print_fn(enum libbpf_print_level level, const char *format, va_list args) | ||
{ | ||
if (level == LIBBPF_DEBUG && !verbose) | ||
return 0; | ||
return vfprintf(stderr, format, args); | ||
} | ||
|
||
static void sig_int(int signo) | ||
{ | ||
exiting = 1; | ||
} | ||
|
||
static int sort_column(const void *obj1, const void *obj2) | ||
{ | ||
struct slabrate_info *s1 = (struct slabrate_info *)obj1; | ||
struct slabrate_info *s2 = (struct slabrate_info *)obj2; | ||
|
||
if (sort_by == SORT_BY_CACHE_NAME) { | ||
return strcasecmp(s1->name, s2->name); | ||
} else if (sort_by == SORT_BY_CACHE_COUNT) { | ||
return s2->count - s1->count; | ||
} else if (sort_by == SORT_BY_CACHE_SIZE) { | ||
return s2->size - s1->size; | ||
} else { | ||
return s2->size - s1->size; | ||
} | ||
} | ||
|
||
static int print_stat(struct slabratetop_bpf *obj) | ||
{ | ||
FILE *f; | ||
time_t t; | ||
struct tm *tm; | ||
char ts[16], buf[256]; | ||
char *key, **prev_key = NULL; | ||
static struct slabrate_info values[OUTPUT_ROWS_LIMIT]; | ||
int n, i, err = 0, rows = 0; | ||
int fd = bpf_map__fd(obj->maps.slab_entries); | ||
|
||
f = fopen("/proc/loadavg", "r"); | ||
if (f) { | ||
time(&t); | ||
tm = localtime(&t); | ||
strftime(ts, sizeof(ts), "%H:%M:%S", tm); | ||
memset(buf, 0 , sizeof(buf)); | ||
n = fread(buf, 1, sizeof(buf), f); | ||
if (n) | ||
printf("%8s loadavg: %s\n", ts, buf); | ||
fclose(f); | ||
} | ||
|
||
printf("%-32s %6s %10s\n", "CACHE", "ALLOCS", "BYTES"); | ||
|
||
while (1) { | ||
err = bpf_map_get_next_key(fd, prev_key, &key); | ||
if (err) { | ||
if (errno == ENOENT) { | ||
err = 0; | ||
break; | ||
} | ||
warn("bpf_map_get_next_key failed: %s\n", strerror(errno)); | ||
return err; | ||
} | ||
err = bpf_map_lookup_elem(fd, &key, &values[rows++]); | ||
if (err) { | ||
warn("bpf_map_lookup_elem failed: %s\n", strerror(errno)); | ||
return err; | ||
} | ||
prev_key = &key; | ||
} | ||
|
||
qsort(values, rows, sizeof(struct slabrate_info), sort_column); | ||
rows = rows < output_rows ? rows : output_rows; | ||
for (i = 0; i < rows; i++) | ||
printf("%-32s %6lld %10lld\n", | ||
values[i].name, values[i].count, values[i].size); | ||
|
||
printf("\n"); | ||
prev_key = NULL; | ||
|
||
while (1) { | ||
err = bpf_map_get_next_key(fd, prev_key, &key); | ||
if (err) { | ||
if (errno == ENOENT) { | ||
err = 0; | ||
break; | ||
} | ||
warn("bpf_map_get_next_key failed: %s\n", strerror(errno)); | ||
return err; | ||
} | ||
err = bpf_map_delete_elem(fd, &key); | ||
if (err) { | ||
warn("bpf_map_delete_elem failed: %s\n", strerror(errno)); | ||
return err; | ||
} | ||
prev_key = &key; | ||
} | ||
return err; | ||
} | ||
|
||
int main(int argc, char **argv) | ||
{ | ||
static const struct argp argp = { | ||
.options = opts, | ||
.parser = parse_arg, | ||
.doc = argp_program_doc, | ||
}; | ||
struct slabratetop_bpf *obj; | ||
int err; | ||
|
||
err = argp_parse(&argp, argc, argv, 0, NULL, NULL); | ||
if (err) | ||
return err; | ||
|
||
libbpf_set_strict_mode(LIBBPF_STRICT_ALL); | ||
libbpf_set_print(libbpf_print_fn); | ||
|
||
obj = slabratetop_bpf__open(); | ||
if (!obj) { | ||
warn("failed to open BPF object\n"); | ||
return 1; | ||
} | ||
|
||
obj->rodata->target_pid = target_pid; | ||
|
||
err = slabratetop_bpf__load(obj); | ||
if (err) { | ||
warn("failed to load BPF object: %d\n", err); | ||
goto cleanup; | ||
} | ||
|
||
err = slabratetop_bpf__attach(obj); | ||
if (err) { | ||
warn("failed to attach BPF programs: %d\n", err); | ||
goto cleanup; | ||
} | ||
|
||
if (signal(SIGINT, sig_int) == SIG_ERR) { | ||
warn("can't set signal handler: %s\n", strerror(errno)); | ||
err = 1; | ||
goto cleanup; | ||
} | ||
|
||
while (1) { | ||
sleep(interval); | ||
|
||
if (clear_screen) { | ||
err = system("clear"); | ||
if (err) | ||
goto cleanup; | ||
} | ||
|
||
err = print_stat(obj); | ||
if (err) | ||
goto cleanup; | ||
|
||
count--; | ||
if (exiting || !count) | ||
goto cleanup; | ||
} | ||
|
||
cleanup: | ||
slabratetop_bpf__destroy(obj); | ||
|
||
return err != 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */ | ||
#ifndef __SLABRATETOP_H | ||
#define __SLABRATETOP_H | ||
|
||
#define CACHE_NAME_SIZE 32 | ||
|
||
struct slabrate_info { | ||
char name[CACHE_NAME_SIZE]; | ||
__u64 count; | ||
__u64 size; | ||
}; | ||
|
||
#endif /* __SLABRATETOP_H */ |