Commit a5dd4982 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'bpftool-JSON'



Jakub Kicinski says:

====================
tools: bpftool: Add JSON output to bpftool

Quentin says:

This series introduces support for JSON output to all bpftool commands. It
adds option parsing, and several options are created:

  * -j, --json     Switch to JSON output.
  * -p, --pretty   Switch to JSON and print it in a human-friendly fashion.
  * -h, --help     Print generic help message.
  * -V, --version  Print version number.

This code uses a "json_writer", which is a copy of the one written by
Stephen Hemminger in iproute2.
---
I don't know if there is an easy way to share the code for json_write
without copying the file, so I am very open to suggestions on this matter.
====================

Acked-by: default avatarAlexei Starovoitov <ast@kernel.org>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 4c4fde21 0641c3c8
Loading
Loading
Loading
Loading
+31 −13
Original line number Original line Diff line number Diff line
@@ -10,26 +10,29 @@ tool for inspection and simple manipulation of eBPF maps
SYNOPSIS
SYNOPSIS
========
========


	**bpftool** **map** *COMMAND*
	**bpftool** [*OPTIONS*] **map** *COMMAND*

	*OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] }


	*COMMANDS* :=
	*COMMANDS* :=
	{ show | dump | update | lookup | getnext | delete | pin | help }
	{ **show** | **dump** | **update** | **lookup** | **getnext** | **delete**
	| **pin** | **help** }


MAP COMMANDS
MAP COMMANDS
=============
=============


|	**bpftool** map show   [*MAP*]
|	**bpftool** **map show**   [*MAP*]
|	**bpftool** map dump    *MAP*
|	**bpftool** **map dump**    *MAP*
|	**bpftool** map update  *MAP*  key *BYTES*   value *VALUE* [*UPDATE_FLAGS*]
|	**bpftool** **map update**  *MAP*  **key** *BYTES*   **value** *VALUE* [*UPDATE_FLAGS*]
|	**bpftool** map lookup  *MAP*  key *BYTES*
|	**bpftool** **map lookup**  *MAP*  **key** *BYTES*
|	**bpftool** map getnext *MAP* [key *BYTES*]
|	**bpftool** **map getnext** *MAP* [**key** *BYTES*]
|	**bpftool** map delete  *MAP*  key *BYTES*
|	**bpftool** **map delete**  *MAP*  **key** *BYTES*
|	**bpftool** map pin     *MAP*  *FILE*
|	**bpftool** **map pin**     *MAP*  *FILE*
|	**bpftool** map help
|	**bpftool** **map help**
|
|
|	*MAP* := { id MAP_ID | pinned FILE }
|	*MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
|	*VALUE* := { BYTES | MAP | PROGRAM }
|	*VALUE* := { *BYTES* | *MAP* | *PROGRAM* }
|	*UPDATE_FLAGS* := { any | exist | noexist }
|	*UPDATE_FLAGS* := { **any** | **exist** | **noexist** }


DESCRIPTION
DESCRIPTION
===========
===========
@@ -68,6 +71,21 @@ DESCRIPTION
	**bpftool map help**
	**bpftool map help**
		  Print short help message.
		  Print short help message.


OPTIONS
=======
	-h, --help
		  Print short generic help message (similar to **bpftool help**).

	-v, --version
		  Print version number (similar to **bpftool version**).

	-j, --json
		  Generate JSON output. For commands that cannot produce JSON, this
		  option has no effect.

	-p, --pretty
		  Generate human-readable JSON output. Implies **-j**.

EXAMPLES
EXAMPLES
========
========
**# bpftool map show**
**# bpftool map show**
+73 −8
Original line number Original line Diff line number Diff line
@@ -10,13 +10,23 @@ tool for inspection and simple manipulation of eBPF progs
SYNOPSIS
SYNOPSIS
========
========


|	**bpftool** prog show [*PROG*]
	**bpftool** [*OPTIONS*] **prog** *COMMAND*
|	**bpftool** prog dump xlated *PROG* [{file *FILE* | opcodes }]

|	**bpftool** prog dump jited  *PROG* [{file *FILE* | opcodes }]
	*OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] }
|	**bpftool** prog pin *PROG* *FILE*

|	**bpftool** prog help
	*COMMANDS* :=
	{ **show** | **dump xlated** | **dump jited** | **pin** | **help** }

MAP COMMANDS
=============

|	**bpftool** **prog show** [*PROG*]
|	**bpftool** **prog dump xlated** *PROG* [{**file** *FILE* | **opcodes**}]
|	**bpftool** **prog dump jited**  *PROG* [{**file** *FILE* | **opcodes**}]
|	**bpftool** **prog pin** *PROG* *FILE*
|	**bpftool** **prog help**
|
|
|	*PROG* := { id *PROG_ID* | pinned *FILE* | tag *PROG_TAG* }
|	*PROG* := { **id** *PROG_ID* | **pinned** *FILE* | **tag** *PROG_TAG* }


DESCRIPTION
DESCRIPTION
===========
===========
@@ -50,6 +60,21 @@ DESCRIPTION
	**bpftool prog help**
	**bpftool prog help**
		  Print short help message.
		  Print short help message.


OPTIONS
=======
	-h, --help
		  Print short generic help message (similar to **bpftool help**).

	-v, --version
		  Print version number (similar to **bpftool version**).

	-j, --json
		  Generate JSON output. For commands that cannot produce JSON, this
		  option has no effect.

	-p, --pretty
		  Generate human-readable JSON output. Implies **-j**.

EXAMPLES
EXAMPLES
========
========
**# bpftool prog show**
**# bpftool prog show**
@@ -59,13 +84,33 @@ EXAMPLES
	loaded_at Sep 29/20:11  uid 0
	loaded_at Sep 29/20:11  uid 0
	xlated 528B  jited 370B  memlock 4096B  map_ids 10
	xlated 528B  jited 370B  memlock 4096B  map_ids 10


**# bpftool --json --pretty prog show**

::

    {
        "programs": [{
                "id": 10,
                "type": "xdp",
                "tag": "005a3d2123620c8b",
                "loaded_at": "Sep 29/20:11",
                "uid": 0,
                "bytes_xlated": 528,
                "jited": true,
                "bytes_jited": 370,
                "bytes_memlock": 4096,
                "map_ids": [10
                ]
            }
        ]
    }

|
|
| **# bpftool prog dump xlated id 10 file /tmp/t**
| **# bpftool prog dump xlated id 10 file /tmp/t**
| **# ls -l /tmp/t**
| **# ls -l /tmp/t**
|   -rw------- 1 root root 560 Jul 22 01:42 /tmp/t
|   -rw------- 1 root root 560 Jul 22 01:42 /tmp/t


|
**# bpftool prog dum jited tag 005a3d2123620c8b**
| **# bpftool prog dum jited pinned /sys/fs/bpf/prog**


::
::


@@ -75,6 +120,26 @@ EXAMPLES
    sub    $0x28,%rbp
    sub    $0x28,%rbp
    mov    %rbx,0x0(%rbp)
    mov    %rbx,0x0(%rbp)


|
| **# mount -t bpf none /sys/fs/bpf/**
| **# bpftool prog pin id 10 /sys/fs/bpf/prog**
| **# ls -l /sys/fs/bpf/**
|   -rw------- 1 root root 0 Jul 22 01:43 prog

**# bpftool prog dum jited pinned /sys/fs/bpf/prog opcodes**

::

    push   %rbp
    55
    mov    %rsp,%rbp
    48 89 e5
    sub    $0x228,%rsp
    48 81 ec 28 02 00 00
    sub    $0x28,%rbp
    48 83 ed 28
    mov    %rbx,0x0(%rbp)
    48 89 5d 00




SEE ALSO
SEE ALSO
+25 −5
Original line number Original line Diff line number Diff line
@@ -10,18 +10,23 @@ tool for inspection and simple manipulation of eBPF programs and maps
SYNOPSIS
SYNOPSIS
========
========


	**bpftool** *OBJECT* { *COMMAND* | help }
	**bpftool** [*OPTIONS*] *OBJECT* { *COMMAND* | **help** }


	**bpftool** batch file *FILE*
	**bpftool** **batch file** *FILE*


	**bpftool** version
	**bpftool** **version**


	*OBJECT* := { **map** | **program** }
	*OBJECT* := { **map** | **program** }


	*OPTIONS* := { { **-V** | **--version** } | { **-h** | **--help** }
	| { **-j** | **--json** } [{ **-p** | **--pretty** }] }

	*MAP-COMMANDS* :=
	*MAP-COMMANDS* :=
	{ show | dump | update | lookup | getnext | delete | pin | help }
	{ **show** | **dump** | **update** | **lookup** | **getnext** | **delete**
	| **pin** | **help** }


	*PROG-COMMANDS* := { show | dump jited | dump xlated | pin | help }
	*PROG-COMMANDS* := { **show** | **dump jited** | **dump xlated** | **pin**
	| **help** }


DESCRIPTION
DESCRIPTION
===========
===========
@@ -31,6 +36,21 @@ DESCRIPTION
	Note that format of the output of all tools is not guaranteed to be
	Note that format of the output of all tools is not guaranteed to be
	stable and should not be depended upon.
	stable and should not be depended upon.


OPTIONS
=======
	-h, --help
		  Print short help message (similar to **bpftool help**).

	-v, --version
		  Print version number (similar to **bpftool version**).

	-j, --json
		  Generate JSON output. For commands that cannot produce JSON, this
		  option has no effect.

	-p, --pretty
		  Generate human-readable JSON output. Implies **-j**.

SEE ALSO
SEE ALSO
========
========
	**bpftool-map**\ (8), **bpftool-prog**\ (8)
	**bpftool-map**\ (8), **bpftool-prog**\ (8)
+23 −13
Original line number Original line Diff line number Diff line
@@ -66,7 +66,7 @@ int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type)


	fd = bpf_obj_get(path);
	fd = bpf_obj_get(path);
	if (fd < 0) {
	if (fd < 0) {
		err("bpf obj get (%s): %s\n", path,
		p_err("bpf obj get (%s): %s", path,
		      errno == EACCES && !is_bpffs(dirname(path)) ?
		      errno == EACCES && !is_bpffs(dirname(path)) ?
		    "directory not in bpf file system (bpffs)" :
		    "directory not in bpf file system (bpffs)" :
		    strerror(errno));
		    strerror(errno));
@@ -79,7 +79,7 @@ int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type)
		return type;
		return type;
	}
	}
	if (type != exp_type) {
	if (type != exp_type) {
		err("incorrect object type: %s\n", get_fd_type_name(type));
		p_err("incorrect object type: %s", get_fd_type_name(type));
		close(fd);
		close(fd);
		return -1;
		return -1;
	}
	}
@@ -95,14 +95,14 @@ int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32))
	int fd;
	int fd;


	if (!is_prefix(*argv, "id")) {
	if (!is_prefix(*argv, "id")) {
		err("expected 'id' got %s\n", *argv);
		p_err("expected 'id' got %s", *argv);
		return -1;
		return -1;
	}
	}
	NEXT_ARG();
	NEXT_ARG();


	id = strtoul(*argv, &endptr, 0);
	id = strtoul(*argv, &endptr, 0);
	if (*endptr) {
	if (*endptr) {
		err("can't parse %s as ID\n", *argv);
		p_err("can't parse %s as ID", *argv);
		return -1;
		return -1;
	}
	}
	NEXT_ARG();
	NEXT_ARG();
@@ -112,14 +112,14 @@ int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32))


	fd = get_fd_by_id(id);
	fd = get_fd_by_id(id);
	if (fd < 0) {
	if (fd < 0) {
		err("can't get prog by id (%u): %s\n", id, strerror(errno));
		p_err("can't get prog by id (%u): %s", id, strerror(errno));
		return -1;
		return -1;
	}
	}


	err = bpf_obj_pin(fd, *argv);
	err = bpf_obj_pin(fd, *argv);
	close(fd);
	close(fd);
	if (err) {
	if (err) {
		err("can't pin the object (%s): %s\n", *argv,
		p_err("can't pin the object (%s): %s", *argv,
		      errno == EACCES && !is_bpffs(dirname(*argv)) ?
		      errno == EACCES && !is_bpffs(dirname(*argv)) ?
		    "directory not in bpf file system (bpffs)" :
		    "directory not in bpf file system (bpffs)" :
		    strerror(errno));
		    strerror(errno));
@@ -153,11 +153,11 @@ int get_fd_type(int fd)


	n = readlink(path, buf, sizeof(buf));
	n = readlink(path, buf, sizeof(buf));
	if (n < 0) {
	if (n < 0) {
		err("can't read link type: %s\n", strerror(errno));
		p_err("can't read link type: %s", strerror(errno));
		return -1;
		return -1;
	}
	}
	if (n == sizeof(path)) {
	if (n == sizeof(path)) {
		err("can't read link type: path too long!\n");
		p_err("can't read link type: path too long!");
		return -1;
		return -1;
	}
	}


@@ -181,7 +181,7 @@ char *get_fdinfo(int fd, const char *key)


	fdi = fopen(path, "r");
	fdi = fopen(path, "r");
	if (!fdi) {
	if (!fdi) {
		err("can't open fdinfo: %s\n", strerror(errno));
		p_err("can't open fdinfo: %s", strerror(errno));
		return NULL;
		return NULL;
	}
	}


@@ -196,7 +196,7 @@ char *get_fdinfo(int fd, const char *key)


		value = strchr(line, '\t');
		value = strchr(line, '\t');
		if (!value || !value[1]) {
		if (!value || !value[1]) {
			err("malformed fdinfo!?\n");
			p_err("malformed fdinfo!?");
			free(line);
			free(line);
			return NULL;
			return NULL;
		}
		}
@@ -209,8 +209,18 @@ char *get_fdinfo(int fd, const char *key)
		return line;
		return line;
	}
	}


	err("key '%s' not found in fdinfo\n", key);
	p_err("key '%s' not found in fdinfo", key);
	free(line);
	free(line);
	fclose(fdi);
	fclose(fdi);
	return NULL;
	return NULL;
}
}

void print_hex_data_json(uint8_t *data, size_t len)
{
	unsigned int i;

	jsonw_start_array(json_wtr);
	for (i = 0; i < len; i++)
		jsonw_printf(json_wtr, "\"0x%02hhx\"", data[i]);
	jsonw_end_array(json_wtr);
}
+80 −6
Original line number Original line Diff line number Diff line
@@ -10,6 +10,7 @@
 * Licensed under the GNU General Public License, version 2.0 (GPLv2)
 * Licensed under the GNU General Public License, version 2.0 (GPLv2)
 */
 */


#include <stdarg.h>
#include <stdint.h>
#include <stdint.h>
#include <stdio.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdlib.h>
@@ -21,6 +22,9 @@
#include <sys/types.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/stat.h>


#include "json_writer.h"
#include "main.h"

static void get_exec_path(char *tpath, size_t size)
static void get_exec_path(char *tpath, size_t size)
{
{
	ssize_t len;
	ssize_t len;
@@ -39,6 +43,38 @@ static void get_exec_path(char *tpath, size_t size)
	free(path);
	free(path);
}
}


static int oper_count;
static int fprintf_json(void *out, const char *fmt, ...)
{
	va_list ap;
	char *s;

	va_start(ap, fmt);
	if (!oper_count) {
		int i;

		s = va_arg(ap, char *);

		/* Strip trailing spaces */
		i = strlen(s) - 1;
		while (s[i] == ' ')
			s[i--] = '\0';

		jsonw_string_field(json_wtr, "operation", s);
		jsonw_name(json_wtr, "operands");
		jsonw_start_array(json_wtr);
		oper_count++;
	} else if (!strcmp(fmt, ",")) {
		   /* Skip */
	} else {
		s = va_arg(ap, char *);
		jsonw_string(json_wtr, s);
		oper_count++;
	}
	va_end(ap);
	return 0;
}

void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes)
void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes)
{
{
	disassembler_ftype disassemble;
	disassembler_ftype disassemble;
@@ -57,7 +93,12 @@ void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes)
	assert(bfdf);
	assert(bfdf);
	assert(bfd_check_format(bfdf, bfd_object));
	assert(bfd_check_format(bfdf, bfd_object));


	init_disassemble_info(&info, stdout, (fprintf_ftype) fprintf);
	if (json_output)
		init_disassemble_info(&info, stdout,
				      (fprintf_ftype) fprintf_json);
	else
		init_disassemble_info(&info, stdout,
				      (fprintf_ftype) fprintf);
	info.arch = bfd_get_arch(bfdf);
	info.arch = bfd_get_arch(bfdf);
	info.mach = bfd_get_mach(bfdf);
	info.mach = bfd_get_mach(bfdf);
	info.buffer = image;
	info.buffer = image;
@@ -68,20 +109,53 @@ void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes)
	disassemble = disassembler(bfdf);
	disassemble = disassembler(bfdf);
	assert(disassemble);
	assert(disassemble);


	if (json_output)
		jsonw_start_array(json_wtr);
	do {
	do {
		if (json_output) {
			jsonw_start_object(json_wtr);
			oper_count = 0;
			jsonw_name(json_wtr, "pc");
			jsonw_printf(json_wtr, "\"0x%x\"", pc);
		} else {
			printf("%4x:\t", pc);
			printf("%4x:\t", pc);
		}


		count = disassemble(pc, &info);
		count = disassemble(pc, &info);
		if (json_output) {
			/* Operand array, was started in fprintf_json. Before
			 * that, make sure we have a _null_ value if no operand
			 * other than operation code was present.
			 */
			if (oper_count == 1)
				jsonw_null(json_wtr);
			jsonw_end_array(json_wtr);
		}


		if (opcodes) {
		if (opcodes) {
			if (json_output) {
				jsonw_name(json_wtr, "opcodes");
				jsonw_start_array(json_wtr);
				for (i = 0; i < count; ++i)
					jsonw_printf(json_wtr, "\"0x%02hhx\"",
						     (uint8_t)image[pc + i]);
				jsonw_end_array(json_wtr);
			} else {
				printf("\n\t");
				printf("\n\t");
				for (i = 0; i < count; ++i)
				for (i = 0; i < count; ++i)
				printf("%02x ", (uint8_t) image[pc + i]);
					printf("%02x ",
					       (uint8_t)image[pc + i]);
			}
		}
		}
		if (json_output)
			jsonw_end_object(json_wtr);
		else
			printf("\n");
			printf("\n");


		pc += count;
		pc += count;
	} while (count > 0 && pc < len);
	} while (count > 0 && pc < len);
	if (json_output)
		jsonw_end_array(json_wtr);


	bfd_close(bfdf);
	bfd_close(bfdf);
}
}
Loading