Skip to content

Commit

Permalink
libbpf-tools: add CO-RE bitesize
Browse files Browse the repository at this point in the history
Signed-off-by: Wenbo Zhang <ethercflow@gmail.com>
  • Loading branch information
ethercflow authored and yonghong-song committed Jun 23, 2020
1 parent 0d9e091 commit 34f8985
Show file tree
Hide file tree
Showing 7 changed files with 324 additions and 23 deletions.
1 change: 1 addition & 0 deletions libbpf-tools/.gitignore
@@ -1,4 +1,5 @@
/.output
/bitesize
/cpudist
/drsnoop
/execsnoop
Expand Down
1 change: 1 addition & 0 deletions libbpf-tools/Makefile
Expand Up @@ -10,6 +10,7 @@ CFLAGS := -g -O2 -Wall
ARCH := $(shell uname -m | sed 's/x86_64/x86/')

APPS = \
bitesize \
cpudist \
drsnoop \
execsnoop \
Expand Down
59 changes: 59 additions & 0 deletions libbpf-tools/bitesize.bpf.c
@@ -0,0 +1,59 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (c) 2020 Wenbo Zhang
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
#include "bitesize.h"
#include "bits.bpf.h"

const volatile char targ_comm[TASK_COMM_LEN] = {};

struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 10240);
__type(key, struct hist_key);
__type(value, struct hist);
__uint(map_flags, BPF_F_NO_PREALLOC);
} hists SEC(".maps");

static struct hist initial_hist;

static __always_inline bool comm_filtered(const char *comm)
{
int i;

for (i = 0; targ_comm[i] != '\0' && i < TASK_COMM_LEN; i++) {
if (comm[i] != targ_comm[i])
return false;
}
return true;
}

SEC("tp_btf/block_rq_issue")
int BPF_PROG(tp_btf__block_rq_issue, struct request_queue *q,
struct request *rq)
{
struct hist_key hkey;
struct hist *histp;
u64 slot;

bpf_get_current_comm(&hkey.comm, sizeof(hkey.comm));
if (!comm_filtered(hkey.comm))
return 0;

histp = bpf_map_lookup_elem(&hists, &hkey);
if (!histp) {
bpf_map_update_elem(&hists, &hkey, &initial_hist, 0);
histp = bpf_map_lookup_elem(&hists, &hkey);
if (!histp)
return 0;
}
slot = log2l(rq->__data_len / 1024);
if (slot >= MAX_SLOTS)
slot = MAX_SLOTS - 1;
__sync_fetch_and_add(&histp->slots[slot], 1);

return 0;
}

char LICENSE[] SEC("license") = "GPL";
219 changes: 219 additions & 0 deletions libbpf-tools/bitesize.c
@@ -0,0 +1,219 @@
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
// Copyright (c) 2020 Wenbo Zhang
//
// Based on bitesize(8) from BCC by Brendan Gregg.
// 16-Jun-2020 Wenbo Zhang Created this.
#include <argp.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <bpf/libbpf.h>
#include <bpf/bpf.h>
#include "bitesize.h"
#include "bitesize.skel.h"
#include "trace_helpers.h"

static struct env {
char *comm;
int comm_len;
time_t interval;
bool timestamp;
bool verbose;
int times;
} env = {
.interval = 99999999,
.times = 99999999,
};

static volatile bool exiting;

const char *argp_program_version = "bitesize 0.1";
const char *argp_program_bug_address = "<ethercflow@gmail.com>";
const char argp_program_doc[] =
"Summarize block device I/O size as a histogram.\n"
"\n"
"USAGE: bitesize [-h] [-T] [-m] [interval] [count]\n"
"\n"
"EXAMPLES:\n"
" bitesize # summarize block I/O latency as a histogram\n"
" bitesize 1 10 # print 1 second summaries, 10 times\n"
" bitesize -T 1 # 1s summaries with timestamps\n"
" bitesize -c fio # trace fio only\n";

static const struct argp_option opts[] = {
{ NULL, 'h', NULL, OPTION_HIDDEN, "Show the full help" },
{ "timestamp", 'T', NULL, 0, "Include timestamp on output" },
{ "comm", 'c', "COMM", 0, "Trace this comm only" },
{ "verbose", 'v', NULL, 0, "Verbose debug output" },
{},
};

static error_t parse_arg(int key, char *arg, struct argp_state *state)
{
static int pos_args, len;

switch (key) {
case 'v':
env.verbose = true;
break;
case 'h':
argp_usage(state);
break;
case 'c':
env.comm = arg;
len = strlen(arg) + 1;
env.comm_len = len > TASK_COMM_LEN ? TASK_COMM_LEN : len;
break;
case 'T':
env.timestamp = true;
break;
case ARGP_KEY_ARG:
errno = 0;
if (pos_args == 0) {
env.interval = strtol(arg, NULL, 10);
if (errno) {
fprintf(stderr, "invalid internal\n");
argp_usage(state);
}
} else if (pos_args == 1) {
env.times = strtol(arg, NULL, 10);
if (errno) {
fprintf(stderr, "invalid times\n");
argp_usage(state);
}
} else {
fprintf(stderr,
"unrecognized positional argument: %s\n", arg);
argp_usage(state);
}
pos_args++;
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}

int libbpf_print_fn(enum libbpf_print_level level,
const char *format, va_list args)
{
if (level == LIBBPF_DEBUG && !env.verbose)
return 0;
return vfprintf(stderr, format, args);
}

static void sig_handler(int sig)
{
exiting = true;
}

static int print_log2_hists(int fd)
{
struct hist_key lookup_key, next_key;
struct hist hist;
int err;

memset(lookup_key.comm, '?', sizeof(lookup_key.comm));
while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) {
err = bpf_map_lookup_elem(fd, &next_key, &hist);
if (err < 0) {
fprintf(stderr, "failed to lookup hist: %d\n", err);
return -1;
}
printf("\nProcess Name = %s\n", next_key.comm);
print_log2_hist(hist.slots, MAX_SLOTS, "Kbytes");
lookup_key = next_key;
}

memset(lookup_key.comm, '?', sizeof(lookup_key.comm));
while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) {
err = bpf_map_delete_elem(fd, &next_key);
if (err < 0) {
fprintf(stderr, "failed to cleanup hist : %d\n", err);
return -1;
}
lookup_key = next_key;
}

return 0;
}

int main(int argc, char **argv)
{
static const struct argp argp = {
.options = opts,
.parser = parse_arg,
.doc = argp_program_doc,
};
struct bitesize_bpf *obj;
struct tm *tm;
char ts[32];
int fd, err;
time_t t;

err = argp_parse(&argp, argc, argv, 0, NULL, NULL);
if (err)
return err;

libbpf_set_print(libbpf_print_fn);

err = bump_memlock_rlimit();
if (err) {
fprintf(stderr, "failed to increase rlimit: %d\n", err);
return 1;
}

obj = bitesize_bpf__open();
if (!obj) {
fprintf(stderr, "failed to open and/or load BPF ojbect\n");
return 1;
}

/* initialize global data (filtering options) */
if (env.comm)
strncpy((char*)obj->rodata->targ_comm, env.comm, env.comm_len);

err = bitesize_bpf__load(obj);
if (err) {
fprintf(stderr, "failed to load BPF object: %d\n", err);
goto cleanup;
}

err = bitesize_bpf__attach(obj);
if (err) {
fprintf(stderr, "failed to attach BPF programs\n");
goto cleanup;
}

fd = bpf_map__fd(obj->maps.hists);

signal(SIGINT, sig_handler);

printf("Tracing block device I/O... Hit Ctrl-C to end.\n");

/* main: poll */
while (1) {
sleep(env.interval);
printf("\n");

if (env.timestamp) {
time(&t);
tm = localtime(&t);
strftime(ts, sizeof(ts), "%H:%M:%S", tm);
printf("%-8s\n", ts);
}

err = print_log2_hists(fd);
if (err)
break;

if (exiting || --env.times == 0)
break;
}

cleanup:
bitesize_bpf__destroy(obj);

return err != 0;
}
15 changes: 15 additions & 0 deletions libbpf-tools/bitesize.h
@@ -0,0 +1,15 @@
#ifndef __BITESIZE_H
#define __BITESIZE_H

#define TASK_COMM_LEN 16
#define MAX_SLOTS 20

struct hist_key {
char comm[TASK_COMM_LEN];
};

struct hist {
__u32 slots[MAX_SLOTS];
};

#endif /* __BITESIZE_H */
28 changes: 28 additions & 0 deletions libbpf-tools/bits.bpf.h
@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
#ifndef __BITS_BPF_H
#define __BITS_BPF_H

static __always_inline u64 log2(u32 v)
{
u32 shift, r;

r = (v > 0xFFFF) << 4; v >>= r;
shift = (v > 0xFF) << 3; v >>= shift; r |= shift;
shift = (v > 0xF) << 2; v >>= shift; r |= shift;
shift = (v > 0x3) << 1; v >>= shift; r |= shift;
r |= (v >> 1);

return r;
}

static __always_inline u64 log2l(u64 v)
{
u32 hi = v >> 32;

if (hi)
return log2(hi) + 32;
else
return log2(v);
}

#endif /* __BITS_BPF_H */
24 changes: 1 addition & 23 deletions libbpf-tools/cpudist.bpf.c
Expand Up @@ -5,6 +5,7 @@
#include <bpf/bpf_core_read.h>
#include <bpf/bpf_tracing.h>
#include "cpudist.h"
#include "bits.bpf.h"

#define TASK_RUNNING 0

Expand All @@ -28,29 +29,6 @@ struct {
__type(value, struct hist);
} hists SEC(".maps");

static __always_inline u64 log2(u32 v)
{
u32 shift, r;

r = (v > 0xFFFF) << 4; v >>= r;
shift = (v > 0xFF) << 3; v >>= shift; r |= shift;
shift = (v > 0xF) << 2; v >>= shift; r |= shift;
shift = (v > 0x3) << 1; v >>= shift; r |= shift;
r |= (v >> 1);

return r;
}

static __always_inline u64 log2l(u64 v)
{
u32 hi = v >> 32;

if (hi)
return log2(hi) + 32;
else
return log2(v);
}

static __always_inline void store_start(u32 tgid, u32 pid, u64 ts)
{
if (targ_tgid != -1 && targ_tgid != tgid)
Expand Down

0 comments on commit 34f8985

Please sign in to comment.