Skip to content

Commit

Permalink
libbpf-tools: add softirqs
Browse files Browse the repository at this point in the history
Signed-off-by: Wenbo Zhang <[email protected]>
  • Loading branch information
ethercflow authored and yonghong-song committed Sep 17, 2020
1 parent 3953c60 commit bf4668d
Show file tree
Hide file tree
Showing 5 changed files with 335 additions and 0 deletions.
1 change: 1 addition & 0 deletions libbpf-tools/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
/opensnoop
/readahead
/runqslower
/softirqs
/syscount
/tcpconnect
/tcpconnlat
Expand Down
1 change: 1 addition & 0 deletions libbpf-tools/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ APPS = \
opensnoop \
readahead \
runqslower \
softirqs \
syscount \
tcpconnect \
tcpconnlat \
Expand Down
67 changes: 67 additions & 0 deletions libbpf-tools/softirqs.bpf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// 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 "softirqs.h"
#include "bits.bpf.h"
#include "maps.bpf.h"

const volatile bool targ_dist = false;
const volatile bool targ_ns = false;

struct {
__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
__uint(max_entries, 1);
__type(key, u32);
__type(value, u64);
} start SEC(".maps");

__u64 counts[NR_SOFTIRQS];
struct hist hists[NR_SOFTIRQS];

SEC("tp_btf/softirq_entry")
int BPF_PROG(softirq_entry, unsigned int vec_nr)
{
u64 ts = bpf_ktime_get_ns();
u32 key = 0;

bpf_map_update_elem(&start, &key, &ts, 0);
return 0;
}

SEC("tp_btf/softirq_exit")
int BPF_PROG(softirq_exit, unsigned int vec_nr)
{
u32 key = 0;
s64 delta;
u64 *tsp;

if (vec_nr >= NR_SOFTIRQS)
return 0;
tsp = bpf_map_lookup_elem(&start, &key);
if (!tsp || !*tsp)
return 0;
delta = bpf_ktime_get_ns() - *tsp;
if (delta < 0)
return 0;
if (!targ_ns)
delta /= 1000U;

if (!targ_dist) {
__sync_fetch_and_add(&counts[vec_nr], delta);
} else {
struct hist *hist;
u64 slot;

hist = &hists[vec_nr];
slot = log2(delta);
if (slot >= MAX_SLOTS)
slot = MAX_SLOTS - 1;
__sync_fetch_and_add(&hist->slots[slot], 1);
}

return 0;
}

char LICENSE[] SEC("license") = "GPL";
255 changes: 255 additions & 0 deletions libbpf-tools/softirqs.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
// Copyright (c) 2020 Wenbo Zhang
//
// Based on softirq(8) from BCC by Brendan Gregg & Sasha Goldshtein.
// 15-Aug-2020 Wenbo Zhang Created this.
#include <argp.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 "softirqs.h"
#include "softirqs.skel.h"
#include "trace_helpers.h"

struct env {
bool distributed;
bool nanoseconds;
time_t interval;
int times;
bool timestamp;
bool verbose;
} env = {
.interval = 99999999,
.times = 99999999,
};

static volatile bool exiting;

const char *argp_program_version = "softirqs 0.1";
const char *argp_program_bug_address = "<[email protected]>";
const char argp_program_doc[] =
"Summarize soft irq event time as histograms.\n"
"\n"
"USAGE: softirqs [--help] [-T] [-N] [-d] [interval] [count]\n"
"\n"
"EXAMPLES:\n"
" softirqss # sum soft irq event time\n"
" softirqss -d # show soft irq event time as histograms\n"
" softirqss 1 10 # print 1 second summaries, 10 times\n"
" softirqss -NT 1 # 1s summaries, nanoseconds, and timestamps\n";

static const struct argp_option opts[] = {
{ "distributed", 'd', NULL, 0, "Show distributions as histograms" },
{ "timestamp", 'T', NULL, 0, "Include timestamp on output" },
{ "nanoseconds", 'N', NULL, 0, "Output in nanoseconds" },
{ "verbose", 'v', NULL, 0, "Verbose debug output" },
{},
};

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

switch (key) {
case 'v':
env.verbose = true;
break;
case 'd':
env.distributed = true;
break;
case 'N':
env.nanoseconds = true;
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;
}

enum {
HI_SOFTIRQ = 0,
TIMER_SOFTIRQ = 1,
NET_TX_SOFTIRQ = 2,
NET_RX_SOFTIRQ = 3,
BLOCK_SOFTIRQ = 4,
IRQ_POLL_SOFTIRQ = 5,
TASKLET_SOFTIRQ = 6,
SCHED_SOFTIRQ = 7,
HRTIMER_SOFTIRQ = 8,
RCU_SOFTIRQ = 9,
NR_SOFTIRQS = 10,
};

static char *vec_names[] = {
[HI_SOFTIRQ] = "hi",
[TIMER_SOFTIRQ] = "timer",
[NET_TX_SOFTIRQ] = "net_tx",
[NET_RX_SOFTIRQ] = "net_rx",
[BLOCK_SOFTIRQ] = "block",
[IRQ_POLL_SOFTIRQ] = "irq_poll",
[TASKLET_SOFTIRQ] = "tasklet",
[SCHED_SOFTIRQ] = "sched",
[HRTIMER_SOFTIRQ] = "hrtimer",
[RCU_SOFTIRQ] = "rcu",
};

static int print_count(struct softirqs_bpf__bss *bss)
{
const char *units = env.nanoseconds ? "nsecs" : "usecs";
__u64 count;
__u32 vec;

printf("%-16s %6s%5s\n", "SOFTIRQ", "TOTAL_", units);

for (vec = 0; vec < NR_SOFTIRQS; vec++) {
count = __atomic_exchange_n(&bss->counts[vec], 0,
__ATOMIC_RELAXED);
if (count > 0)
printf("%-16s %11llu\n", vec_names[vec], count);
}

return 0;
}

static struct hist zero;

static int print_hist(struct softirqs_bpf__bss *bss)
{
const char *units = env.nanoseconds ? "nsecs" : "usecs";
__u32 vec;

for (vec = 0; vec < NR_SOFTIRQS; vec++) {
struct hist hist = bss->hists[vec];

bss->hists[vec] = zero;
if (!memcmp(&zero, &hist, sizeof(hist)))
continue;
printf("softirq = %s\n", vec_names[vec]);
print_log2_hist(hist.slots, MAX_SLOTS, units);
printf("\n");
}

return 0;
}

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

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 = softirqs_bpf__open();
if (!obj) {
fprintf(stderr, "failed to open and/or load BPF object\n");
return 1;
}

/* initialize global data (filtering options) */
obj->rodata->targ_dist = env.distributed;
obj->rodata->targ_ns = env.nanoseconds;

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

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

signal(SIGINT, sig_handler);

printf("Tracing soft irq event time... 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);
}

if (!env.distributed)
err = print_count(obj->bss);
else
err = print_hist(obj->bss);
if (err)
break;

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

cleanup:
softirqs_bpf__destroy(obj);

return err != 0;
}
11 changes: 11 additions & 0 deletions libbpf-tools/softirqs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
#ifndef __SOFTIRQS_H
#define __SOFTIRQS_H

#define MAX_SLOTS 20

struct hist {
__u32 slots[MAX_SLOTS];
};

#endif /* __SOFTIRQS_H */

0 comments on commit bf4668d

Please sign in to comment.