bpf: btf: print map dump and lookup with btf info
This patch augments the output of bpftool's map dump and map lookup commands to print data along side btf info, if the correspondin btf info is available. The outputs for each of map dump and map lookup commands are augmented in two ways: 1. when neither of -j and -p are supplied, btf-ful map data is printed whose aim is human readability. This means no commitments for json- or backward- compatibility. 2. when either -j or -p are supplied, a new json object named "formatted" is added for each key-value pair. This object contains the same data as the key-value pair, but with btf info. "formatted" object promises json- and backward- compatibility. Below is a sample output. $ bpftool map dump -p id 8 [{ "key": ["0x0f","0x00","0x00","0x00" ], "value": ["0x03", "0x00", "0x00", "0x00", ... ], "formatted": { "key": 15, "value": { "int_field": 3, ... } } } ] This patch calls btf_dumper introduced in previous patch to accomplish the above. Indeed, btf-ful info is only displayed if btf data for the given map is available. Otherwise existing output is displayed as-is. Signed-off-by: Okash Khawaja <osk@fb.com> Acked-by: Martin KaFai Lau <kafai@fb.com> Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
This commit is contained in:
parent
b12d6ec097
commit
2d3feca8c4
|
@ -34,6 +34,7 @@
|
|||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/err.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -44,6 +45,8 @@
|
|||
|
||||
#include <bpf.h>
|
||||
|
||||
#include "btf.h"
|
||||
#include "json_writer.h"
|
||||
#include "main.h"
|
||||
|
||||
static const char * const map_type_name[] = {
|
||||
|
@ -148,8 +151,109 @@ int map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len)
|
|||
return fd;
|
||||
}
|
||||
|
||||
static int do_dump_btf(const struct btf_dumper *d,
|
||||
struct bpf_map_info *map_info, void *key,
|
||||
void *value)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* start of key-value pair */
|
||||
jsonw_start_object(d->jw);
|
||||
|
||||
jsonw_name(d->jw, "key");
|
||||
|
||||
ret = btf_dumper_type(d, map_info->btf_key_type_id, key);
|
||||
if (ret)
|
||||
goto err_end_obj;
|
||||
|
||||
jsonw_name(d->jw, "value");
|
||||
|
||||
ret = btf_dumper_type(d, map_info->btf_value_type_id, value);
|
||||
|
||||
err_end_obj:
|
||||
/* end of key-value pair */
|
||||
jsonw_end_object(d->jw);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int get_btf(struct bpf_map_info *map_info, struct btf **btf)
|
||||
{
|
||||
struct bpf_btf_info btf_info = { 0 };
|
||||
__u32 len = sizeof(btf_info);
|
||||
__u32 last_size;
|
||||
int btf_fd;
|
||||
void *ptr;
|
||||
int err;
|
||||
|
||||
err = 0;
|
||||
*btf = NULL;
|
||||
btf_fd = bpf_btf_get_fd_by_id(map_info->btf_id);
|
||||
if (btf_fd < 0)
|
||||
return 0;
|
||||
|
||||
/* we won't know btf_size until we call bpf_obj_get_info_by_fd(). so
|
||||
* let's start with a sane default - 4KiB here - and resize it only if
|
||||
* bpf_obj_get_info_by_fd() needs a bigger buffer.
|
||||
*/
|
||||
btf_info.btf_size = 4096;
|
||||
last_size = btf_info.btf_size;
|
||||
ptr = malloc(last_size);
|
||||
if (!ptr) {
|
||||
err = -ENOMEM;
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
bzero(ptr, last_size);
|
||||
btf_info.btf = ptr_to_u64(ptr);
|
||||
err = bpf_obj_get_info_by_fd(btf_fd, &btf_info, &len);
|
||||
|
||||
if (!err && btf_info.btf_size > last_size) {
|
||||
void *temp_ptr;
|
||||
|
||||
last_size = btf_info.btf_size;
|
||||
temp_ptr = realloc(ptr, last_size);
|
||||
if (!temp_ptr) {
|
||||
err = -ENOMEM;
|
||||
goto exit_free;
|
||||
}
|
||||
ptr = temp_ptr;
|
||||
bzero(ptr, last_size);
|
||||
btf_info.btf = ptr_to_u64(ptr);
|
||||
err = bpf_obj_get_info_by_fd(btf_fd, &btf_info, &len);
|
||||
}
|
||||
|
||||
if (err || btf_info.btf_size > last_size) {
|
||||
err = errno;
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
*btf = btf__new((__u8 *)btf_info.btf, btf_info.btf_size, NULL);
|
||||
if (IS_ERR(*btf)) {
|
||||
err = PTR_ERR(btf);
|
||||
*btf = NULL;
|
||||
}
|
||||
|
||||
exit_free:
|
||||
close(btf_fd);
|
||||
free(ptr);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static json_writer_t *get_btf_writer(void)
|
||||
{
|
||||
json_writer_t *jw = jsonw_new(stdout);
|
||||
|
||||
if (!jw)
|
||||
return NULL;
|
||||
jsonw_pretty(jw, true);
|
||||
|
||||
return jw;
|
||||
}
|
||||
|
||||
static void print_entry_json(struct bpf_map_info *info, unsigned char *key,
|
||||
unsigned char *value)
|
||||
unsigned char *value, struct btf *btf)
|
||||
{
|
||||
jsonw_start_object(json_wtr);
|
||||
|
||||
|
@ -158,6 +262,16 @@ static void print_entry_json(struct bpf_map_info *info, unsigned char *key,
|
|||
print_hex_data_json(key, info->key_size);
|
||||
jsonw_name(json_wtr, "value");
|
||||
print_hex_data_json(value, info->value_size);
|
||||
if (btf) {
|
||||
struct btf_dumper d = {
|
||||
.btf = btf,
|
||||
.jw = json_wtr,
|
||||
.is_plain_text = false,
|
||||
};
|
||||
|
||||
jsonw_name(json_wtr, "formatted");
|
||||
do_dump_btf(&d, info, key, value);
|
||||
}
|
||||
} else {
|
||||
unsigned int i, n;
|
||||
|
||||
|
@ -508,10 +622,12 @@ static int do_show(int argc, char **argv)
|
|||
|
||||
static int do_dump(int argc, char **argv)
|
||||
{
|
||||
struct bpf_map_info info = {};
|
||||
void *key, *value, *prev_key;
|
||||
unsigned int num_elems = 0;
|
||||
struct bpf_map_info info = {};
|
||||
__u32 len = sizeof(info);
|
||||
json_writer_t *btf_wtr;
|
||||
struct btf *btf = NULL;
|
||||
int err;
|
||||
int fd;
|
||||
|
||||
|
@ -537,8 +653,27 @@ static int do_dump(int argc, char **argv)
|
|||
}
|
||||
|
||||
prev_key = NULL;
|
||||
|
||||
err = get_btf(&info, &btf);
|
||||
if (err) {
|
||||
p_err("failed to get btf");
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
if (json_output)
|
||||
jsonw_start_array(json_wtr);
|
||||
else
|
||||
if (btf) {
|
||||
btf_wtr = get_btf_writer();
|
||||
if (!btf_wtr) {
|
||||
p_info("failed to create json writer for btf. falling back to plain output");
|
||||
btf__free(btf);
|
||||
btf = NULL;
|
||||
} else {
|
||||
jsonw_start_array(btf_wtr);
|
||||
}
|
||||
}
|
||||
|
||||
while (true) {
|
||||
err = bpf_map_get_next_key(fd, prev_key, key);
|
||||
if (err) {
|
||||
|
@ -549,9 +684,19 @@ static int do_dump(int argc, char **argv)
|
|||
|
||||
if (!bpf_map_lookup_elem(fd, key, value)) {
|
||||
if (json_output)
|
||||
print_entry_json(&info, key, value);
|
||||
print_entry_json(&info, key, value, btf);
|
||||
else
|
||||
if (btf) {
|
||||
struct btf_dumper d = {
|
||||
.btf = btf,
|
||||
.jw = btf_wtr,
|
||||
.is_plain_text = true,
|
||||
};
|
||||
|
||||
do_dump_btf(&d, &info, key, value);
|
||||
} else {
|
||||
print_entry_plain(&info, key, value);
|
||||
}
|
||||
} else {
|
||||
if (json_output) {
|
||||
jsonw_name(json_wtr, "key");
|
||||
|
@ -574,14 +719,19 @@ static int do_dump(int argc, char **argv)
|
|||
|
||||
if (json_output)
|
||||
jsonw_end_array(json_wtr);
|
||||
else
|
||||
else if (btf) {
|
||||
jsonw_end_array(btf_wtr);
|
||||
jsonw_destroy(&btf_wtr);
|
||||
} else {
|
||||
printf("Found %u element%s\n", num_elems,
|
||||
num_elems != 1 ? "s" : "");
|
||||
}
|
||||
|
||||
exit_free:
|
||||
free(key);
|
||||
free(value);
|
||||
close(fd);
|
||||
btf__free(btf);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
@ -637,6 +787,8 @@ static int do_lookup(int argc, char **argv)
|
|||
{
|
||||
struct bpf_map_info info = {};
|
||||
__u32 len = sizeof(info);
|
||||
json_writer_t *btf_wtr;
|
||||
struct btf *btf = NULL;
|
||||
void *key, *value;
|
||||
int err;
|
||||
int fd;
|
||||
|
@ -661,12 +813,8 @@ static int do_lookup(int argc, char **argv)
|
|||
goto exit_free;
|
||||
|
||||
err = bpf_map_lookup_elem(fd, key, value);
|
||||
if (!err) {
|
||||
if (json_output)
|
||||
print_entry_json(&info, key, value);
|
||||
else
|
||||
print_entry_plain(&info, key, value);
|
||||
} else if (errno == ENOENT) {
|
||||
if (err) {
|
||||
if (errno == ENOENT) {
|
||||
if (json_output) {
|
||||
jsonw_null(json_wtr);
|
||||
} else {
|
||||
|
@ -678,10 +826,47 @@ static int do_lookup(int argc, char **argv)
|
|||
p_err("lookup failed: %s", strerror(errno));
|
||||
}
|
||||
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
/* here means bpf_map_lookup_elem() succeeded */
|
||||
err = get_btf(&info, &btf);
|
||||
if (err) {
|
||||
p_err("failed to get btf");
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
if (json_output) {
|
||||
print_entry_json(&info, key, value, btf);
|
||||
} else if (btf) {
|
||||
/* if here json_wtr wouldn't have been initialised,
|
||||
* so let's create separate writer for btf
|
||||
*/
|
||||
btf_wtr = get_btf_writer();
|
||||
if (!btf_wtr) {
|
||||
p_info("failed to create json writer for btf. falling back to plain output");
|
||||
btf__free(btf);
|
||||
btf = NULL;
|
||||
print_entry_plain(&info, key, value);
|
||||
} else {
|
||||
struct btf_dumper d = {
|
||||
.btf = btf,
|
||||
.jw = btf_wtr,
|
||||
.is_plain_text = true,
|
||||
};
|
||||
|
||||
do_dump_btf(&d, &info, key, value);
|
||||
jsonw_destroy(&btf_wtr);
|
||||
}
|
||||
} else {
|
||||
print_entry_plain(&info, key, value);
|
||||
}
|
||||
|
||||
exit_free:
|
||||
free(key);
|
||||
free(value);
|
||||
close(fd);
|
||||
btf__free(btf);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue