Skip to content

Commit

Permalink
libbpf-tools: add hardirqs
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 Sep 17, 2020
1 parent da8c6a9 commit 3953c60
Show file tree
Hide file tree
Showing 5 changed files with 380 additions and 0 deletions.
1 change: 1 addition & 0 deletions libbpf-tools/.gitignore
Expand Up @@ -8,6 +8,7 @@
/drsnoop
/execsnoop
/filelife
/hardirqs
/opensnoop
/readahead
/runqslower
Expand Down
1 change: 1 addition & 0 deletions libbpf-tools/Makefile
Expand Up @@ -19,6 +19,7 @@ APPS = \
drsnoop \
execsnoop \
filelife \
hardirqs \
opensnoop \
readahead \
runqslower \
Expand Down
93 changes: 93 additions & 0 deletions libbpf-tools/hardirqs.bpf.c
@@ -0,0 +1,93 @@
// 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 "hardirqs.h"
#include "bits.bpf.h"
#include "maps.bpf.h"

#define MAX_ENTRIES 256

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");

struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, MAX_ENTRIES);
__type(key, struct irq_key);
__type(value, struct info);
} infos SEC(".maps");

static struct info zero;

SEC("tracepoint/irq/irq_handler_entry")
int handle__irq_handler(struct trace_event_raw_irq_handler_entry *ctx)
{
struct irq_key key = {};
struct info *info;

bpf_probe_read_kernel_str(&key.name, sizeof(key.name), ctx->__data);
info = bpf_map_lookup_or_try_init(&infos, &key, &zero);
if (!info)
return 0;
info->count += 1;
return 0;
}

SEC("tp_btf/irq_handler_entry")
int BPF_PROG(irq_handler_entry)
{
u64 ts = bpf_ktime_get_ns();
u32 key = 0;

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

SEC("tp_btf/irq_handler_exit")
int BPF_PROG(irq_handler_exit_exit, int irq, struct irqaction *action)
{
struct irq_key ikey = {};
struct info *info;
u32 key = 0;
s64 delta;
u64 *tsp;

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;

bpf_probe_read_kernel_str(&ikey.name, sizeof(ikey.name), action->name);
info = bpf_map_lookup_or_try_init(&infos, &ikey, &zero);
if (!info)
return 0;

if (!targ_dist) {
info->count += delta;
} else {
u64 slot;

slot = log2(delta);
if (slot >= MAX_SLOTS)
slot = MAX_SLOTS - 1;
info->slots[slot]++;
}

return 0;
}

char LICENSE[] SEC("license") = "GPL";
269 changes: 269 additions & 0 deletions libbpf-tools/hardirqs.c
@@ -0,0 +1,269 @@
// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
// Copyright (c) 2020 Wenbo Zhang
//
// Based on hardirq(8) from BCC by Brendan Gregg.
// 31-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 "hardirqs.h"
#include "hardirqs.skel.h"
#include "trace_helpers.h"

struct env {
bool count;
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 = "hardirqs 0.1";
const char *argp_program_bug_address = "<bpf@vger.kernel.org>";
const char argp_program_doc[] =
"Summarize hard irq event time as histograms.\n"
"\n"
"USAGE: hardirqs [--help] [-T] [-N] [-d] [interval] [count]\n"
"\n"
"EXAMPLES:\n"
" hardirqs # sum hard irq event time\n"
" hardirqs -d # show hard irq event time as histograms\n"
" hardirqs 1 10 # print 1 second summaries, 10 times\n"
" hardirqs -NT 1 # 1s summaries, nanoseconds, and timestamps\n";

static const struct argp_option opts[] = {
{ "count", 'C', NULL, 0, "Show event counts instead of timing" },
{ "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 'C':
env.count = 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;
}

static int print_map(struct bpf_map *map)
{
struct irq_key lookup_key = {}, next_key;
struct info info;
int fd, err;

if (env.count) {
printf("%-26s %11s\n", "HARDIRQ", "TOTAL_count");
} else if (!env.distributed) {
const char *units = env.nanoseconds ? "nsecs" : "usecs";

printf("%-26s %6s%5s\n", "HARDIRQ", "TOTAL_", units);
}

fd = bpf_map__fd(map);
while (!bpf_map_get_next_key(fd, &lookup_key, &next_key)) {
err = bpf_map_lookup_elem(fd, &next_key, &info);
if (err < 0) {
fprintf(stderr, "failed to lookup infos: %d\n", err);
return -1;
}
if (!env.distributed)
printf("%-26s %11llu\n", next_key.name, info.count);
else {
const char *units = env.nanoseconds ? "nsecs" : "usecs";

printf("hardirq = %s\n", next_key.name);
print_log2_hist(info.slots, MAX_SLOTS, units);
}
lookup_key = next_key;
}

memset(&lookup_key, 0, sizeof(lookup_key));

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 infos: %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 hardirqs_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;

if (env.count && env.distributed) {
fprintf(stderr, "count, distributed cann't be used together.\n");
return 1;
}

libbpf_set_print(libbpf_print_fn);

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

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

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

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

if (env.count) {
obj->links.handle__irq_handler =
bpf_program__attach(obj->progs.handle__irq_handler);
err = libbpf_get_error(obj->links.handle__irq_handler);
if (err) {
fprintf(stderr,
"failed to attach irq/irq_handler_entry: %s\n",
strerror(err));
}
} else {
obj->links.irq_handler_entry =
bpf_program__attach(obj->progs.irq_handler_entry);
err = libbpf_get_error(obj->links.irq_handler_entry);
if (err) {
fprintf(stderr,
"failed to attach irq_handler_entry: %s\n",
strerror(err));
}
obj->links.irq_handler_exit_exit =
bpf_program__attach(obj->progs.irq_handler_exit_exit);
err = libbpf_get_error(obj->links.irq_handler_exit_exit);
if (err) {
fprintf(stderr,
"failed to attach irq_handler_exit: %s\n",
strerror(err));
}
}

signal(SIGINT, sig_handler);

if (env.count)
printf("Tracing hard irq events... Hit Ctrl-C to end.\n");
else
printf("Tracing hard 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);
}

err = print_map(obj->maps.infos);
if (err)
break;

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

cleanup:
hardirqs_bpf__destroy(obj);

return err != 0;
}
16 changes: 16 additions & 0 deletions libbpf-tools/hardirqs.h
@@ -0,0 +1,16 @@
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
#ifndef __HARDIRQS_H
#define __HARDIRQS_H

#define MAX_SLOTS 20

struct irq_key {
char name[32];
};

struct info {
__u64 count;
__u32 slots[MAX_SLOTS];
};

#endif /* __HARDIRQS_H */

0 comments on commit 3953c60

Please sign in to comment.