Hi, Here are the changes from v3: patch 2: - use compile time check for big endian - remove extra parentheses - don't inline declaration of `data` in loop inside btf_dumper_struct() - don't print null when char is 0 patch 3: - return error from get_btf() - add new line after instantiating struct btf_dumper Thanks, Okash
[-- Attachment #1: 02-add-btf-dump-map.patch --] [-- Type: text/plain, Size: 9432 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> --- tools/bpf/bpftool/btf_dumper.c | 248 +++++++++++++++++++++++++++++++++++++++++ tools/bpf/bpftool/main.h | 15 ++ 2 files changed, 263 insertions(+) --- /dev/null +++ b/tools/bpf/bpftool/btf_dumper.c @@ -0,0 +1,248 @@ +// 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)) + +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); +#ifdef __BIG_ENDIAN_BITFIELD + left_shift_bits = bit_offset; +#else + left_shift_bits = 64 - bits_to_copy; +#endif + 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 (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; + const void *data_off; + 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++) { + 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
[-- Attachment #1: 03-json-print-btf-info-for-map --] [-- Type: text/plain, Size: 8314 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 | 217 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 201 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,109 @@ 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 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_ 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 - 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 +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 **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 +813,60 @@ 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 */ + 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 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; }
Thank you for all the changes made so far. On Tue, 10 Jul 2018 20:21:10 -0700, Okash Khawaja wrote: > --- /dev/null > +++ b/tools/bpf/bpftool/btf_dumper.c > @@ -0,0 +1,248 @@ > +// 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> Again, please sort the headers the way I suggested. Otherwise as the list of includes grows it's hard to know what's already there. > +#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)) > + > +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)); Again, please drop the extraneous parens. > +} > + > +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); > +#ifdef __BIG_ENDIAN_BITFIELD > + left_shift_bits = bit_offset; > +#else > + left_shift_bits = 64 - bits_to_copy; > +#endif > + right_shift_bits = 64 - nr_bits; Please include <asm/byteorder.h> as I suggested to you previously. This is dead code right now, look: $ git diff diff --git a/tools/bpf/bpftool/btf_dumper.c b/tools/bpf/bpftool/btf_dumper.c index c64465094b92..045add07b721 100644 --- a/tools/bpf/bpftool/btf_dumper.c +++ b/tools/bpf/bpftool/btf_dumper.c @@ -91,7 +91,8 @@ static void btf_dumper_int_bits(__u32 int_type, __u8 bit_offset, print_num = 0; memcpy(&print_num, data, bytes_to_copy); -#ifdef __BIG_ENDIAN_BITFIELD +#ifndef __LITTLE_ENDIAN_BITFIELD +#error "abc" left_shift_bits = bit_offset; #else left_shift_bits = 64 - bits_to_copy; $ make -C tools/bpf/bpftool/ CC=gcc-8 make: Entering directory '/home/jkicinski/devel/linux/tools/bpf/bpftool' CC btf_dumper.o btf_dumper.c: In function ‘btf_dumper_int_bits’: btf_dumper.c:95:2: error: #error "abc" #error "abc" ^~~~~ Makefile:96: recipe for target 'btf_dumper.o' failed make: *** [btf_dumper.o] Error 1 make: Leaving directory '/home/jkicinski/devel/linux/tools/bpf/bpftool'
On Tue, 10 Jul 2018 20:21:11 -0700, Okash Khawaja wrote: > + if (err || btf_info.btf_size > last_size) { > + err = errno; errno may not be set in case btf_info.btf_size > last_size errno is positive, while other error return codes are negative. > + goto exit_free; > + }
On 07/11/2018 09:10 PM, Jakub Kicinski wrote:
> Thank you for all the changes made so far.
>
> On Tue, 10 Jul 2018 20:21:10 -0700, Okash Khawaja wrote:
>> --- /dev/null
>> +++ b/tools/bpf/bpftool/btf_dumper.c
>> @@ -0,0 +1,248 @@
>> +// 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>
>
> Again, please sort the headers the way I suggested. Otherwise as the
> list of includes grows it's hard to know what's already there.
>
>> +#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))
>> +
>> +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));
>
> Again, please drop the extraneous parens.
>
>> +}
>> +
>
>> +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);
>> +#ifdef __BIG_ENDIAN_BITFIELD
>> + left_shift_bits = bit_offset;
>> +#else
>> + left_shift_bits = 64 - bits_to_copy;
>> +#endif
>> + right_shift_bits = 64 - nr_bits;
>
> Please include <asm/byteorder.h> as I suggested to you previously.
> This is dead code right now, look:
>
> $ git diff
> diff --git a/tools/bpf/bpftool/btf_dumper.c b/tools/bpf/bpftool/btf_dumper.c
> index c64465094b92..045add07b721 100644
> --- a/tools/bpf/bpftool/btf_dumper.c
> +++ b/tools/bpf/bpftool/btf_dumper.c
> @@ -91,7 +91,8 @@ static void btf_dumper_int_bits(__u32 int_type, __u8 bit_offset,
>
> print_num = 0;
> memcpy(&print_num, data, bytes_to_copy);
> -#ifdef __BIG_ENDIAN_BITFIELD
> +#ifndef __LITTLE_ENDIAN_BITFIELD
> +#error "abc"
> left_shift_bits = bit_offset;
> #else
> left_shift_bits = 64 - bits_to_copy;
>
> $ make -C tools/bpf/bpftool/ CC=gcc-8
> make: Entering directory '/home/jkicinski/devel/linux/tools/bpf/bpftool'
> CC btf_dumper.o
> btf_dumper.c: In function ‘btf_dumper_int_bits’:
> btf_dumper.c:95:2: error: #error "abc"
> #error "abc"
> ^~~~~
> Makefile:96: recipe for target 'btf_dumper.o' failed
> make: *** [btf_dumper.o] Error 1
> make: Leaving directory '/home/jkicinski/devel/linux/tools/bpf/bpftool'
You could also easily test this on s390x (big endian) through a LinuxONE
test instance, this is how I usually test changes related to their JIT.
Thanks,
Daniel
On Wed, Jul 11, 2018 at 12:10:15PM -0700, Jakub Kicinski wrote: > Thank you for all the changes made so far. > > On Tue, 10 Jul 2018 20:21:10 -0700, Okash Khawaja wrote: > > --- /dev/null > > +++ b/tools/bpf/bpftool/btf_dumper.c > > @@ -0,0 +1,248 @@ > > +// 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> > > Again, please sort the headers the way I suggested. Otherwise as the > list of includes grows it's hard to know what's already there. > > > +#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)) > > + > > +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)); > > Again, please drop the extraneous parens. > > > +} > > + > > > +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); > > +#ifdef __BIG_ENDIAN_BITFIELD > > + left_shift_bits = bit_offset; > > +#else > > + left_shift_bits = 64 - bits_to_copy; > > +#endif > > + right_shift_bits = 64 - nr_bits; > > Please include <asm/byteorder.h> as I suggested to you previously. > This is dead code right now, look: Sorry, should have checked ifndef case. Will fix. Thanks. > > $ git diff > diff --git a/tools/bpf/bpftool/btf_dumper.c b/tools/bpf/bpftool/btf_dumper.c > index c64465094b92..045add07b721 100644 > --- a/tools/bpf/bpftool/btf_dumper.c > +++ b/tools/bpf/bpftool/btf_dumper.c > @@ -91,7 +91,8 @@ static void btf_dumper_int_bits(__u32 int_type, __u8 bit_offset, > > print_num = 0; > memcpy(&print_num, data, bytes_to_copy); > -#ifdef __BIG_ENDIAN_BITFIELD > +#ifndef __LITTLE_ENDIAN_BITFIELD > +#error "abc" > left_shift_bits = bit_offset; > #else > left_shift_bits = 64 - bits_to_copy; > > $ make -C tools/bpf/bpftool/ CC=gcc-8 > make: Entering directory '/home/jkicinski/devel/linux/tools/bpf/bpftool' > CC btf_dumper.o > btf_dumper.c: In function ‘btf_dumper_int_bits’: > btf_dumper.c:95:2: error: #error "abc" > #error "abc" > ^~~~~ > Makefile:96: recipe for target 'btf_dumper.o' failed > make: *** [btf_dumper.o] Error 1 > make: Leaving directory '/home/jkicinski/devel/linux/tools/bpf/bpftool'
On Wed, Jul 11, 2018 at 10:08:35PM +0200, Daniel Borkmann wrote: > On 07/11/2018 09:10 PM, Jakub Kicinski wrote: > > Thank you for all the changes made so far. > > > > On Tue, 10 Jul 2018 20:21:10 -0700, Okash Khawaja wrote: > >> --- /dev/null > >> +++ b/tools/bpf/bpftool/btf_dumper.c > >> @@ -0,0 +1,248 @@ > >> +// 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> > > > > Again, please sort the headers the way I suggested. Otherwise as the > > list of includes grows it's hard to know what's already there. > > > >> +#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)) > >> + > >> +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)); > > > > Again, please drop the extraneous parens. > > > >> +} > >> + > > > >> +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); > >> +#ifdef __BIG_ENDIAN_BITFIELD > >> + left_shift_bits = bit_offset; > >> +#else > >> + left_shift_bits = 64 - bits_to_copy; > >> +#endif > >> + right_shift_bits = 64 - nr_bits; > > > > Please include <asm/byteorder.h> as I suggested to you previously. > > This is dead code right now, look: > > > > $ git diff > > diff --git a/tools/bpf/bpftool/btf_dumper.c b/tools/bpf/bpftool/btf_dumper.c > > index c64465094b92..045add07b721 100644 > > --- a/tools/bpf/bpftool/btf_dumper.c > > +++ b/tools/bpf/bpftool/btf_dumper.c > > @@ -91,7 +91,8 @@ static void btf_dumper_int_bits(__u32 int_type, __u8 bit_offset, > > > > print_num = 0; > > memcpy(&print_num, data, bytes_to_copy); > > -#ifdef __BIG_ENDIAN_BITFIELD > > +#ifndef __LITTLE_ENDIAN_BITFIELD > > +#error "abc" > > left_shift_bits = bit_offset; > > #else > > left_shift_bits = 64 - bits_to_copy; > > > > $ make -C tools/bpf/bpftool/ CC=gcc-8 > > make: Entering directory '/home/jkicinski/devel/linux/tools/bpf/bpftool' > > CC btf_dumper.o > > btf_dumper.c: In function ‘btf_dumper_int_bits’: > > btf_dumper.c:95:2: error: #error "abc" > > #error "abc" > > ^~~~~ > > Makefile:96: recipe for target 'btf_dumper.o' failed > > make: *** [btf_dumper.o] Error 1 > > make: Leaving directory '/home/jkicinski/devel/linux/tools/bpf/bpftool' > > You could also easily test this on s390x (big endian) through a LinuxONE > test instance, this is how I usually test changes related to their JIT. Thanks. I've been using MIPS qemu set up. This will definitely help. > > Thanks, > Daniel