* [PATCH bpf-next v3 0/3] bpf: btf: print bpftool map data with btf
@ 2018-07-08 20:30 Okash Khawaja
2018-07-08 20:30 ` [PATCH bpf-next v3 2/3] bpf: btf: add btf print functionality Okash Khawaja
2018-07-08 20:30 ` [PATCH bpf-next v3 3/3] bpf: btf: print map dump and lookup with btf info Okash Khawaja
0 siblings, 2 replies; 6+ messages in thread
From: Okash Khawaja @ 2018-07-08 20:30 UTC (permalink / raw)
To: Daniel Borkmann, Martin KaFai Lau, Alexei Starovoitov,
Yonghong Song, Quentin Monnet, Jakub Kicinski, David S. Miller
Cc: netdev, kernel-team, linux-kernel
Hi,
This v3 contains incorporates feedback from v2, including a fix for big endian
when extracting bitfields. Below is a summary of all changes.
patch 1:
- use kernel integer types instead of stdint
patch 2:
- change stdint types to kernel equivalents
- remove variable ret from btf_dumper_modifier()
- remove unnecessary parentheses in btf_dumper_enum()
- remove unnecessary parentheses in btf_dumper_array()
- change integer types from explicitly sized to int in btf_dumper_int_bits
- fix btf_dumper_int_bits() so it works for little and big endian
- remove double space in btf_dumper_int()
- print non-printable characters as string
- remove ret variable from btf_dumper_int()
- don't initialise variable with function which needs error check in
btf_dumper_struct()
- use a temp variable to avoid multi-line statement in btf_dumper_struct()
- call jsonw_end_object before returning in error case in btf_dumper_struct()
- print something in case of BTF_KIND_FWD in btf_dumper_do_type()
- return directly from cases in switch-case and save 9 LOC in
btf_dumper_do_type()
- remove check for null argument in btf_dumper_type()
- remove header file btf_dumper.h and move declarations to main.h
patch 3:
- change stdint types to kernel equivalents
- keep header includes in alphabetical order
- use goto in do_dump_btf() to ensure jsonw_end_object() in cases of error
- don't initialise variable with functions that can fail in get_btf()
- remove json-breaking printf in get_btf()
- refactor so that there isn't too much code in if (!err) case in do_lookup()
Thanks,
Okash
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH bpf-next v3 2/3] bpf: btf: add btf print functionality
2018-07-08 20:30 [PATCH bpf-next v3 0/3] bpf: btf: print bpftool map data with btf Okash Khawaja
@ 2018-07-08 20:30 ` Okash Khawaja
2018-07-10 3:56 ` Jakub Kicinski
2018-07-08 20:30 ` [PATCH bpf-next v3 3/3] bpf: btf: print map dump and lookup with btf info Okash Khawaja
1 sibling, 1 reply; 6+ messages in thread
From: Okash Khawaja @ 2018-07-08 20:30 UTC (permalink / raw)
To: Daniel Borkmann, Martin KaFai Lau, Alexei Starovoitov,
Yonghong Song, Quentin Monnet, Jakub Kicinski, David S. Miller
Cc: netdev, kernel-team, linux-kernel
[-- Attachment #1: 02-add-btf-dump-map.patch --]
[-- Type: text/plain, Size: 9658 bytes --]
This consumes functionality exported in the previous patch. It does the
main job of printing with BTF data. This is used in the following patch
to provide a more readable output of a map's dump. It relies on
json_writer to do json printing. Below is sample output where map keys
are ints and values are of type struct A:
typedef int int_type;
enum E {
E0,
E1,
};
struct B {
int x;
int y;
};
struct A {
int m;
unsigned long long n;
char o;
int p[8];
int q[4][8];
enum E r;
void *s;
struct B t;
const int u;
int_type v;
unsigned int w1: 3;
unsigned int w2: 3;
};
$ sudo bpftool map dump id 14
[{
"key": 0,
"value": {
"m": 1,
"n": 2,
"o": "c",
"p": [15,16,17,18,15,16,17,18
],
"q": [[25,26,27,28,25,26,27,28
],[35,36,37,38,35,36,37,38
],[45,46,47,48,45,46,47,48
],[55,56,57,58,55,56,57,58
]
],
"r": 1,
"s": 0x7ffd80531cf8,
"t": {
"x": 5,
"y": 10
},
"u": 100,
"v": 20,
"w1": 0x7,
"w2": 0x3
}
}
]
This patch uses json's {} and [] to imply struct/union and array. More
explicit information can be added later. For example, a command line
option can be introduced to print whether a key or value is struct
or union, name of a struct etc. This will however come at the expense
of duplicating info when, for example, printing an array of structs.
enums are printed as ints without their names.
Signed-off-by: Okash Khawaja <osk@fb.com>
Acked-by: Martin KaFai Lau <kafai@fb.com>
---
tools/bpf/bpftool/btf_dumper.c | 253 +++++++++++++++++++++++++++++++++++++++++
tools/bpf/bpftool/main.h | 15 ++
2 files changed, 268 insertions(+)
--- /dev/null
+++ b/tools/bpf/bpftool/btf_dumper.c
@@ -0,0 +1,253 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2018 Facebook */
+
+#include <linux/btf.h>
+#include <linux/err.h>
+#include <stdio.h> /* for (FILE *) used by json_writer */
+#include <linux/bitops.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "btf.h"
+#include "json_writer.h"
+#include "main.h"
+
+#define BITS_PER_BYTE_MASK (BITS_PER_BYTE - 1)
+#define BITS_PER_BYTE_MASKED(bits) ((bits) & BITS_PER_BYTE_MASK)
+#define BITS_ROUNDDOWN_BYTES(bits) ((bits) >> 3)
+#define BITS_ROUNDUP_BYTES(bits) \
+ (BITS_ROUNDDOWN_BYTES(bits) + !!BITS_PER_BYTE_MASKED(bits))
+const int one = 1;
+#define is_big_endian() ((*(char *)&one) == 0)
+
+static int btf_dumper_do_type(const struct btf_dumper *d, __u32 type_id,
+ __u8 bit_offset, const void *data);
+
+static void btf_dumper_ptr(const void *data, json_writer_t *jw,
+ bool is_plain_text)
+{
+ if (is_plain_text)
+ jsonw_printf(jw, "%p", *((unsigned long *)data));
+ else
+ jsonw_printf(jw, "%u", *((unsigned long *)data));
+}
+
+static int btf_dumper_modifier(const struct btf_dumper *d, __u32 type_id,
+ const void *data)
+{
+ int actual_type_id;
+
+ actual_type_id = btf__resolve_type(d->btf, type_id);
+ if (actual_type_id < 0)
+ return actual_type_id;
+
+ return btf_dumper_do_type(d, actual_type_id, 0, data);
+}
+
+static void btf_dumper_enum(const void *data, json_writer_t *jw)
+{
+ jsonw_printf(jw, "%d", *(int *)data);
+}
+
+static int btf_dumper_array(const struct btf_dumper *d, __u32 type_id,
+ const void *data)
+{
+ const struct btf_type *t = btf__type_by_id(d->btf, type_id);
+ struct btf_array *arr = (struct btf_array *)(t + 1);
+ long long elem_size;
+ int ret = 0;
+ __u32 i;
+
+ elem_size = btf__resolve_size(d->btf, arr->type);
+ if (elem_size < 0)
+ return elem_size;
+
+ jsonw_start_array(d->jw);
+ for (i = 0; i < arr->nelems; i++) {
+ ret = btf_dumper_do_type(d, arr->type, 0,
+ data + i * elem_size);
+ if (ret)
+ break;
+ }
+
+ jsonw_end_array(d->jw);
+ return ret;
+}
+
+static void btf_dumper_int_bits(__u32 int_type, __u8 bit_offset,
+ const void *data, json_writer_t *jw,
+ bool is_plain_text)
+{
+ int left_shift_bits, right_shift_bits;
+ int nr_bits = BTF_INT_BITS(int_type);
+ int total_bits_offset;
+ int bytes_to_copy;
+ int bits_to_copy;
+ __u64 print_num;
+
+ total_bits_offset = bit_offset + BTF_INT_OFFSET(int_type);
+ data += BITS_ROUNDDOWN_BYTES(total_bits_offset);
+ bit_offset = BITS_PER_BYTE_MASKED(total_bits_offset);
+ bits_to_copy = bit_offset + nr_bits;
+ bytes_to_copy = BITS_ROUNDUP_BYTES(bits_to_copy);
+
+ print_num = 0;
+ memcpy(&print_num, data, bytes_to_copy);
+ if (is_big_endian()) {
+ left_shift_bits = bit_offset;
+ right_shift_bits = 64 - nr_bits;
+ } else {
+ left_shift_bits = 64 - bits_to_copy;
+ right_shift_bits = 64 - nr_bits;
+ }
+
+ print_num <<= left_shift_bits;
+ print_num >>= right_shift_bits;
+ if (is_plain_text)
+ jsonw_printf(jw, "0x%llx", print_num);
+ else
+ jsonw_printf(jw, "%llu", print_num);
+}
+
+static int btf_dumper_int(const struct btf_type *t, __u8 bit_offset,
+ const void *data, json_writer_t *jw,
+ bool is_plain_text)
+{
+ __u32 *int_type;
+ __u32 nr_bits;
+
+ int_type = (__u32 *)(t + 1);
+ nr_bits = BTF_INT_BITS(*int_type);
+ /* if this is bit field */
+ if (bit_offset || BTF_INT_OFFSET(*int_type) ||
+ BITS_PER_BYTE_MASKED(nr_bits)) {
+ btf_dumper_int_bits(*int_type, bit_offset, data, jw,
+ is_plain_text);
+ return 0;
+ }
+
+ switch (BTF_INT_ENCODING(*int_type)) {
+ case 0:
+ if (BTF_INT_BITS(*int_type) == 64)
+ jsonw_printf(jw, "%lu", *((__u64 *)data));
+ else if (BTF_INT_BITS(*int_type) == 32)
+ jsonw_printf(jw, "%u", *((__u32 *)data));
+ else if (BTF_INT_BITS(*int_type) == 16)
+ jsonw_printf(jw, "%hu", *((__u16 *)data));
+ else if (BTF_INT_BITS(*int_type) == 8)
+ jsonw_printf(jw, "%hhu", *((__u8 *)data));
+ else
+ btf_dumper_int_bits(*int_type, bit_offset, data, jw,
+ is_plain_text);
+ break;
+ case BTF_INT_SIGNED:
+ if (BTF_INT_BITS(*int_type) == 64)
+ jsonw_printf(jw, "%ld", *((long long *)data));
+ else if (BTF_INT_BITS(*int_type) == 32)
+ jsonw_printf(jw, "%d", *((int *)data));
+ else if (BTF_INT_BITS(*int_type) == 16)
+ jsonw_printf(jw, "%hd", *((short *)data));
+ else if (BTF_INT_BITS(*int_type) == 8)
+ jsonw_printf(jw, "%hhd", *((char *)data));
+ else
+ btf_dumper_int_bits(*int_type, bit_offset, data, jw,
+ is_plain_text);
+ break;
+ case BTF_INT_CHAR:
+ if (*((char *)data) == '\0')
+ jsonw_null(jw);
+ else if (isprint(*((char *)data)))
+ jsonw_printf(jw, "\"%c\"", *((char *)data));
+ else
+ if (is_plain_text)
+ jsonw_printf(jw, "0x%hhx", *((char *)data));
+ else
+ jsonw_printf(jw, "\"\\u00%02hhx\"",
+ *((char *)data));
+ break;
+ case BTF_INT_BOOL:
+ jsonw_bool(jw, *((int *)data));
+ break;
+ default:
+ /* shouldn't happen */
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int btf_dumper_struct(const struct btf_dumper *d, __u32 type_id,
+ const void *data)
+{
+ const struct btf_type *t;
+ struct btf_member *m;
+ int ret = 0;
+ int i, vlen;
+
+ t = btf__type_by_id(d->btf, type_id);
+ if (!t)
+ return -EINVAL;
+
+ vlen = BTF_INFO_VLEN(t->info);
+ jsonw_start_object(d->jw);
+ m = (struct btf_member *)(t + 1);
+
+ for (i = 0; i < vlen; i++) {
+ const void *data_off = data +
+ BITS_ROUNDDOWN_BYTES(m[i].offset);
+ jsonw_name(d->jw, btf__name_by_offset(d->btf, m[i].name_off));
+ ret = btf_dumper_do_type(d, m[i].type,
+ BITS_PER_BYTE_MASKED(m[i].offset),
+ data_off);
+ if (ret)
+ break;
+ }
+
+ jsonw_end_object(d->jw);
+
+ return ret;
+}
+
+static int btf_dumper_do_type(const struct btf_dumper *d, __u32 type_id,
+ __u8 bit_offset, const void *data)
+{
+ const struct btf_type *t = btf__type_by_id(d->btf, type_id);
+
+ switch (BTF_INFO_KIND(t->info)) {
+ case BTF_KIND_INT:
+ return btf_dumper_int(t, bit_offset, data, d->jw,
+ d->is_plain_text);
+ case BTF_KIND_STRUCT:
+ case BTF_KIND_UNION:
+ return btf_dumper_struct(d, type_id, data);
+ case BTF_KIND_ARRAY:
+ return btf_dumper_array(d, type_id, data);
+ case BTF_KIND_ENUM:
+ btf_dumper_enum(data, d->jw);
+ return 0;
+ case BTF_KIND_PTR:
+ btf_dumper_ptr(data, d->jw, d->is_plain_text);
+ return 0;
+ case BTF_KIND_UNKN:
+ jsonw_printf(d->jw, "(unknown)");
+ return 0;
+ case BTF_KIND_FWD:
+ /* map key or value can't be forward */
+ jsonw_printf(d->jw, "(fwd-kind-invalid)");
+ return -EINVAL;
+ case BTF_KIND_TYPEDEF:
+ case BTF_KIND_VOLATILE:
+ case BTF_KIND_CONST:
+ case BTF_KIND_RESTRICT:
+ return btf_dumper_modifier(d, type_id, data);
+ default:
+ jsonw_printf(d->jw, "(unsupported-kind");
+ return -EINVAL;
+ }
+}
+
+int btf_dumper_type(const struct btf_dumper *d, __u32 type_id,
+ const void *data)
+{
+ return btf_dumper_do_type(d, type_id, 0, data);
+}
--- a/tools/bpf/bpftool/main.h
+++ b/tools/bpf/bpftool/main.h
@@ -131,4 +131,19 @@ unsigned int get_page_size(void);
unsigned int get_possible_cpus(void);
const char *ifindex_to_bfd_name_ns(__u32 ifindex, __u64 ns_dev, __u64 ns_ino);
+struct btf_dumper {
+ const struct btf *btf;
+ json_writer_t *jw;
+ bool is_plain_text;
+};
+
+/* btf_dumper_type - print data along with type information
+ * @d: an instance containing context for dumping types
+ * @type_id: index in btf->types array. this points to the type to be dumped
+ * @data: pointer the actual data, i.e. the values to be printed
+ *
+ * Returns zero on success and negative error code otherwise
+ */
+int btf_dumper_type(const struct btf_dumper *d, __u32 type_id,
+ const void *data);
#endif
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH bpf-next v3 3/3] bpf: btf: print map dump and lookup with btf info
2018-07-08 20:30 [PATCH bpf-next v3 0/3] bpf: btf: print bpftool map data with btf Okash Khawaja
2018-07-08 20:30 ` [PATCH bpf-next v3 2/3] bpf: btf: add btf print functionality Okash Khawaja
@ 2018-07-08 20:30 ` Okash Khawaja
2018-07-10 4:06 ` Jakub Kicinski
1 sibling, 1 reply; 6+ messages in thread
From: Okash Khawaja @ 2018-07-08 20:30 UTC (permalink / raw)
To: Daniel Borkmann, Martin KaFai Lau, Alexei Starovoitov,
Yonghong Song, Quentin Monnet, Jakub Kicinski, David S. Miller
Cc: netdev, kernel-team, linux-kernel
[-- Attachment #1: 03-json-print-btf-info-for-map --]
[-- Type: text/plain, Size: 8408 bytes --]
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>
---
tools/bpf/bpftool/map.c | 207 ++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 191 insertions(+), 16 deletions(-)
--- a/tools/bpf/bpftool/map.c
+++ b/tools/bpf/bpftool/map.c
@@ -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,111 @@ int map_parse_fd_and_info(int *argc, cha
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 struct btf *get_btf(struct bpf_map_info *map_info)
+{
+ struct bpf_btf_info btf_info = { 0 };
+ __u32 len = sizeof(btf_info);
+ struct btf *btf = NULL;
+ __u32 last_size;
+ int btf_fd;
+ void *ptr;
+ int err;
+
+ btf_fd = bpf_btf_get_fd_by_id(map_info->btf_id);
+ if (btf_fd < 0)
+ return NULL;
+
+ /* 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) {
+ p_err("unable to allocate memory for debug info");
+ 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) {
+ p_err("unable to re-allocate memory for debug info");
+ 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) {
+ p_info("can't get btf info. debug info won't be displayed. error: %s",
+ err ? strerror(errno) : "exceeds size retry");
+ goto exit_free;
+ }
+
+ btf = btf__new((__u8 *)btf_info.btf,
+ btf_info.btf_size, NULL);
+ if (IS_ERR(btf)) {
+ p_info("error when initialising btf: %s\n",
+ strerror(PTR_ERR(btf)));
+ btf = NULL;
+ }
+
+exit_free:
+ close(btf_fd);
+ free(ptr);
+
+ return btf;
+}
+
+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 +264,16 @@ static void print_entry_json(struct bpf_
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 +624,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 +655,22 @@ static int do_dump(int argc, char **argv
}
prev_key = NULL;
+
+ btf = get_btf(&info);
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 +681,18 @@ 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
- print_entry_plain(&info, key, value);
+ 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 +715,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 +783,8 @@ static int do_lookup(int argc, char **ar
{
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,27 +809,54 @@ static int do_lookup(int argc, char **ar
goto exit_free;
err = bpf_map_lookup_elem(fd, key, value);
- if (!err) {
- if (json_output)
- print_entry_json(&info, key, value);
- else
+ if (err) {
+ if (errno == ENOENT) {
+ if (json_output) {
+ jsonw_null(json_wtr);
+ } else {
+ printf("key:\n");
+ fprint_hex(stdout, key, info.key_size, " ");
+ printf("\n\nNot found\n");
+ }
+ } else {
+ p_err("lookup failed: %s", strerror(errno));
+ }
+
+ goto exit_free;
+ }
+
+ /* here means bpf_map_lookup_elem() succeeded */
+ btf = get_btf(&info);
+ 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 if (errno == ENOENT) {
- if (json_output) {
- jsonw_null(json_wtr);
} else {
- printf("key:\n");
- fprint_hex(stdout, key, info.key_size, " ");
- printf("\n\nNot found\n");
+ 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 {
- p_err("lookup failed: %s", strerror(errno));
+ print_entry_plain(&info, key, value);
}
exit_free:
free(key);
free(value);
close(fd);
+ btf__free(btf);
return err;
}
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH bpf-next v3 2/3] bpf: btf: add btf print functionality
2018-07-08 20:30 ` [PATCH bpf-next v3 2/3] bpf: btf: add btf print functionality Okash Khawaja
@ 2018-07-10 3:56 ` Jakub Kicinski
2018-07-10 15:05 ` Okash Khawaja
0 siblings, 1 reply; 6+ messages in thread
From: Jakub Kicinski @ 2018-07-10 3:56 UTC (permalink / raw)
To: Okash Khawaja
Cc: Daniel Borkmann, Martin KaFai Lau, Alexei Starovoitov,
Yonghong Song, Quentin Monnet, David S. Miller, netdev,
kernel-team, linux-kernel
On Sun, 8 Jul 2018 13:30:04 -0700, Okash Khawaja wrote:
> This consumes functionality exported in the previous patch. It does the
> main job of printing with BTF data. This is used in the following patch
> to provide a more readable output of a map's dump. It relies on
> json_writer to do json printing. Below is sample output where map keys
> are ints and values are of type struct A:
>
> typedef int int_type;
> enum E {
> E0,
> E1,
> };
>
> struct B {
> int x;
> int y;
> };
>
> struct A {
> int m;
> unsigned long long n;
> char o;
> int p[8];
> int q[4][8];
> enum E r;
> void *s;
> struct B t;
> const int u;
> int_type v;
> unsigned int w1: 3;
> unsigned int w2: 3;
> };
>
> $ sudo bpftool map dump id 14
> [{
> "key": 0,
> "value": {
> "m": 1,
> "n": 2,
> "o": "c",
> "p": [15,16,17,18,15,16,17,18
> ],
> "q": [[25,26,27,28,25,26,27,28
> ],[35,36,37,38,35,36,37,38
> ],[45,46,47,48,45,46,47,48
> ],[55,56,57,58,55,56,57,58
> ]
> ],
> "r": 1,
> "s": 0x7ffd80531cf8,
> "t": {
> "x": 5,
> "y": 10
> },
> "u": 100,
> "v": 20,
> "w1": 0x7,
> "w2": 0x3
> }
> }
> ]
>
> This patch uses json's {} and [] to imply struct/union and array. More
> explicit information can be added later. For example, a command line
> option can be introduced to print whether a key or value is struct
> or union, name of a struct etc. This will however come at the expense
> of duplicating info when, for example, printing an array of structs.
> enums are printed as ints without their names.
>
> Signed-off-by: Okash Khawaja <osk@fb.com>
> Acked-by: Martin KaFai Lau <kafai@fb.com>
>
> ---
> tools/bpf/bpftool/btf_dumper.c | 253 +++++++++++++++++++++++++++++++++++++++++
> tools/bpf/bpftool/main.h | 15 ++
> 2 files changed, 268 insertions(+)
>
> --- /dev/null
> +++ b/tools/bpf/bpftool/btf_dumper.c
> @@ -0,0 +1,253 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/* Copyright (c) 2018 Facebook */
> +
> +#include <linux/btf.h>
> +#include <linux/err.h>
> +#include <stdio.h> /* for (FILE *) used by json_writer */
> +#include <linux/bitops.h>
> +#include <string.h>
> +#include <ctype.h>
fwiw: the preferred ordering would have been:
#include <ctype.h>
#include <stdio.h> /* for (FILE *) used by json_writer */
#include <string.h>
#include <linux/bitops.h>
#include <linux/btf.h>
#include <linux/err.h>
> +#include "btf.h"
> +#include "json_writer.h"
> +#include "main.h"
> +
> +#define BITS_PER_BYTE_MASK (BITS_PER_BYTE - 1)
> +#define BITS_PER_BYTE_MASKED(bits) ((bits) & BITS_PER_BYTE_MASK)
> +#define BITS_ROUNDDOWN_BYTES(bits) ((bits) >> 3)
> +#define BITS_ROUNDUP_BYTES(bits) \
> + (BITS_ROUNDDOWN_BYTES(bits) + !!BITS_PER_BYTE_MASKED(bits))
> +const int one = 1;
> +#define is_big_endian() ((*(char *)&one) == 0)
Could we try to do this at compilation time? Without the variable? :(
#include <asm/byteorder.h>
#if defined(__BYTE_ORDER) ? __BYTE_ORDER == __BIG_ENDIAN : defined(__BIG_ENDIAN)
return true;
#else
return false;
#endif
We could also just include endian.h, but since it's a non-standard
extension perhaps using kernel header is a safer bet.
> +static int btf_dumper_do_type(const struct btf_dumper *d, __u32 type_id,
> + __u8 bit_offset, const void *data);
> +
> +static void btf_dumper_ptr(const void *data, json_writer_t *jw,
> + bool is_plain_text)
> +{
> + if (is_plain_text)
> + jsonw_printf(jw, "%p", *((unsigned long *)data));
> + else
> + jsonw_printf(jw, "%u", *((unsigned long *)data));
nit: I think you missed these parenthesis
> +}
> +
> +static void btf_dumper_int_bits(__u32 int_type, __u8 bit_offset,
> + const void *data, json_writer_t *jw,
> + bool is_plain_text)
> +{
> + int left_shift_bits, right_shift_bits;
> + int nr_bits = BTF_INT_BITS(int_type);
> + int total_bits_offset;
> + int bytes_to_copy;
> + int bits_to_copy;
> + __u64 print_num;
> +
> + total_bits_offset = bit_offset + BTF_INT_OFFSET(int_type);
> + data += BITS_ROUNDDOWN_BYTES(total_bits_offset);
> + bit_offset = BITS_PER_BYTE_MASKED(total_bits_offset);
> + bits_to_copy = bit_offset + nr_bits;
> + bytes_to_copy = BITS_ROUNDUP_BYTES(bits_to_copy);
> +
> + print_num = 0;
> + memcpy(&print_num, data, bytes_to_copy);
> + if (is_big_endian()) {
> + left_shift_bits = bit_offset;
> + right_shift_bits = 64 - nr_bits;
> + } else {
> + left_shift_bits = 64 - bits_to_copy;
> + right_shift_bits = 64 - nr_bits;
> + }
Or you can just put the #if here, since it's the only use.
> + print_num <<= left_shift_bits;
> + print_num >>= right_shift_bits;
> + if (is_plain_text)
> + jsonw_printf(jw, "0x%llx", print_num);
> + else
> + jsonw_printf(jw, "%llu", print_num);
> +}
> +
> +static int btf_dumper_int(const struct btf_type *t, __u8 bit_offset,
> + const void *data, json_writer_t *jw,
> + bool is_plain_text)
> +{
> + __u32 *int_type;
> + __u32 nr_bits;
> +
> + int_type = (__u32 *)(t + 1);
> + nr_bits = BTF_INT_BITS(*int_type);
> + /* if this is bit field */
> + if (bit_offset || BTF_INT_OFFSET(*int_type) ||
> + BITS_PER_BYTE_MASKED(nr_bits)) {
> + btf_dumper_int_bits(*int_type, bit_offset, data, jw,
> + is_plain_text);
> + return 0;
> + }
> +
> + switch (BTF_INT_ENCODING(*int_type)) {
> + case 0:
> + if (BTF_INT_BITS(*int_type) == 64)
> + jsonw_printf(jw, "%lu", *((__u64 *)data));
nit: more parenthesis here
> + else if (BTF_INT_BITS(*int_type) == 32)
> + jsonw_printf(jw, "%u", *((__u32 *)data));
> + else if (BTF_INT_BITS(*int_type) == 16)
> + jsonw_printf(jw, "%hu", *((__u16 *)data));
> + else if (BTF_INT_BITS(*int_type) == 8)
> + jsonw_printf(jw, "%hhu", *((__u8 *)data));
> + else
> + btf_dumper_int_bits(*int_type, bit_offset, data, jw,
> + is_plain_text);
> + break;
> + case BTF_INT_SIGNED:
> + if (BTF_INT_BITS(*int_type) == 64)
> + jsonw_printf(jw, "%ld", *((long long *)data));
> + else if (BTF_INT_BITS(*int_type) == 32)
> + jsonw_printf(jw, "%d", *((int *)data));
> + else if (BTF_INT_BITS(*int_type) == 16)
> + jsonw_printf(jw, "%hd", *((short *)data));
> + else if (BTF_INT_BITS(*int_type) == 8)
> + jsonw_printf(jw, "%hhd", *((char *)data));
> + else
> + btf_dumper_int_bits(*int_type, bit_offset, data, jw,
> + is_plain_text);
> + break;
> + case BTF_INT_CHAR:
> + if (*((char *)data) == '\0')
nit: here too, etc..
> + jsonw_null(jw);
I don't think the null is good. I thought I mentioned that? Look for
example at Python:
>>> import json
>>> thing = json.loads('{"a": [97, 98, 99, 100]}')
>>> bytearray(thing["str"]).decode('utf-8')
'abcd'
>>> "".join(map(chr, thing["str"]))
'abcd'
>>> thing = json.loads('{"str": [97, 98, 99, 100, null]}')
>>> bytearray(thing["str"]).decode('utf-8')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: an integer is required
>>> "".join(map(chr, thing["str"]))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: an integer is required (got type NoneType)
If you start putting nulls into the array the conversion to a string
will become more difficult, won't it? Do you have a use case where
this helps? Maybe my Python-foo is not strong enough?
> + else if (isprint(*((char *)data)))
> + jsonw_printf(jw, "\"%c\"", *((char *)data));
> + else
> + if (is_plain_text)
> + jsonw_printf(jw, "0x%hhx", *((char *)data));
> + else
> + jsonw_printf(jw, "\"\\u00%02hhx\"",
> + *((char *)data));
> + break;
> + case BTF_INT_BOOL:
> + jsonw_bool(jw, *((int *)data));
> + break;
> + default:
> + /* shouldn't happen */
> + return -EINVAL;
> + }
> +
> + return 0;
> +}
> +
> +static int btf_dumper_struct(const struct btf_dumper *d, __u32 type_id,
> + const void *data)
> +{
> + const struct btf_type *t;
> + struct btf_member *m;
> + int ret = 0;
> + int i, vlen;
> +
> + t = btf__type_by_id(d->btf, type_id);
> + if (!t)
> + return -EINVAL;
> +
> + vlen = BTF_INFO_VLEN(t->info);
> + jsonw_start_object(d->jw);
> + m = (struct btf_member *)(t + 1);
> +
> + for (i = 0; i < vlen; i++) {
> + const void *data_off = data +
> + BITS_ROUNDDOWN_BYTES(m[i].offset);
nit: empty line between variable declaration and code, perhaps also
don't init inline since it doesn't fit that way?
> + jsonw_name(d->jw, btf__name_by_offset(d->btf, m[i].name_off));
> + ret = btf_dumper_do_type(d, m[i].type,
> + BITS_PER_BYTE_MASKED(m[i].offset),
> + data_off);
> + if (ret)
> + break;
> + }
> +
> + jsonw_end_object(d->jw);
> +
> + return ret;
> +}
Thanks for all the changes you've made so far!
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH bpf-next v3 3/3] bpf: btf: print map dump and lookup with btf info
2018-07-08 20:30 ` [PATCH bpf-next v3 3/3] bpf: btf: print map dump and lookup with btf info Okash Khawaja
@ 2018-07-10 4:06 ` Jakub Kicinski
0 siblings, 0 replies; 6+ messages in thread
From: Jakub Kicinski @ 2018-07-10 4:06 UTC (permalink / raw)
To: Okash Khawaja
Cc: Daniel Borkmann, Martin KaFai Lau, Alexei Starovoitov,
Yonghong Song, Quentin Monnet, David S. Miller, netdev,
kernel-team, linux-kernel
On Sun, 8 Jul 2018 13:30:05 -0700, Okash Khawaja wrote:
> 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>
> +static struct btf *get_btf(struct bpf_map_info *map_info)
> +{
> + struct bpf_btf_info btf_info = { 0 };
> + __u32 len = sizeof(btf_info);
> + struct btf *btf = NULL;
> + __u32 last_size;
> + int btf_fd;
> + void *ptr;
> + int err;
> +
> + btf_fd = bpf_btf_get_fd_by_id(map_info->btf_id);
> + if (btf_fd < 0)
> + return NULL;
> +
> + /* 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) {
> + p_err("unable to allocate memory for debug info");
> + goto exit_free;
I don't think we can continue working after a p_err() call :S
Or p_info() for that matter. Something else may call p_err() again and
we'll end up with multiple "error" members in JSON.
Could you return an error and make the callers fail where you do goto
exit_free? The case there is no BTF is okay to return NULL, but other
cases should really not happen, and I think it's OK to just error out
completely.
> + }
> +
> + 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) {
> + p_err("unable to re-allocate memory for debug info");
> + 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) {
> + p_info("can't get btf info. debug info won't be displayed. error: %s",
> + err ? strerror(errno) : "exceeds size retry");
> + goto exit_free;
> + }
> +
> + btf = btf__new((__u8 *)btf_info.btf,
> + btf_info.btf_size, NULL);
> + if (IS_ERR(btf)) {
> + p_info("error when initialising btf: %s\n",
> + strerror(PTR_ERR(btf)));
> + btf = NULL;
> + }
> +
> +exit_free:
> + close(btf_fd);
> + free(ptr);
> +
> + return btf;
> +}
> @@ -549,9 +681,18 @@ 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
> - print_entry_plain(&info, key, value);
> + if (btf) {
> + struct btf_dumper d = {
> + .btf = btf,
> + .jw = btf_wtr,
> + .is_plain_text = true,
> + };
nit: new line missing here and in another place
> + do_dump_btf(&d, &info, key, value);
> + } else {
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH bpf-next v3 2/3] bpf: btf: add btf print functionality
2018-07-10 3:56 ` Jakub Kicinski
@ 2018-07-10 15:05 ` Okash Khawaja
0 siblings, 0 replies; 6+ messages in thread
From: Okash Khawaja @ 2018-07-10 15:05 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Daniel Borkmann, Martin KaFai Lau, Alexei Starovoitov,
Yonghong Song, Quentin Monnet, David S. Miller, netdev,
kernel-team, linux-kernel
On Mon, Jul 09, 2018 at 08:56:12PM -0700, Jakub Kicinski wrote:
> On Sun, 8 Jul 2018 13:30:04 -0700, Okash Khawaja wrote:
> > This consumes functionality exported in the previous patch. It does the
> > main job of printing with BTF data. This is used in the following patch
> > to provide a more readable output of a map's dump. It relies on
> > json_writer to do json printing. Below is sample output where map keys
> > are ints and values are of type struct A:
> >
> > typedef int int_type;
> > enum E {
> > E0,
> > E1,
> > };
> >
> > struct B {
> > int x;
> > int y;
> > };
> >
> > struct A {
> > int m;
> > unsigned long long n;
> > char o;
> > int p[8];
> > int q[4][8];
> > enum E r;
> > void *s;
> > struct B t;
> > const int u;
> > int_type v;
> > unsigned int w1: 3;
> > unsigned int w2: 3;
> > };
> >
> > $ sudo bpftool map dump id 14
> > [{
> > "key": 0,
> > "value": {
> > "m": 1,
> > "n": 2,
> > "o": "c",
> > "p": [15,16,17,18,15,16,17,18
> > ],
> > "q": [[25,26,27,28,25,26,27,28
> > ],[35,36,37,38,35,36,37,38
> > ],[45,46,47,48,45,46,47,48
> > ],[55,56,57,58,55,56,57,58
> > ]
> > ],
> > "r": 1,
> > "s": 0x7ffd80531cf8,
> > "t": {
> > "x": 5,
> > "y": 10
> > },
> > "u": 100,
> > "v": 20,
> > "w1": 0x7,
> > "w2": 0x3
> > }
> > }
> > ]
> >
> > This patch uses json's {} and [] to imply struct/union and array. More
> > explicit information can be added later. For example, a command line
> > option can be introduced to print whether a key or value is struct
> > or union, name of a struct etc. This will however come at the expense
> > of duplicating info when, for example, printing an array of structs.
> > enums are printed as ints without their names.
> >
> > Signed-off-by: Okash Khawaja <osk@fb.com>
> > Acked-by: Martin KaFai Lau <kafai@fb.com>
> >
> > ---
> > tools/bpf/bpftool/btf_dumper.c | 253 +++++++++++++++++++++++++++++++++++++++++
> > tools/bpf/bpftool/main.h | 15 ++
> > 2 files changed, 268 insertions(+)
> >
> > --- /dev/null
> > +++ b/tools/bpf/bpftool/btf_dumper.c
> > @@ -0,0 +1,253 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/* Copyright (c) 2018 Facebook */
> > +
> > +#include <linux/btf.h>
> > +#include <linux/err.h>
> > +#include <stdio.h> /* for (FILE *) used by json_writer */
> > +#include <linux/bitops.h>
> > +#include <string.h>
> > +#include <ctype.h>
>
> fwiw: the preferred ordering would have been:
>
> #include <ctype.h>
> #include <stdio.h> /* for (FILE *) used by json_writer */
> #include <string.h>
> #include <linux/bitops.h>
> #include <linux/btf.h>
> #include <linux/err.h>
>
> > +#include "btf.h"
> > +#include "json_writer.h"
> > +#include "main.h"
> > +
> > +#define BITS_PER_BYTE_MASK (BITS_PER_BYTE - 1)
> > +#define BITS_PER_BYTE_MASKED(bits) ((bits) & BITS_PER_BYTE_MASK)
> > +#define BITS_ROUNDDOWN_BYTES(bits) ((bits) >> 3)
> > +#define BITS_ROUNDUP_BYTES(bits) \
> > + (BITS_ROUNDDOWN_BYTES(bits) + !!BITS_PER_BYTE_MASKED(bits))
> > +const int one = 1;
> > +#define is_big_endian() ((*(char *)&one) == 0)
>
> Could we try to do this at compilation time? Without the variable? :(
>
> #include <asm/byteorder.h>
>
> #if defined(__BYTE_ORDER) ? __BYTE_ORDER == __BIG_ENDIAN : defined(__BIG_ENDIAN)
> return true;
> #else
> return false;
> #endif
>
> We could also just include endian.h, but since it's a non-standard
> extension perhaps using kernel header is a safer bet.
>
> > +static int btf_dumper_do_type(const struct btf_dumper *d, __u32 type_id,
> > + __u8 bit_offset, const void *data);
> > +
> > +static void btf_dumper_ptr(const void *data, json_writer_t *jw,
> > + bool is_plain_text)
> > +{
> > + if (is_plain_text)
> > + jsonw_printf(jw, "%p", *((unsigned long *)data));
> > + else
> > + jsonw_printf(jw, "%u", *((unsigned long *)data));
>
> nit: I think you missed these parenthesis
>
> > +}
> > +
>
> > +static void btf_dumper_int_bits(__u32 int_type, __u8 bit_offset,
> > + const void *data, json_writer_t *jw,
> > + bool is_plain_text)
> > +{
> > + int left_shift_bits, right_shift_bits;
> > + int nr_bits = BTF_INT_BITS(int_type);
> > + int total_bits_offset;
> > + int bytes_to_copy;
> > + int bits_to_copy;
> > + __u64 print_num;
> > +
> > + total_bits_offset = bit_offset + BTF_INT_OFFSET(int_type);
> > + data += BITS_ROUNDDOWN_BYTES(total_bits_offset);
> > + bit_offset = BITS_PER_BYTE_MASKED(total_bits_offset);
> > + bits_to_copy = bit_offset + nr_bits;
> > + bytes_to_copy = BITS_ROUNDUP_BYTES(bits_to_copy);
> > +
> > + print_num = 0;
> > + memcpy(&print_num, data, bytes_to_copy);
> > + if (is_big_endian()) {
> > + left_shift_bits = bit_offset;
> > + right_shift_bits = 64 - nr_bits;
> > + } else {
> > + left_shift_bits = 64 - bits_to_copy;
> > + right_shift_bits = 64 - nr_bits;
> > + }
>
> Or you can just put the #if here, since it's the only use.
>
> > + print_num <<= left_shift_bits;
> > + print_num >>= right_shift_bits;
> > + if (is_plain_text)
> > + jsonw_printf(jw, "0x%llx", print_num);
> > + else
> > + jsonw_printf(jw, "%llu", print_num);
> > +}
> > +
> > +static int btf_dumper_int(const struct btf_type *t, __u8 bit_offset,
> > + const void *data, json_writer_t *jw,
> > + bool is_plain_text)
> > +{
> > + __u32 *int_type;
> > + __u32 nr_bits;
> > +
> > + int_type = (__u32 *)(t + 1);
> > + nr_bits = BTF_INT_BITS(*int_type);
> > + /* if this is bit field */
> > + if (bit_offset || BTF_INT_OFFSET(*int_type) ||
> > + BITS_PER_BYTE_MASKED(nr_bits)) {
> > + btf_dumper_int_bits(*int_type, bit_offset, data, jw,
> > + is_plain_text);
> > + return 0;
> > + }
> > +
> > + switch (BTF_INT_ENCODING(*int_type)) {
> > + case 0:
> > + if (BTF_INT_BITS(*int_type) == 64)
> > + jsonw_printf(jw, "%lu", *((__u64 *)data));
>
> nit: more parenthesis here
>
> > + else if (BTF_INT_BITS(*int_type) == 32)
> > + jsonw_printf(jw, "%u", *((__u32 *)data));
> > + else if (BTF_INT_BITS(*int_type) == 16)
> > + jsonw_printf(jw, "%hu", *((__u16 *)data));
> > + else if (BTF_INT_BITS(*int_type) == 8)
> > + jsonw_printf(jw, "%hhu", *((__u8 *)data));
> > + else
> > + btf_dumper_int_bits(*int_type, bit_offset, data, jw,
> > + is_plain_text);
> > + break;
> > + case BTF_INT_SIGNED:
> > + if (BTF_INT_BITS(*int_type) == 64)
> > + jsonw_printf(jw, "%ld", *((long long *)data));
> > + else if (BTF_INT_BITS(*int_type) == 32)
> > + jsonw_printf(jw, "%d", *((int *)data));
> > + else if (BTF_INT_BITS(*int_type) == 16)
> > + jsonw_printf(jw, "%hd", *((short *)data));
> > + else if (BTF_INT_BITS(*int_type) == 8)
> > + jsonw_printf(jw, "%hhd", *((char *)data));
> > + else
> > + btf_dumper_int_bits(*int_type, bit_offset, data, jw,
> > + is_plain_text);
> > + break;
> > + case BTF_INT_CHAR:
> > + if (*((char *)data) == '\0')
>
> nit: here too, etc..
>
> > + jsonw_null(jw);
>
> I don't think the null is good. I thought I mentioned that?
yes! i intended but missed doing that amongst the several other changes :)
> Look for
> example at Python:
>
> >>> import json
> >>> thing = json.loads('{"a": [97, 98, 99, 100]}')
> >>> bytearray(thing["str"]).decode('utf-8')
> 'abcd'
> >>> "".join(map(chr, thing["str"]))
> 'abcd'
> >>> thing = json.loads('{"str": [97, 98, 99, 100, null]}')
> >>> bytearray(thing["str"]).decode('utf-8')
> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> TypeError: an integer is required
> >>> "".join(map(chr, thing["str"]))
> Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> TypeError: an integer is required (got type NoneType)
>
> If you start putting nulls into the array the conversion to a string
> will become more difficult, won't it? Do you have a use case where
> this helps? Maybe my Python-foo is not strong enough?
>
> > + else if (isprint(*((char *)data)))
> > + jsonw_printf(jw, "\"%c\"", *((char *)data));
> > + else
> > + if (is_plain_text)
> > + jsonw_printf(jw, "0x%hhx", *((char *)data));
> > + else
> > + jsonw_printf(jw, "\"\\u00%02hhx\"",
> > + *((char *)data));
> > + break;
> > + case BTF_INT_BOOL:
> > + jsonw_bool(jw, *((int *)data));
> > + break;
> > + default:
> > + /* shouldn't happen */
> > + return -EINVAL;
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +static int btf_dumper_struct(const struct btf_dumper *d, __u32 type_id,
> > + const void *data)
> > +{
> > + const struct btf_type *t;
> > + struct btf_member *m;
> > + int ret = 0;
> > + int i, vlen;
> > +
> > + t = btf__type_by_id(d->btf, type_id);
> > + if (!t)
> > + return -EINVAL;
> > +
> > + vlen = BTF_INFO_VLEN(t->info);
> > + jsonw_start_object(d->jw);
> > + m = (struct btf_member *)(t + 1);
> > +
> > + for (i = 0; i < vlen; i++) {
> > + const void *data_off = data +
> > + BITS_ROUNDDOWN_BYTES(m[i].offset);
>
> nit: empty line between variable declaration and code, perhaps also
> don't init inline since it doesn't fit that way?
>
> > + jsonw_name(d->jw, btf__name_by_offset(d->btf, m[i].name_off));
> > + ret = btf_dumper_do_type(d, m[i].type,
> > + BITS_PER_BYTE_MASKED(m[i].offset),
> > + data_off);
> > + if (ret)
> > + break;
> > + }
> > +
> > + jsonw_end_object(d->jw);
> > +
> > + return ret;
> > +}
>
> Thanks for all the changes you've made so far!
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2018-07-10 15:05 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2018-07-08 20:30 [PATCH bpf-next v3 0/3] bpf: btf: print bpftool map data with btf Okash Khawaja
2018-07-08 20:30 ` [PATCH bpf-next v3 2/3] bpf: btf: add btf print functionality Okash Khawaja
2018-07-10 3:56 ` Jakub Kicinski
2018-07-10 15:05 ` Okash Khawaja
2018-07-08 20:30 ` [PATCH bpf-next v3 3/3] bpf: btf: print map dump and lookup with btf info Okash Khawaja
2018-07-10 4:06 ` Jakub Kicinski
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).