linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH bpf-next v4 0/3] bpf: btf: print bpftool map data with btf
@ 2018-07-11  3:21 Okash Khawaja
  2018-07-11  3:21 ` [PATCH bpf-next v4 2/3] bpf: btf: add btf print functionality Okash Khawaja
  2018-07-11  3:21 ` [PATCH bpf-next v4 3/3] bpf: btf: print map dump and lookup with btf info Okash Khawaja
  0 siblings, 2 replies; 8+ messages in thread
From: Okash Khawaja @ 2018-07-11  3:21 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,

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

^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH bpf-next v4 2/3] bpf: btf: add btf print functionality
  2018-07-11  3:21 [PATCH bpf-next v4 0/3] bpf: btf: print bpftool map data with btf Okash Khawaja
@ 2018-07-11  3:21 ` Okash Khawaja
  2018-07-11 19:10   ` Jakub Kicinski
  2018-07-11  3:21 ` [PATCH bpf-next v4 3/3] bpf: btf: print map dump and lookup with btf info Okash Khawaja
  1 sibling, 1 reply; 8+ messages in thread
From: Okash Khawaja @ 2018-07-11  3:21 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: 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


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH bpf-next v4 3/3] bpf: btf: print map dump and lookup with btf info
  2018-07-11  3:21 [PATCH bpf-next v4 0/3] bpf: btf: print bpftool map data with btf Okash Khawaja
  2018-07-11  3:21 ` [PATCH bpf-next v4 2/3] bpf: btf: add btf print functionality Okash Khawaja
@ 2018-07-11  3:21 ` Okash Khawaja
  2018-07-11 20:04   ` Jakub Kicinski
  1 sibling, 1 reply; 8+ messages in thread
From: Okash Khawaja @ 2018-07-11  3:21 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: 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;
 }


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH bpf-next v4 2/3] bpf: btf: add btf print functionality
  2018-07-11  3:21 ` [PATCH bpf-next v4 2/3] bpf: btf: add btf print functionality Okash Khawaja
@ 2018-07-11 19:10   ` Jakub Kicinski
  2018-07-11 20:08     ` Daniel Borkmann
  2018-07-11 21:18     ` Okash Khawaja
  0 siblings, 2 replies; 8+ messages in thread
From: Jakub Kicinski @ 2018-07-11 19:10 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

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'

^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH bpf-next v4 3/3] bpf: btf: print map dump and lookup with btf info
  2018-07-11  3:21 ` [PATCH bpf-next v4 3/3] bpf: btf: print map dump and lookup with btf info Okash Khawaja
@ 2018-07-11 20:04   ` Jakub Kicinski
  0 siblings, 0 replies; 8+ messages in thread
From: Jakub Kicinski @ 2018-07-11 20:04 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 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;
> +	}

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH bpf-next v4 2/3] bpf: btf: add btf print functionality
  2018-07-11 19:10   ` Jakub Kicinski
@ 2018-07-11 20:08     ` Daniel Borkmann
  2018-07-11 21:20       ` Okash Khawaja
  2018-07-11 21:18     ` Okash Khawaja
  1 sibling, 1 reply; 8+ messages in thread
From: Daniel Borkmann @ 2018-07-11 20:08 UTC (permalink / raw)
  To: Jakub Kicinski, Okash Khawaja
  Cc: Martin KaFai Lau, Alexei Starovoitov, Yonghong Song,
	Quentin Monnet, David S. Miller, netdev, kernel-team,
	linux-kernel

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

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH bpf-next v4 2/3] bpf: btf: add btf print functionality
  2018-07-11 19:10   ` Jakub Kicinski
  2018-07-11 20:08     ` Daniel Borkmann
@ 2018-07-11 21:18     ` Okash Khawaja
  1 sibling, 0 replies; 8+ messages in thread
From: Okash Khawaja @ 2018-07-11 21:18 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 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'

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH bpf-next v4 2/3] bpf: btf: add btf print functionality
  2018-07-11 20:08     ` Daniel Borkmann
@ 2018-07-11 21:20       ` Okash Khawaja
  0 siblings, 0 replies; 8+ messages in thread
From: Okash Khawaja @ 2018-07-11 21:20 UTC (permalink / raw)
  To: Daniel Borkmann
  Cc: Jakub Kicinski, Martin KaFai Lau, Alexei Starovoitov,
	Yonghong Song, Quentin Monnet, David S. Miller, netdev,
	kernel-team, linux-kernel

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

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2018-07-11 21:21 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-07-11  3:21 [PATCH bpf-next v4 0/3] bpf: btf: print bpftool map data with btf Okash Khawaja
2018-07-11  3:21 ` [PATCH bpf-next v4 2/3] bpf: btf: add btf print functionality Okash Khawaja
2018-07-11 19:10   ` Jakub Kicinski
2018-07-11 20:08     ` Daniel Borkmann
2018-07-11 21:20       ` Okash Khawaja
2018-07-11 21:18     ` Okash Khawaja
2018-07-11  3:21 ` [PATCH bpf-next v4 3/3] bpf: btf: print map dump and lookup with btf info Okash Khawaja
2018-07-11 20:04   ` 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).