mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-13 22:25:03 +00:00
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: Alexei Starovoitov <ast@kernel.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
a5dd498287
11 changed files with 1214 additions and 189 deletions
|
@ -10,26 +10,29 @@ tool for inspection and simple manipulation of eBPF maps
|
|||
SYNOPSIS
|
||||
========
|
||||
|
||||
**bpftool** **map** *COMMAND*
|
||||
**bpftool** [*OPTIONS*] **map** *COMMAND*
|
||||
|
||||
*OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] }
|
||||
|
||||
*COMMANDS* :=
|
||||
{ show | dump | update | lookup | getnext | delete | pin | help }
|
||||
{ **show** | **dump** | **update** | **lookup** | **getnext** | **delete**
|
||||
| **pin** | **help** }
|
||||
|
||||
MAP COMMANDS
|
||||
=============
|
||||
|
||||
| **bpftool** map show [*MAP*]
|
||||
| **bpftool** map dump *MAP*
|
||||
| **bpftool** map update *MAP* key *BYTES* value *VALUE* [*UPDATE_FLAGS*]
|
||||
| **bpftool** map lookup *MAP* key *BYTES*
|
||||
| **bpftool** map getnext *MAP* [key *BYTES*]
|
||||
| **bpftool** map delete *MAP* key *BYTES*
|
||||
| **bpftool** map pin *MAP* *FILE*
|
||||
| **bpftool** map help
|
||||
| **bpftool** **map show** [*MAP*]
|
||||
| **bpftool** **map dump** *MAP*
|
||||
| **bpftool** **map update** *MAP* **key** *BYTES* **value** *VALUE* [*UPDATE_FLAGS*]
|
||||
| **bpftool** **map lookup** *MAP* **key** *BYTES*
|
||||
| **bpftool** **map getnext** *MAP* [**key** *BYTES*]
|
||||
| **bpftool** **map delete** *MAP* **key** *BYTES*
|
||||
| **bpftool** **map pin** *MAP* *FILE*
|
||||
| **bpftool** **map help**
|
||||
|
|
||||
| *MAP* := { id MAP_ID | pinned FILE }
|
||||
| *VALUE* := { BYTES | MAP | PROGRAM }
|
||||
| *UPDATE_FLAGS* := { any | exist | noexist }
|
||||
| *MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
|
||||
| *VALUE* := { *BYTES* | *MAP* | *PROGRAM* }
|
||||
| *UPDATE_FLAGS* := { **any** | **exist** | **noexist** }
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
|
@ -68,6 +71,21 @@ DESCRIPTION
|
|||
**bpftool map help**
|
||||
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
|
||||
========
|
||||
**# bpftool map show**
|
||||
|
|
|
@ -10,13 +10,23 @@ tool for inspection and simple manipulation of eBPF progs
|
|||
SYNOPSIS
|
||||
========
|
||||
|
||||
| **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
|
||||
**bpftool** [*OPTIONS*] **prog** *COMMAND*
|
||||
|
||||
*OPTIONS* := { { **-j** | **--json** } [{ **-p** | **--pretty** }] }
|
||||
|
||||
*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
|
||||
===========
|
||||
|
@ -50,6 +60,21 @@ DESCRIPTION
|
|||
**bpftool prog help**
|
||||
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
|
||||
========
|
||||
**# bpftool prog show**
|
||||
|
@ -59,13 +84,33 @@ EXAMPLES
|
|||
loaded_at Sep 29/20:11 uid 0
|
||||
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**
|
||||
| **# ls -l /tmp/t**
|
||||
| -rw------- 1 root root 560 Jul 22 01:42 /tmp/t
|
||||
|
||||
|
|
||||
| **# bpftool prog dum jited pinned /sys/fs/bpf/prog**
|
||||
**# bpftool prog dum jited tag 005a3d2123620c8b**
|
||||
|
||||
::
|
||||
|
||||
|
@ -75,6 +120,26 @@ EXAMPLES
|
|||
sub $0x28,%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
|
||||
|
|
|
@ -10,18 +10,23 @@ tool for inspection and simple manipulation of eBPF programs and maps
|
|||
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** }
|
||||
|
||||
*MAP-COMMANDS* :=
|
||||
{ show | dump | update | lookup | getnext | delete | pin | help }
|
||||
*OPTIONS* := { { **-V** | **--version** } | { **-h** | **--help** }
|
||||
| { **-j** | **--json** } [{ **-p** | **--pretty** }] }
|
||||
|
||||
*PROG-COMMANDS* := { show | dump jited | dump xlated | pin | help }
|
||||
*MAP-COMMANDS* :=
|
||||
{ **show** | **dump** | **update** | **lookup** | **getnext** | **delete**
|
||||
| **pin** | **help** }
|
||||
|
||||
*PROG-COMMANDS* := { **show** | **dump jited** | **dump xlated** | **pin**
|
||||
| **help** }
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
|
@ -31,6 +36,21 @@ DESCRIPTION
|
|||
Note that format of the output of all tools is not guaranteed to be
|
||||
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
|
||||
========
|
||||
**bpftool-map**\ (8), **bpftool-prog**\ (8)
|
||||
|
|
|
@ -66,8 +66,8 @@ int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type)
|
|||
|
||||
fd = bpf_obj_get(path);
|
||||
if (fd < 0) {
|
||||
err("bpf obj get (%s): %s\n", path,
|
||||
errno == EACCES && !is_bpffs(dirname(path)) ?
|
||||
p_err("bpf obj get (%s): %s", path,
|
||||
errno == EACCES && !is_bpffs(dirname(path)) ?
|
||||
"directory not in bpf file system (bpffs)" :
|
||||
strerror(errno));
|
||||
return -1;
|
||||
|
@ -79,7 +79,7 @@ int open_obj_pinned_any(char *path, enum bpf_obj_type exp_type)
|
|||
return 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);
|
||||
return -1;
|
||||
}
|
||||
|
@ -95,14 +95,14 @@ int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32))
|
|||
int fd;
|
||||
|
||||
if (!is_prefix(*argv, "id")) {
|
||||
err("expected 'id' got %s\n", *argv);
|
||||
p_err("expected 'id' got %s", *argv);
|
||||
return -1;
|
||||
}
|
||||
NEXT_ARG();
|
||||
|
||||
id = strtoul(*argv, &endptr, 0);
|
||||
if (*endptr) {
|
||||
err("can't parse %s as ID\n", *argv);
|
||||
p_err("can't parse %s as ID", *argv);
|
||||
return -1;
|
||||
}
|
||||
NEXT_ARG();
|
||||
|
@ -112,15 +112,15 @@ int do_pin_any(int argc, char **argv, int (*get_fd_by_id)(__u32))
|
|||
|
||||
fd = get_fd_by_id(id);
|
||||
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;
|
||||
}
|
||||
|
||||
err = bpf_obj_pin(fd, *argv);
|
||||
close(fd);
|
||||
if (err) {
|
||||
err("can't pin the object (%s): %s\n", *argv,
|
||||
errno == EACCES && !is_bpffs(dirname(*argv)) ?
|
||||
p_err("can't pin the object (%s): %s", *argv,
|
||||
errno == EACCES && !is_bpffs(dirname(*argv)) ?
|
||||
"directory not in bpf file system (bpffs)" :
|
||||
strerror(errno));
|
||||
return -1;
|
||||
|
@ -153,11 +153,11 @@ int get_fd_type(int fd)
|
|||
|
||||
n = readlink(path, buf, sizeof(buf));
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -181,7 +181,7 @@ char *get_fdinfo(int fd, const char *key)
|
|||
|
||||
fdi = fopen(path, "r");
|
||||
if (!fdi) {
|
||||
err("can't open fdinfo: %s\n", strerror(errno));
|
||||
p_err("can't open fdinfo: %s", strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -196,7 +196,7 @@ char *get_fdinfo(int fd, const char *key)
|
|||
|
||||
value = strchr(line, '\t');
|
||||
if (!value || !value[1]) {
|
||||
err("malformed fdinfo!?\n");
|
||||
p_err("malformed fdinfo!?");
|
||||
free(line);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -209,8 +209,18 @@ char *get_fdinfo(int fd, const char *key)
|
|||
return line;
|
||||
}
|
||||
|
||||
err("key '%s' not found in fdinfo\n", key);
|
||||
p_err("key '%s' not found in fdinfo", key);
|
||||
free(line);
|
||||
fclose(fdi);
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
* Licensed under the GNU General Public License, version 2.0 (GPLv2)
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -21,6 +22,9 @@
|
|||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "json_writer.h"
|
||||
#include "main.h"
|
||||
|
||||
static void get_exec_path(char *tpath, size_t size)
|
||||
{
|
||||
ssize_t len;
|
||||
|
@ -39,6 +43,38 @@ static void get_exec_path(char *tpath, size_t size)
|
|||
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)
|
||||
{
|
||||
disassembler_ftype disassemble;
|
||||
|
@ -57,7 +93,12 @@ void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes)
|
|||
assert(bfdf);
|
||||
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.mach = bfd_get_mach(bfdf);
|
||||
info.buffer = image;
|
||||
|
@ -68,20 +109,53 @@ void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes)
|
|||
disassemble = disassembler(bfdf);
|
||||
assert(disassemble);
|
||||
|
||||
if (json_output)
|
||||
jsonw_start_array(json_wtr);
|
||||
do {
|
||||
printf("%4x:\t", pc);
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
printf("\n\t");
|
||||
for (i = 0; i < count; ++i)
|
||||
printf("%02x ", (uint8_t) image[pc + i]);
|
||||
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");
|
||||
for (i = 0; i < count; ++i)
|
||||
printf("%02x ",
|
||||
(uint8_t)image[pc + i]);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
if (json_output)
|
||||
jsonw_end_object(json_wtr);
|
||||
else
|
||||
printf("\n");
|
||||
|
||||
pc += count;
|
||||
} while (count > 0 && pc < len);
|
||||
if (json_output)
|
||||
jsonw_end_array(json_wtr);
|
||||
|
||||
bfd_close(bfdf);
|
||||
}
|
||||
|
|
356
tools/bpf/bpftool/json_writer.c
Normal file
356
tools/bpf/bpftool/json_writer.c
Normal file
|
@ -0,0 +1,356 @@
|
|||
/*
|
||||
* Simple streaming JSON writer
|
||||
*
|
||||
* This takes care of the annoying bits of JSON syntax like the commas
|
||||
* after elements
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* Authors: Stephen Hemminger <stephen@networkplumber.org>
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdarg.h>
|
||||
#include <assert.h>
|
||||
#include <malloc.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "json_writer.h"
|
||||
|
||||
struct json_writer {
|
||||
FILE *out; /* output file */
|
||||
unsigned depth; /* nesting */
|
||||
bool pretty; /* optional whitepace */
|
||||
char sep; /* either nul or comma */
|
||||
};
|
||||
|
||||
/* indentation for pretty print */
|
||||
static void jsonw_indent(json_writer_t *self)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < self->depth; ++i)
|
||||
fputs(" ", self->out);
|
||||
}
|
||||
|
||||
/* end current line and indent if pretty printing */
|
||||
static void jsonw_eol(json_writer_t *self)
|
||||
{
|
||||
if (!self->pretty)
|
||||
return;
|
||||
|
||||
putc('\n', self->out);
|
||||
jsonw_indent(self);
|
||||
}
|
||||
|
||||
/* If current object is not empty print a comma */
|
||||
static void jsonw_eor(json_writer_t *self)
|
||||
{
|
||||
if (self->sep != '\0')
|
||||
putc(self->sep, self->out);
|
||||
self->sep = ',';
|
||||
}
|
||||
|
||||
|
||||
/* Output JSON encoded string */
|
||||
/* Handles C escapes, does not do Unicode */
|
||||
static void jsonw_puts(json_writer_t *self, const char *str)
|
||||
{
|
||||
putc('"', self->out);
|
||||
for (; *str; ++str)
|
||||
switch (*str) {
|
||||
case '\t':
|
||||
fputs("\\t", self->out);
|
||||
break;
|
||||
case '\n':
|
||||
fputs("\\n", self->out);
|
||||
break;
|
||||
case '\r':
|
||||
fputs("\\r", self->out);
|
||||
break;
|
||||
case '\f':
|
||||
fputs("\\f", self->out);
|
||||
break;
|
||||
case '\b':
|
||||
fputs("\\b", self->out);
|
||||
break;
|
||||
case '\\':
|
||||
fputs("\\n", self->out);
|
||||
break;
|
||||
case '"':
|
||||
fputs("\\\"", self->out);
|
||||
break;
|
||||
case '\'':
|
||||
fputs("\\\'", self->out);
|
||||
break;
|
||||
default:
|
||||
putc(*str, self->out);
|
||||
}
|
||||
putc('"', self->out);
|
||||
}
|
||||
|
||||
/* Create a new JSON stream */
|
||||
json_writer_t *jsonw_new(FILE *f)
|
||||
{
|
||||
json_writer_t *self = malloc(sizeof(*self));
|
||||
if (self) {
|
||||
self->out = f;
|
||||
self->depth = 0;
|
||||
self->pretty = false;
|
||||
self->sep = '\0';
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/* End output to JSON stream */
|
||||
void jsonw_destroy(json_writer_t **self_p)
|
||||
{
|
||||
json_writer_t *self = *self_p;
|
||||
|
||||
assert(self->depth == 0);
|
||||
fputs("\n", self->out);
|
||||
fflush(self->out);
|
||||
free(self);
|
||||
*self_p = NULL;
|
||||
}
|
||||
|
||||
void jsonw_pretty(json_writer_t *self, bool on)
|
||||
{
|
||||
self->pretty = on;
|
||||
}
|
||||
|
||||
/* Basic blocks */
|
||||
static void jsonw_begin(json_writer_t *self, int c)
|
||||
{
|
||||
jsonw_eor(self);
|
||||
putc(c, self->out);
|
||||
++self->depth;
|
||||
self->sep = '\0';
|
||||
}
|
||||
|
||||
static void jsonw_end(json_writer_t *self, int c)
|
||||
{
|
||||
assert(self->depth > 0);
|
||||
|
||||
--self->depth;
|
||||
if (self->sep != '\0')
|
||||
jsonw_eol(self);
|
||||
putc(c, self->out);
|
||||
self->sep = ',';
|
||||
}
|
||||
|
||||
|
||||
/* Add a JSON property name */
|
||||
void jsonw_name(json_writer_t *self, const char *name)
|
||||
{
|
||||
jsonw_eor(self);
|
||||
jsonw_eol(self);
|
||||
self->sep = '\0';
|
||||
jsonw_puts(self, name);
|
||||
putc(':', self->out);
|
||||
if (self->pretty)
|
||||
putc(' ', self->out);
|
||||
}
|
||||
|
||||
void jsonw_vprintf_enquote(json_writer_t *self, const char *fmt, va_list ap)
|
||||
{
|
||||
jsonw_eor(self);
|
||||
putc('"', self->out);
|
||||
vfprintf(self->out, fmt, ap);
|
||||
putc('"', self->out);
|
||||
}
|
||||
|
||||
void jsonw_printf(json_writer_t *self, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
jsonw_eor(self);
|
||||
vfprintf(self->out, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
/* Collections */
|
||||
void jsonw_start_object(json_writer_t *self)
|
||||
{
|
||||
jsonw_begin(self, '{');
|
||||
}
|
||||
|
||||
void jsonw_end_object(json_writer_t *self)
|
||||
{
|
||||
jsonw_end(self, '}');
|
||||
}
|
||||
|
||||
void jsonw_start_array(json_writer_t *self)
|
||||
{
|
||||
jsonw_begin(self, '[');
|
||||
}
|
||||
|
||||
void jsonw_end_array(json_writer_t *self)
|
||||
{
|
||||
jsonw_end(self, ']');
|
||||
}
|
||||
|
||||
/* JSON value types */
|
||||
void jsonw_string(json_writer_t *self, const char *value)
|
||||
{
|
||||
jsonw_eor(self);
|
||||
jsonw_puts(self, value);
|
||||
}
|
||||
|
||||
void jsonw_bool(json_writer_t *self, bool val)
|
||||
{
|
||||
jsonw_printf(self, "%s", val ? "true" : "false");
|
||||
}
|
||||
|
||||
void jsonw_null(json_writer_t *self)
|
||||
{
|
||||
jsonw_printf(self, "null");
|
||||
}
|
||||
|
||||
void jsonw_float_fmt(json_writer_t *self, const char *fmt, double num)
|
||||
{
|
||||
jsonw_printf(self, fmt, num);
|
||||
}
|
||||
|
||||
#ifdef notused
|
||||
void jsonw_float(json_writer_t *self, double num)
|
||||
{
|
||||
jsonw_printf(self, "%g", num);
|
||||
}
|
||||
#endif
|
||||
|
||||
void jsonw_hu(json_writer_t *self, unsigned short num)
|
||||
{
|
||||
jsonw_printf(self, "%hu", num);
|
||||
}
|
||||
|
||||
void jsonw_uint(json_writer_t *self, uint64_t num)
|
||||
{
|
||||
jsonw_printf(self, "%"PRIu64, num);
|
||||
}
|
||||
|
||||
void jsonw_lluint(json_writer_t *self, unsigned long long int num)
|
||||
{
|
||||
jsonw_printf(self, "%llu", num);
|
||||
}
|
||||
|
||||
void jsonw_int(json_writer_t *self, int64_t num)
|
||||
{
|
||||
jsonw_printf(self, "%"PRId64, num);
|
||||
}
|
||||
|
||||
/* Basic name/value objects */
|
||||
void jsonw_string_field(json_writer_t *self, const char *prop, const char *val)
|
||||
{
|
||||
jsonw_name(self, prop);
|
||||
jsonw_string(self, val);
|
||||
}
|
||||
|
||||
void jsonw_bool_field(json_writer_t *self, const char *prop, bool val)
|
||||
{
|
||||
jsonw_name(self, prop);
|
||||
jsonw_bool(self, val);
|
||||
}
|
||||
|
||||
#ifdef notused
|
||||
void jsonw_float_field(json_writer_t *self, const char *prop, double val)
|
||||
{
|
||||
jsonw_name(self, prop);
|
||||
jsonw_float(self, val);
|
||||
}
|
||||
#endif
|
||||
|
||||
void jsonw_float_field_fmt(json_writer_t *self,
|
||||
const char *prop,
|
||||
const char *fmt,
|
||||
double val)
|
||||
{
|
||||
jsonw_name(self, prop);
|
||||
jsonw_float_fmt(self, fmt, val);
|
||||
}
|
||||
|
||||
void jsonw_uint_field(json_writer_t *self, const char *prop, uint64_t num)
|
||||
{
|
||||
jsonw_name(self, prop);
|
||||
jsonw_uint(self, num);
|
||||
}
|
||||
|
||||
void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num)
|
||||
{
|
||||
jsonw_name(self, prop);
|
||||
jsonw_hu(self, num);
|
||||
}
|
||||
|
||||
void jsonw_lluint_field(json_writer_t *self,
|
||||
const char *prop,
|
||||
unsigned long long int num)
|
||||
{
|
||||
jsonw_name(self, prop);
|
||||
jsonw_lluint(self, num);
|
||||
}
|
||||
|
||||
void jsonw_int_field(json_writer_t *self, const char *prop, int64_t num)
|
||||
{
|
||||
jsonw_name(self, prop);
|
||||
jsonw_int(self, num);
|
||||
}
|
||||
|
||||
void jsonw_null_field(json_writer_t *self, const char *prop)
|
||||
{
|
||||
jsonw_name(self, prop);
|
||||
jsonw_null(self);
|
||||
}
|
||||
|
||||
#ifdef TEST
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
json_writer_t *wr = jsonw_new(stdout);
|
||||
|
||||
jsonw_start_object(wr);
|
||||
jsonw_pretty(wr, true);
|
||||
jsonw_name(wr, "Vyatta");
|
||||
jsonw_start_object(wr);
|
||||
jsonw_string_field(wr, "url", "http://vyatta.com");
|
||||
jsonw_uint_field(wr, "downloads", 2000000ul);
|
||||
jsonw_float_field(wr, "stock", 8.16);
|
||||
|
||||
jsonw_name(wr, "ARGV");
|
||||
jsonw_start_array(wr);
|
||||
while (--argc)
|
||||
jsonw_string(wr, *++argv);
|
||||
jsonw_end_array(wr);
|
||||
|
||||
jsonw_name(wr, "empty");
|
||||
jsonw_start_array(wr);
|
||||
jsonw_end_array(wr);
|
||||
|
||||
jsonw_name(wr, "NIL");
|
||||
jsonw_start_object(wr);
|
||||
jsonw_end_object(wr);
|
||||
|
||||
jsonw_null_field(wr, "my_null");
|
||||
|
||||
jsonw_name(wr, "special chars");
|
||||
jsonw_start_array(wr);
|
||||
jsonw_string_field(wr, "slash", "/");
|
||||
jsonw_string_field(wr, "newline", "\n");
|
||||
jsonw_string_field(wr, "tab", "\t");
|
||||
jsonw_string_field(wr, "ff", "\f");
|
||||
jsonw_string_field(wr, "quote", "\"");
|
||||
jsonw_string_field(wr, "tick", "\'");
|
||||
jsonw_string_field(wr, "backslash", "\\");
|
||||
jsonw_end_array(wr);
|
||||
|
||||
jsonw_end_object(wr);
|
||||
|
||||
jsonw_end_object(wr);
|
||||
jsonw_destroy(&wr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
72
tools/bpf/bpftool/json_writer.h
Normal file
72
tools/bpf/bpftool/json_writer.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Simple streaming JSON writer
|
||||
*
|
||||
* This takes care of the annoying bits of JSON syntax like the commas
|
||||
* after elements
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* Authors: Stephen Hemminger <stephen@networkplumber.org>
|
||||
*/
|
||||
|
||||
#ifndef _JSON_WRITER_H_
|
||||
#define _JSON_WRITER_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
/* Opaque class structure */
|
||||
typedef struct json_writer json_writer_t;
|
||||
|
||||
/* Create a new JSON stream */
|
||||
json_writer_t *jsonw_new(FILE *f);
|
||||
/* End output to JSON stream */
|
||||
void jsonw_destroy(json_writer_t **self_p);
|
||||
|
||||
/* Cause output to have pretty whitespace */
|
||||
void jsonw_pretty(json_writer_t *self, bool on);
|
||||
|
||||
/* Add property name */
|
||||
void jsonw_name(json_writer_t *self, const char *name);
|
||||
|
||||
/* Add value */
|
||||
void jsonw_vprintf_enquote(json_writer_t *self, const char *fmt, va_list ap);
|
||||
void jsonw_printf(json_writer_t *self, const char *fmt, ...);
|
||||
void jsonw_string(json_writer_t *self, const char *value);
|
||||
void jsonw_bool(json_writer_t *self, bool value);
|
||||
void jsonw_float(json_writer_t *self, double number);
|
||||
void jsonw_float_fmt(json_writer_t *self, const char *fmt, double num);
|
||||
void jsonw_uint(json_writer_t *self, uint64_t number);
|
||||
void jsonw_hu(json_writer_t *self, unsigned short number);
|
||||
void jsonw_int(json_writer_t *self, int64_t number);
|
||||
void jsonw_null(json_writer_t *self);
|
||||
void jsonw_lluint(json_writer_t *self, unsigned long long int num);
|
||||
|
||||
/* Useful Combinations of name and value */
|
||||
void jsonw_string_field(json_writer_t *self, const char *prop, const char *val);
|
||||
void jsonw_bool_field(json_writer_t *self, const char *prop, bool value);
|
||||
void jsonw_float_field(json_writer_t *self, const char *prop, double num);
|
||||
void jsonw_uint_field(json_writer_t *self, const char *prop, uint64_t num);
|
||||
void jsonw_hu_field(json_writer_t *self, const char *prop, unsigned short num);
|
||||
void jsonw_int_field(json_writer_t *self, const char *prop, int64_t num);
|
||||
void jsonw_null_field(json_writer_t *self, const char *prop);
|
||||
void jsonw_lluint_field(json_writer_t *self, const char *prop,
|
||||
unsigned long long int num);
|
||||
void jsonw_float_field_fmt(json_writer_t *self, const char *prop,
|
||||
const char *fmt, double val);
|
||||
|
||||
/* Collections */
|
||||
void jsonw_start_object(json_writer_t *self);
|
||||
void jsonw_end_object(json_writer_t *self);
|
||||
|
||||
void jsonw_start_array(json_writer_t *self);
|
||||
void jsonw_end_array(json_writer_t *self);
|
||||
|
||||
/* Override default exception handling */
|
||||
typedef void (jsonw_err_handler_fn)(const char *);
|
||||
|
||||
#endif /* _JSON_WRITER_H_ */
|
|
@ -36,6 +36,7 @@
|
|||
#include <bfd.h>
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <linux/bpf.h>
|
||||
#include <linux/version.h>
|
||||
#include <stdio.h>
|
||||
|
@ -50,6 +51,9 @@ const char *bin_name;
|
|||
static int last_argc;
|
||||
static char **last_argv;
|
||||
static int (*last_do_help)(int argc, char **argv);
|
||||
json_writer_t *json_wtr;
|
||||
bool pretty_output;
|
||||
bool json_output;
|
||||
|
||||
void usage(void)
|
||||
{
|
||||
|
@ -60,12 +64,19 @@ void usage(void)
|
|||
|
||||
static int do_help(int argc, char **argv)
|
||||
{
|
||||
if (json_output) {
|
||||
jsonw_null(json_wtr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fprintf(stderr,
|
||||
"Usage: %s OBJECT { COMMAND | help }\n"
|
||||
"Usage: %s [OPTIONS] OBJECT { COMMAND | help }\n"
|
||||
" %s batch file FILE\n"
|
||||
" %s version\n"
|
||||
"\n"
|
||||
" OBJECT := { prog | map }\n",
|
||||
" OBJECT := { prog | map }\n"
|
||||
" " HELP_SPEC_OPTIONS "\n"
|
||||
"",
|
||||
bin_name, bin_name, bin_name);
|
||||
|
||||
return 0;
|
||||
|
@ -73,10 +84,22 @@ static int do_help(int argc, char **argv)
|
|||
|
||||
static int do_version(int argc, char **argv)
|
||||
{
|
||||
printf("%s v%d.%d.%d\n", bin_name,
|
||||
LINUX_VERSION_CODE >> 16,
|
||||
LINUX_VERSION_CODE >> 8 & 0xf,
|
||||
LINUX_VERSION_CODE & 0xf);
|
||||
unsigned int version[3];
|
||||
|
||||
version[0] = LINUX_VERSION_CODE >> 16;
|
||||
version[1] = LINUX_VERSION_CODE >> 8 & 0xf;
|
||||
version[2] = LINUX_VERSION_CODE & 0xf;
|
||||
|
||||
if (json_output) {
|
||||
jsonw_start_object(json_wtr);
|
||||
jsonw_name(json_wtr, "version");
|
||||
jsonw_printf(json_wtr, "\"%u.%u.%u\"",
|
||||
version[0], version[1], version[2]);
|
||||
jsonw_end_object(json_wtr);
|
||||
} else {
|
||||
printf("%s v%u.%u.%u\n", bin_name,
|
||||
version[0], version[1], version[2]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -151,25 +174,28 @@ static int do_batch(int argc, char **argv)
|
|||
int n_argc;
|
||||
FILE *fp;
|
||||
int err;
|
||||
int i;
|
||||
|
||||
if (argc < 2) {
|
||||
err("too few parameters for batch\n");
|
||||
p_err("too few parameters for batch");
|
||||
return -1;
|
||||
} else if (!is_prefix(*argv, "file")) {
|
||||
err("expected 'file', got: %s\n", *argv);
|
||||
p_err("expected 'file', got: %s", *argv);
|
||||
return -1;
|
||||
} else if (argc > 2) {
|
||||
err("too many parameters for batch\n");
|
||||
p_err("too many parameters for batch");
|
||||
return -1;
|
||||
}
|
||||
NEXT_ARG();
|
||||
|
||||
fp = fopen(*argv, "r");
|
||||
if (!fp) {
|
||||
err("Can't open file (%s): %s\n", *argv, strerror(errno));
|
||||
p_err("Can't open file (%s): %s", *argv, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (json_output)
|
||||
jsonw_start_array(json_wtr);
|
||||
while (fgets(buf, sizeof(buf), fp)) {
|
||||
if (strlen(buf) == sizeof(buf) - 1) {
|
||||
errno = E2BIG;
|
||||
|
@ -182,8 +208,8 @@ static int do_batch(int argc, char **argv)
|
|||
while (n_argv[n_argc]) {
|
||||
n_argc++;
|
||||
if (n_argc == ARRAY_SIZE(n_argv)) {
|
||||
err("line %d has too many arguments, skip\n",
|
||||
lines);
|
||||
p_err("line %d has too many arguments, skip",
|
||||
lines);
|
||||
n_argc = 0;
|
||||
break;
|
||||
}
|
||||
|
@ -193,7 +219,21 @@ static int do_batch(int argc, char **argv)
|
|||
if (!n_argc)
|
||||
continue;
|
||||
|
||||
if (json_output) {
|
||||
jsonw_start_object(json_wtr);
|
||||
jsonw_name(json_wtr, "command");
|
||||
jsonw_start_array(json_wtr);
|
||||
for (i = 0; i < n_argc; i++)
|
||||
jsonw_string(json_wtr, n_argv[i]);
|
||||
jsonw_end_array(json_wtr);
|
||||
jsonw_name(json_wtr, "output");
|
||||
}
|
||||
|
||||
err = cmd_select(cmds, n_argc, n_argv, do_help);
|
||||
|
||||
if (json_output)
|
||||
jsonw_end_object(json_wtr);
|
||||
|
||||
if (err)
|
||||
goto err_close;
|
||||
|
||||
|
@ -204,21 +244,72 @@ static int do_batch(int argc, char **argv)
|
|||
perror("reading batch file failed");
|
||||
err = -1;
|
||||
} else {
|
||||
info("processed %d lines\n", lines);
|
||||
p_info("processed %d lines", lines);
|
||||
err = 0;
|
||||
}
|
||||
err_close:
|
||||
fclose(fp);
|
||||
|
||||
if (json_output)
|
||||
jsonw_end_array(json_wtr);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
static const struct option options[] = {
|
||||
{ "json", no_argument, NULL, 'j' },
|
||||
{ "help", no_argument, NULL, 'h' },
|
||||
{ "pretty", no_argument, NULL, 'p' },
|
||||
{ "version", no_argument, NULL, 'V' },
|
||||
{ 0 }
|
||||
};
|
||||
int opt, ret;
|
||||
|
||||
last_do_help = do_help;
|
||||
pretty_output = false;
|
||||
json_output = false;
|
||||
bin_name = argv[0];
|
||||
NEXT_ARG();
|
||||
|
||||
while ((opt = getopt_long(argc, argv, "Vhpj",
|
||||
options, NULL)) >= 0) {
|
||||
switch (opt) {
|
||||
case 'V':
|
||||
return do_version(argc, argv);
|
||||
case 'h':
|
||||
return do_help(argc, argv);
|
||||
case 'p':
|
||||
pretty_output = true;
|
||||
/* fall through */
|
||||
case 'j':
|
||||
json_output = true;
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
}
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
if (argc < 0)
|
||||
usage();
|
||||
|
||||
if (json_output) {
|
||||
json_wtr = jsonw_new(stdout);
|
||||
if (!json_wtr) {
|
||||
p_err("failed to create JSON writer");
|
||||
return -1;
|
||||
}
|
||||
jsonw_pretty(json_wtr, pretty_output);
|
||||
}
|
||||
|
||||
bfd_init();
|
||||
|
||||
return cmd_select(cmds, argc, argv, do_help);
|
||||
ret = cmd_select(cmds, argc, argv, do_help);
|
||||
|
||||
if (json_output)
|
||||
jsonw_destroy(&json_wtr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -43,20 +43,20 @@
|
|||
#include <linux/bpf.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#define err(msg...) fprintf(stderr, "Error: " msg)
|
||||
#define warn(msg...) fprintf(stderr, "Warning: " msg)
|
||||
#define info(msg...) fprintf(stderr, msg)
|
||||
#include "json_writer.h"
|
||||
|
||||
#define ptr_to_u64(ptr) ((__u64)(unsigned long)(ptr))
|
||||
|
||||
#define NEXT_ARG() ({ argc--; argv++; if (argc < 0) usage(); })
|
||||
#define NEXT_ARGP() ({ (*argc)--; (*argv)++; if (*argc < 0) usage(); })
|
||||
#define BAD_ARG() ({ err("what is '%s'?\n", *argv); -1; })
|
||||
#define BAD_ARG() ({ p_err("what is '%s'?\n", *argv); -1; })
|
||||
|
||||
#define BPF_TAG_FMT "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
|
||||
|
||||
#define HELP_SPEC_PROGRAM \
|
||||
"PROG := { id PROG_ID | pinned FILE | tag PROG_TAG }"
|
||||
#define HELP_SPEC_OPTIONS \
|
||||
"OPTIONS := { {-j|--json} [{-p|--pretty}] }"
|
||||
|
||||
enum bpf_obj_type {
|
||||
BPF_OBJ_UNKNOWN,
|
||||
|
@ -66,6 +66,9 @@ enum bpf_obj_type {
|
|||
|
||||
extern const char *bin_name;
|
||||
|
||||
extern json_writer_t *json_wtr;
|
||||
extern bool json_output;
|
||||
|
||||
bool is_prefix(const char *pfx, const char *str);
|
||||
void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep);
|
||||
void usage(void) __attribute__((noreturn));
|
||||
|
@ -90,5 +93,37 @@ int do_map(int argc, char **arg);
|
|||
int prog_parse_fd(int *argc, char ***argv);
|
||||
|
||||
void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes);
|
||||
void print_hex_data_json(uint8_t *data, size_t len);
|
||||
|
||||
static inline void p_err(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
if (json_output) {
|
||||
jsonw_start_object(json_wtr);
|
||||
jsonw_name(json_wtr, "error");
|
||||
jsonw_vprintf_enquote(json_wtr, fmt, ap);
|
||||
jsonw_end_object(json_wtr);
|
||||
} else {
|
||||
fprintf(stderr, "Error: ");
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static inline void p_info(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (json_output)
|
||||
return;
|
||||
|
||||
va_start(ap, fmt);
|
||||
vfprintf(stderr, fmt, ap);
|
||||
fprintf(stderr, "\n");
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -81,19 +81,19 @@ static unsigned int get_possible_cpus(void)
|
|||
|
||||
fd = open("/sys/devices/system/cpu/possible", O_RDONLY);
|
||||
if (fd < 0) {
|
||||
err("can't open sysfs possible cpus\n");
|
||||
p_err("can't open sysfs possible cpus");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
n = read(fd, buf, sizeof(buf));
|
||||
if (n < 2) {
|
||||
err("can't read sysfs possible cpus\n");
|
||||
p_err("can't read sysfs possible cpus");
|
||||
exit(-1);
|
||||
}
|
||||
close(fd);
|
||||
|
||||
if (n == sizeof(buf)) {
|
||||
err("read sysfs possible cpus overflow\n");
|
||||
p_err("read sysfs possible cpus overflow");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
|
@ -161,14 +161,14 @@ static int map_parse_fd(int *argc, char ***argv)
|
|||
|
||||
id = strtoul(**argv, &endptr, 0);
|
||||
if (*endptr) {
|
||||
err("can't parse %s as ID\n", **argv);
|
||||
p_err("can't parse %s as ID", **argv);
|
||||
return -1;
|
||||
}
|
||||
NEXT_ARGP();
|
||||
|
||||
fd = bpf_map_get_fd_by_id(id);
|
||||
if (fd < 0)
|
||||
err("get map by id (%u): %s\n", id, strerror(errno));
|
||||
p_err("get map by id (%u): %s", id, strerror(errno));
|
||||
return fd;
|
||||
} else if (is_prefix(**argv, "pinned")) {
|
||||
char *path;
|
||||
|
@ -181,7 +181,7 @@ static int map_parse_fd(int *argc, char ***argv)
|
|||
return open_obj_pinned_any(path, BPF_OBJ_MAP);
|
||||
}
|
||||
|
||||
err("expected 'id' or 'pinned', got: '%s'?\n", **argv);
|
||||
p_err("expected 'id' or 'pinned', got: '%s'?", **argv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -197,7 +197,7 @@ map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len)
|
|||
|
||||
err = bpf_obj_get_info_by_fd(fd, info, info_len);
|
||||
if (err) {
|
||||
err("can't get map info: %s\n", strerror(errno));
|
||||
p_err("can't get map info: %s", strerror(errno));
|
||||
close(fd);
|
||||
return err;
|
||||
}
|
||||
|
@ -205,8 +205,45 @@ map_parse_fd_and_info(int *argc, char ***argv, void *info, __u32 *info_len)
|
|||
return fd;
|
||||
}
|
||||
|
||||
static void print_entry(struct bpf_map_info *info, unsigned char *key,
|
||||
unsigned char *value)
|
||||
static void print_entry_json(struct bpf_map_info *info, unsigned char *key,
|
||||
unsigned char *value)
|
||||
{
|
||||
jsonw_start_object(json_wtr);
|
||||
|
||||
if (!map_is_per_cpu(info->type)) {
|
||||
jsonw_name(json_wtr, "key");
|
||||
print_hex_data_json(key, info->key_size);
|
||||
jsonw_name(json_wtr, "value");
|
||||
print_hex_data_json(value, info->value_size);
|
||||
} else {
|
||||
unsigned int i, n;
|
||||
|
||||
n = get_possible_cpus();
|
||||
|
||||
jsonw_name(json_wtr, "key");
|
||||
print_hex_data_json(key, info->key_size);
|
||||
|
||||
jsonw_name(json_wtr, "values");
|
||||
jsonw_start_array(json_wtr);
|
||||
for (i = 0; i < n; i++) {
|
||||
jsonw_start_object(json_wtr);
|
||||
|
||||
jsonw_int_field(json_wtr, "cpu", i);
|
||||
|
||||
jsonw_name(json_wtr, "value");
|
||||
print_hex_data_json(value + i * info->value_size,
|
||||
info->value_size);
|
||||
|
||||
jsonw_end_object(json_wtr);
|
||||
}
|
||||
jsonw_end_array(json_wtr);
|
||||
}
|
||||
|
||||
jsonw_end_object(json_wtr);
|
||||
}
|
||||
|
||||
static void print_entry_plain(struct bpf_map_info *info, unsigned char *key,
|
||||
unsigned char *value)
|
||||
{
|
||||
if (!map_is_per_cpu(info->type)) {
|
||||
bool single_line, break_names;
|
||||
|
@ -251,14 +288,14 @@ static char **parse_bytes(char **argv, const char *name, unsigned char *val,
|
|||
while (i < n && argv[i]) {
|
||||
val[i] = strtoul(argv[i], &endptr, 0);
|
||||
if (*endptr) {
|
||||
err("error parsing byte: %s\n", argv[i]);
|
||||
p_err("error parsing byte: %s", argv[i]);
|
||||
return NULL;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
if (i != n) {
|
||||
err("%s expected %d bytes got %d\n", name, n, i);
|
||||
p_err("%s expected %d bytes got %d", name, n, i);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -272,16 +309,16 @@ static int parse_elem(char **argv, struct bpf_map_info *info,
|
|||
if (!*argv) {
|
||||
if (!key && !value)
|
||||
return 0;
|
||||
err("did not find %s\n", key ? "key" : "value");
|
||||
p_err("did not find %s", key ? "key" : "value");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (is_prefix(*argv, "key")) {
|
||||
if (!key) {
|
||||
if (key_size)
|
||||
err("duplicate key\n");
|
||||
p_err("duplicate key");
|
||||
else
|
||||
err("unnecessary key\n");
|
||||
p_err("unnecessary key");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -296,9 +333,9 @@ static int parse_elem(char **argv, struct bpf_map_info *info,
|
|||
|
||||
if (!value) {
|
||||
if (value_size)
|
||||
err("duplicate value\n");
|
||||
p_err("duplicate value");
|
||||
else
|
||||
err("unnecessary value\n");
|
||||
p_err("unnecessary value");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -308,11 +345,11 @@ static int parse_elem(char **argv, struct bpf_map_info *info,
|
|||
int argc = 2;
|
||||
|
||||
if (value_size != 4) {
|
||||
err("value smaller than 4B for map in map?\n");
|
||||
p_err("value smaller than 4B for map in map?");
|
||||
return -1;
|
||||
}
|
||||
if (!argv[0] || !argv[1]) {
|
||||
err("not enough value arguments for map in map\n");
|
||||
p_err("not enough value arguments for map in map");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -326,11 +363,11 @@ static int parse_elem(char **argv, struct bpf_map_info *info,
|
|||
int argc = 2;
|
||||
|
||||
if (value_size != 4) {
|
||||
err("value smaller than 4B for map of progs?\n");
|
||||
p_err("value smaller than 4B for map of progs?");
|
||||
return -1;
|
||||
}
|
||||
if (!argv[0] || !argv[1]) {
|
||||
err("not enough value arguments for map of progs\n");
|
||||
p_err("not enough value arguments for map of progs");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -351,7 +388,7 @@ static int parse_elem(char **argv, struct bpf_map_info *info,
|
|||
} else if (is_prefix(*argv, "any") || is_prefix(*argv, "noexist") ||
|
||||
is_prefix(*argv, "exist")) {
|
||||
if (!flags) {
|
||||
err("flags specified multiple times: %s\n", *argv);
|
||||
p_err("flags specified multiple times: %s", *argv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -366,11 +403,45 @@ static int parse_elem(char **argv, struct bpf_map_info *info,
|
|||
value_size, NULL, value_fd);
|
||||
}
|
||||
|
||||
err("expected key or value, got: %s\n", *argv);
|
||||
p_err("expected key or value, got: %s", *argv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int show_map_close(int fd, struct bpf_map_info *info)
|
||||
static int show_map_close_json(int fd, struct bpf_map_info *info)
|
||||
{
|
||||
char *memlock;
|
||||
|
||||
memlock = get_fdinfo(fd, "memlock");
|
||||
close(fd);
|
||||
|
||||
jsonw_start_object(json_wtr);
|
||||
|
||||
jsonw_uint_field(json_wtr, "id", info->id);
|
||||
if (info->type < ARRAY_SIZE(map_type_name))
|
||||
jsonw_string_field(json_wtr, "type",
|
||||
map_type_name[info->type]);
|
||||
else
|
||||
jsonw_uint_field(json_wtr, "type", info->type);
|
||||
|
||||
if (*info->name)
|
||||
jsonw_string_field(json_wtr, "name", info->name);
|
||||
|
||||
jsonw_name(json_wtr, "flags");
|
||||
jsonw_printf(json_wtr, "%#x", info->map_flags);
|
||||
jsonw_uint_field(json_wtr, "bytes_key", info->key_size);
|
||||
jsonw_uint_field(json_wtr, "bytes_value", info->value_size);
|
||||
jsonw_uint_field(json_wtr, "max_entries", info->max_entries);
|
||||
|
||||
if (memlock)
|
||||
jsonw_int_field(json_wtr, "bytes_memlock", atoi(memlock));
|
||||
free(memlock);
|
||||
|
||||
jsonw_end_object(json_wtr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int show_map_close_plain(int fd, struct bpf_map_info *info)
|
||||
{
|
||||
char *memlock;
|
||||
|
||||
|
@ -412,39 +483,48 @@ static int do_show(int argc, char **argv)
|
|||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
return show_map_close(fd, &info);
|
||||
if (json_output)
|
||||
return show_map_close_json(fd, &info);
|
||||
else
|
||||
return show_map_close_plain(fd, &info);
|
||||
}
|
||||
|
||||
if (argc)
|
||||
return BAD_ARG();
|
||||
|
||||
if (json_output)
|
||||
jsonw_start_array(json_wtr);
|
||||
while (true) {
|
||||
err = bpf_map_get_next_id(id, &id);
|
||||
if (err) {
|
||||
if (errno == ENOENT)
|
||||
break;
|
||||
err("can't get next map: %s\n", strerror(errno));
|
||||
if (errno == EINVAL)
|
||||
err("kernel too old?\n");
|
||||
p_err("can't get next map: %s%s", strerror(errno),
|
||||
errno == EINVAL ? " -- kernel too old?" : "");
|
||||
return -1;
|
||||
}
|
||||
|
||||
fd = bpf_map_get_fd_by_id(id);
|
||||
if (fd < 0) {
|
||||
err("can't get map by id (%u): %s\n",
|
||||
id, strerror(errno));
|
||||
p_err("can't get map by id (%u): %s",
|
||||
id, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = bpf_obj_get_info_by_fd(fd, &info, &len);
|
||||
if (err) {
|
||||
err("can't get map info: %s\n", strerror(errno));
|
||||
p_err("can't get map info: %s", strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
show_map_close(fd, &info);
|
||||
if (json_output)
|
||||
show_map_close_json(fd, &info);
|
||||
else
|
||||
show_map_close_plain(fd, &info);
|
||||
}
|
||||
if (json_output)
|
||||
jsonw_end_array(json_wtr);
|
||||
|
||||
return errno == ENOENT ? 0 : -1;
|
||||
}
|
||||
|
@ -466,7 +546,7 @@ static int do_dump(int argc, char **argv)
|
|||
return -1;
|
||||
|
||||
if (map_is_map_of_maps(info.type) || map_is_map_of_progs(info.type)) {
|
||||
err("Dumping maps of maps and program maps not supported\n");
|
||||
p_err("Dumping maps of maps and program maps not supported");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
@ -474,12 +554,14 @@ static int do_dump(int argc, char **argv)
|
|||
key = malloc(info.key_size);
|
||||
value = alloc_value(&info);
|
||||
if (!key || !value) {
|
||||
err("mem alloc failed\n");
|
||||
p_err("mem alloc failed");
|
||||
err = -1;
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
prev_key = NULL;
|
||||
if (json_output)
|
||||
jsonw_start_array(json_wtr);
|
||||
while (true) {
|
||||
err = bpf_map_get_next_key(fd, prev_key, key);
|
||||
if (err) {
|
||||
|
@ -489,18 +571,35 @@ static int do_dump(int argc, char **argv)
|
|||
}
|
||||
|
||||
if (!bpf_map_lookup_elem(fd, key, value)) {
|
||||
print_entry(&info, key, value);
|
||||
if (json_output)
|
||||
print_entry_json(&info, key, value);
|
||||
else
|
||||
print_entry_plain(&info, key, value);
|
||||
} else {
|
||||
info("can't lookup element with key: ");
|
||||
fprint_hex(stderr, key, info.key_size, " ");
|
||||
fprintf(stderr, "\n");
|
||||
if (json_output) {
|
||||
jsonw_name(json_wtr, "key");
|
||||
print_hex_data_json(key, info.key_size);
|
||||
jsonw_name(json_wtr, "value");
|
||||
jsonw_start_object(json_wtr);
|
||||
jsonw_string_field(json_wtr, "error",
|
||||
"can't lookup element");
|
||||
jsonw_end_object(json_wtr);
|
||||
} else {
|
||||
p_info("can't lookup element with key: ");
|
||||
fprint_hex(stderr, key, info.key_size, " ");
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
prev_key = key;
|
||||
num_elems++;
|
||||
}
|
||||
|
||||
printf("Found %u element%s\n", num_elems, num_elems != 1 ? "s" : "");
|
||||
if (json_output)
|
||||
jsonw_end_array(json_wtr);
|
||||
else
|
||||
printf("Found %u element%s\n", num_elems,
|
||||
num_elems != 1 ? "s" : "");
|
||||
|
||||
exit_free:
|
||||
free(key);
|
||||
|
@ -529,7 +628,7 @@ static int do_update(int argc, char **argv)
|
|||
key = malloc(info.key_size);
|
||||
value = alloc_value(&info);
|
||||
if (!key || !value) {
|
||||
err("mem alloc failed");
|
||||
p_err("mem alloc failed");
|
||||
err = -1;
|
||||
goto exit_free;
|
||||
}
|
||||
|
@ -541,7 +640,7 @@ static int do_update(int argc, char **argv)
|
|||
|
||||
err = bpf_map_update_elem(fd, key, value, flags);
|
||||
if (err) {
|
||||
err("update failed: %s\n", strerror(errno));
|
||||
p_err("update failed: %s", strerror(errno));
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
|
@ -552,6 +651,8 @@ static int do_update(int argc, char **argv)
|
|||
free(value);
|
||||
close(fd);
|
||||
|
||||
if (!err && json_output)
|
||||
jsonw_null(json_wtr);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -573,7 +674,7 @@ static int do_lookup(int argc, char **argv)
|
|||
key = malloc(info.key_size);
|
||||
value = alloc_value(&info);
|
||||
if (!key || !value) {
|
||||
err("mem alloc failed");
|
||||
p_err("mem alloc failed");
|
||||
err = -1;
|
||||
goto exit_free;
|
||||
}
|
||||
|
@ -584,13 +685,20 @@ static int do_lookup(int argc, char **argv)
|
|||
|
||||
err = bpf_map_lookup_elem(fd, key, value);
|
||||
if (!err) {
|
||||
print_entry(&info, key, value);
|
||||
if (json_output)
|
||||
print_entry_json(&info, key, value);
|
||||
else
|
||||
print_entry_plain(&info, key, value);
|
||||
} else if (errno == ENOENT) {
|
||||
printf("key:\n");
|
||||
fprint_hex(stdout, key, info.key_size, " ");
|
||||
printf("\n\nNot found\n");
|
||||
if (json_output) {
|
||||
jsonw_null(json_wtr);
|
||||
} else {
|
||||
printf("key:\n");
|
||||
fprint_hex(stdout, key, info.key_size, " ");
|
||||
printf("\n\nNot found\n");
|
||||
}
|
||||
} else {
|
||||
err("lookup failed: %s\n", strerror(errno));
|
||||
p_err("lookup failed: %s", strerror(errno));
|
||||
}
|
||||
|
||||
exit_free:
|
||||
|
@ -619,7 +727,7 @@ static int do_getnext(int argc, char **argv)
|
|||
key = malloc(info.key_size);
|
||||
nextkey = malloc(info.key_size);
|
||||
if (!key || !nextkey) {
|
||||
err("mem alloc failed");
|
||||
p_err("mem alloc failed");
|
||||
err = -1;
|
||||
goto exit_free;
|
||||
}
|
||||
|
@ -636,22 +744,34 @@ static int do_getnext(int argc, char **argv)
|
|||
|
||||
err = bpf_map_get_next_key(fd, key, nextkey);
|
||||
if (err) {
|
||||
err("can't get next key: %s\n", strerror(errno));
|
||||
p_err("can't get next key: %s", strerror(errno));
|
||||
goto exit_free;
|
||||
}
|
||||
|
||||
if (key) {
|
||||
printf("key:\n");
|
||||
fprint_hex(stdout, key, info.key_size, " ");
|
||||
printf("\n");
|
||||
if (json_output) {
|
||||
jsonw_start_object(json_wtr);
|
||||
if (key) {
|
||||
jsonw_name(json_wtr, "key");
|
||||
print_hex_data_json(key, info.key_size);
|
||||
} else {
|
||||
jsonw_null_field(json_wtr, "key");
|
||||
}
|
||||
jsonw_name(json_wtr, "next_key");
|
||||
print_hex_data_json(nextkey, info.key_size);
|
||||
jsonw_end_object(json_wtr);
|
||||
} else {
|
||||
printf("key: None\n");
|
||||
if (key) {
|
||||
printf("key:\n");
|
||||
fprint_hex(stdout, key, info.key_size, " ");
|
||||
printf("\n");
|
||||
} else {
|
||||
printf("key: None\n");
|
||||
}
|
||||
printf("next key:\n");
|
||||
fprint_hex(stdout, nextkey, info.key_size, " ");
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
printf("next key:\n");
|
||||
fprint_hex(stdout, nextkey, info.key_size, " ");
|
||||
printf("\n");
|
||||
|
||||
exit_free:
|
||||
free(nextkey);
|
||||
free(key);
|
||||
|
@ -677,7 +797,7 @@ static int do_delete(int argc, char **argv)
|
|||
|
||||
key = malloc(info.key_size);
|
||||
if (!key) {
|
||||
err("mem alloc failed");
|
||||
p_err("mem alloc failed");
|
||||
err = -1;
|
||||
goto exit_free;
|
||||
}
|
||||
|
@ -688,22 +808,34 @@ static int do_delete(int argc, char **argv)
|
|||
|
||||
err = bpf_map_delete_elem(fd, key);
|
||||
if (err)
|
||||
err("delete failed: %s\n", strerror(errno));
|
||||
p_err("delete failed: %s", strerror(errno));
|
||||
|
||||
exit_free:
|
||||
free(key);
|
||||
close(fd);
|
||||
|
||||
if (!err && json_output)
|
||||
jsonw_null(json_wtr);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int do_pin(int argc, char **argv)
|
||||
{
|
||||
return do_pin_any(argc, argv, bpf_map_get_fd_by_id);
|
||||
int err;
|
||||
|
||||
err = do_pin_any(argc, argv, bpf_map_get_fd_by_id);
|
||||
if (!err && json_output)
|
||||
jsonw_null(json_wtr);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int do_help(int argc, char **argv)
|
||||
{
|
||||
if (json_output) {
|
||||
jsonw_null(json_wtr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fprintf(stderr,
|
||||
"Usage: %s %s show [MAP]\n"
|
||||
" %s %s dump MAP\n"
|
||||
|
@ -718,6 +850,7 @@ static int do_help(int argc, char **argv)
|
|||
" " HELP_SPEC_PROGRAM "\n"
|
||||
" VALUE := { BYTES | MAP | PROG }\n"
|
||||
" UPDATE_FLAGS := { any | exist | noexist }\n"
|
||||
" " HELP_SPEC_OPTIONS "\n"
|
||||
"",
|
||||
bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
|
||||
bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
|
||||
|
|
|
@ -104,21 +104,21 @@ static int prog_fd_by_tag(unsigned char *tag)
|
|||
while (true) {
|
||||
err = bpf_prog_get_next_id(id, &id);
|
||||
if (err) {
|
||||
err("%s\n", strerror(errno));
|
||||
p_err("%s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
fd = bpf_prog_get_fd_by_id(id);
|
||||
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;
|
||||
}
|
||||
|
||||
err = bpf_obj_get_info_by_fd(fd, &info, &len);
|
||||
if (err) {
|
||||
err("can't get prog info (%u): %s\n",
|
||||
id, strerror(errno));
|
||||
p_err("can't get prog info (%u): %s",
|
||||
id, strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
@ -142,14 +142,14 @@ int prog_parse_fd(int *argc, char ***argv)
|
|||
|
||||
id = strtoul(**argv, &endptr, 0);
|
||||
if (*endptr) {
|
||||
err("can't parse %s as ID\n", **argv);
|
||||
p_err("can't parse %s as ID", **argv);
|
||||
return -1;
|
||||
}
|
||||
NEXT_ARGP();
|
||||
|
||||
fd = bpf_prog_get_fd_by_id(id);
|
||||
if (fd < 0)
|
||||
err("get by id (%u): %s\n", id, strerror(errno));
|
||||
p_err("get by id (%u): %s", id, strerror(errno));
|
||||
return fd;
|
||||
} else if (is_prefix(**argv, "tag")) {
|
||||
unsigned char tag[BPF_TAG_SIZE];
|
||||
|
@ -159,7 +159,7 @@ int prog_parse_fd(int *argc, char ***argv)
|
|||
if (sscanf(**argv, BPF_TAG_FMT, tag, tag + 1, tag + 2,
|
||||
tag + 3, tag + 4, tag + 5, tag + 6, tag + 7)
|
||||
!= BPF_TAG_SIZE) {
|
||||
err("can't parse tag\n");
|
||||
p_err("can't parse tag");
|
||||
return -1;
|
||||
}
|
||||
NEXT_ARGP();
|
||||
|
@ -176,7 +176,7 @@ int prog_parse_fd(int *argc, char ***argv)
|
|||
return open_obj_pinned_any(path, BPF_OBJ_PROG);
|
||||
}
|
||||
|
||||
err("expected 'id', 'tag' or 'pinned', got: '%s'?\n", **argv);
|
||||
p_err("expected 'id', 'tag' or 'pinned', got: '%s'?", **argv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -195,51 +195,100 @@ static void show_prog_maps(int fd, u32 num_maps)
|
|||
if (err || !info.nr_map_ids)
|
||||
return;
|
||||
|
||||
printf(" map_ids ");
|
||||
for (i = 0; i < info.nr_map_ids; i++)
|
||||
printf("%u%s", map_ids[i],
|
||||
i == info.nr_map_ids - 1 ? "" : ",");
|
||||
if (json_output) {
|
||||
jsonw_name(json_wtr, "map_ids");
|
||||
jsonw_start_array(json_wtr);
|
||||
for (i = 0; i < info.nr_map_ids; i++)
|
||||
jsonw_uint(json_wtr, map_ids[i]);
|
||||
jsonw_end_array(json_wtr);
|
||||
} else {
|
||||
printf(" map_ids ");
|
||||
for (i = 0; i < info.nr_map_ids; i++)
|
||||
printf("%u%s", map_ids[i],
|
||||
i == info.nr_map_ids - 1 ? "" : ",");
|
||||
}
|
||||
}
|
||||
|
||||
static int show_prog(int fd)
|
||||
static void print_prog_json(struct bpf_prog_info *info, int fd)
|
||||
{
|
||||
struct bpf_prog_info info = {};
|
||||
__u32 len = sizeof(info);
|
||||
char *memlock;
|
||||
int err;
|
||||
|
||||
err = bpf_obj_get_info_by_fd(fd, &info, &len);
|
||||
if (err) {
|
||||
err("can't get prog info: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("%u: ", info.id);
|
||||
if (info.type < ARRAY_SIZE(prog_type_name))
|
||||
printf("%s ", prog_type_name[info.type]);
|
||||
jsonw_start_object(json_wtr);
|
||||
jsonw_uint_field(json_wtr, "id", info->id);
|
||||
if (info->type < ARRAY_SIZE(prog_type_name))
|
||||
jsonw_string_field(json_wtr, "type",
|
||||
prog_type_name[info->type]);
|
||||
else
|
||||
printf("type %u ", info.type);
|
||||
jsonw_uint_field(json_wtr, "type", info->type);
|
||||
|
||||
if (*info.name)
|
||||
printf("name %s ", info.name);
|
||||
if (*info->name)
|
||||
jsonw_string_field(json_wtr, "name", info->name);
|
||||
|
||||
printf("tag ");
|
||||
fprint_hex(stdout, info.tag, BPF_TAG_SIZE, "");
|
||||
printf("\n");
|
||||
jsonw_name(json_wtr, "tag");
|
||||
jsonw_printf(json_wtr, "\"" BPF_TAG_FMT "\"",
|
||||
info->tag[0], info->tag[1], info->tag[2], info->tag[3],
|
||||
info->tag[4], info->tag[5], info->tag[6], info->tag[7]);
|
||||
|
||||
if (info.load_time) {
|
||||
if (info->load_time) {
|
||||
char buf[32];
|
||||
|
||||
print_boot_time(info.load_time, buf, sizeof(buf));
|
||||
print_boot_time(info->load_time, buf, sizeof(buf));
|
||||
|
||||
/* Piggy back on load_time, since 0 uid is a valid one */
|
||||
printf("\tloaded_at %s uid %u\n", buf, info.created_by_uid);
|
||||
jsonw_string_field(json_wtr, "loaded_at", buf);
|
||||
jsonw_uint_field(json_wtr, "uid", info->created_by_uid);
|
||||
}
|
||||
|
||||
printf("\txlated %uB", info.xlated_prog_len);
|
||||
jsonw_uint_field(json_wtr, "bytes_xlated", info->xlated_prog_len);
|
||||
|
||||
if (info.jited_prog_len)
|
||||
printf(" jited %uB", info.jited_prog_len);
|
||||
if (info->jited_prog_len) {
|
||||
jsonw_bool_field(json_wtr, "jited", true);
|
||||
jsonw_uint_field(json_wtr, "bytes_jited", info->jited_prog_len);
|
||||
} else {
|
||||
jsonw_bool_field(json_wtr, "jited", false);
|
||||
}
|
||||
|
||||
memlock = get_fdinfo(fd, "memlock");
|
||||
if (memlock)
|
||||
jsonw_int_field(json_wtr, "bytes_memlock", atoi(memlock));
|
||||
free(memlock);
|
||||
|
||||
if (info->nr_map_ids)
|
||||
show_prog_maps(fd, info->nr_map_ids);
|
||||
|
||||
jsonw_end_object(json_wtr);
|
||||
}
|
||||
|
||||
static void print_prog_plain(struct bpf_prog_info *info, int fd)
|
||||
{
|
||||
char *memlock;
|
||||
|
||||
printf("%u: ", info->id);
|
||||
if (info->type < ARRAY_SIZE(prog_type_name))
|
||||
printf("%s ", prog_type_name[info->type]);
|
||||
else
|
||||
printf("type %u ", info->type);
|
||||
|
||||
if (*info->name)
|
||||
printf("name %s ", info->name);
|
||||
|
||||
printf("tag ");
|
||||
fprint_hex(stdout, info->tag, BPF_TAG_SIZE, "");
|
||||
printf("\n");
|
||||
|
||||
if (info->load_time) {
|
||||
char buf[32];
|
||||
|
||||
print_boot_time(info->load_time, buf, sizeof(buf));
|
||||
|
||||
/* Piggy back on load_time, since 0 uid is a valid one */
|
||||
printf("\tloaded_at %s uid %u\n", buf, info->created_by_uid);
|
||||
}
|
||||
|
||||
printf("\txlated %uB", info->xlated_prog_len);
|
||||
|
||||
if (info->jited_prog_len)
|
||||
printf(" jited %uB", info->jited_prog_len);
|
||||
else
|
||||
printf(" not jited");
|
||||
|
||||
|
@ -248,16 +297,35 @@ static int show_prog(int fd)
|
|||
printf(" memlock %sB", memlock);
|
||||
free(memlock);
|
||||
|
||||
if (info.nr_map_ids)
|
||||
show_prog_maps(fd, info.nr_map_ids);
|
||||
if (info->nr_map_ids)
|
||||
show_prog_maps(fd, info->nr_map_ids);
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static int show_prog(int fd)
|
||||
{
|
||||
struct bpf_prog_info info = {};
|
||||
__u32 len = sizeof(info);
|
||||
int err;
|
||||
|
||||
err = bpf_obj_get_info_by_fd(fd, &info, &len);
|
||||
if (err) {
|
||||
p_err("can't get prog info: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (json_output)
|
||||
print_prog_json(&info, fd);
|
||||
else
|
||||
print_prog_plain(&info, fd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_show(int argc, char **argv)
|
||||
{ __u32 id = 0;
|
||||
{
|
||||
__u32 id = 0;
|
||||
int err;
|
||||
int fd;
|
||||
|
||||
|
@ -272,6 +340,8 @@ static int do_show(int argc, char **argv)
|
|||
if (argc)
|
||||
return BAD_ARG();
|
||||
|
||||
if (json_output)
|
||||
jsonw_start_array(json_wtr);
|
||||
while (true) {
|
||||
err = bpf_prog_get_next_id(id, &id);
|
||||
if (err) {
|
||||
|
@ -279,26 +349,30 @@ static int do_show(int argc, char **argv)
|
|||
err = 0;
|
||||
break;
|
||||
}
|
||||
err("can't get next program: %s\n", strerror(errno));
|
||||
if (errno == EINVAL)
|
||||
err("kernel too old?\n");
|
||||
return -1;
|
||||
p_err("can't get next program: %s%s", strerror(errno),
|
||||
errno == EINVAL ? " -- kernel too old?" : "");
|
||||
err = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
fd = bpf_prog_get_fd_by_id(id);
|
||||
if (fd < 0) {
|
||||
err("can't get prog by id (%u): %s\n",
|
||||
id, strerror(errno));
|
||||
return -1;
|
||||
p_err("can't get prog by id (%u): %s",
|
||||
id, strerror(errno));
|
||||
err = -1;
|
||||
break;
|
||||
}
|
||||
|
||||
err = show_prog(fd);
|
||||
close(fd);
|
||||
if (err)
|
||||
return err;
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
if (json_output)
|
||||
jsonw_end_array(json_wtr);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void print_insn(struct bpf_verifier_env *env, const char *fmt, ...)
|
||||
|
@ -310,7 +384,7 @@ static void print_insn(struct bpf_verifier_env *env, const char *fmt, ...)
|
|||
va_end(args);
|
||||
}
|
||||
|
||||
static void dump_xlated(void *buf, unsigned int len, bool opcodes)
|
||||
static void dump_xlated_plain(void *buf, unsigned int len, bool opcodes)
|
||||
{
|
||||
struct bpf_insn *insn = buf;
|
||||
bool double_insn = false;
|
||||
|
@ -339,6 +413,69 @@ static void dump_xlated(void *buf, unsigned int len, bool opcodes)
|
|||
}
|
||||
}
|
||||
|
||||
static void print_insn_json(struct bpf_verifier_env *env, const char *fmt, ...)
|
||||
{
|
||||
unsigned int l = strlen(fmt);
|
||||
char chomped_fmt[l];
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
if (l > 0) {
|
||||
strncpy(chomped_fmt, fmt, l - 1);
|
||||
chomped_fmt[l - 1] = '\0';
|
||||
}
|
||||
jsonw_vprintf_enquote(json_wtr, chomped_fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
static void dump_xlated_json(void *buf, unsigned int len, bool opcodes)
|
||||
{
|
||||
struct bpf_insn *insn = buf;
|
||||
bool double_insn = false;
|
||||
unsigned int i;
|
||||
|
||||
jsonw_start_array(json_wtr);
|
||||
for (i = 0; i < len / sizeof(*insn); i++) {
|
||||
if (double_insn) {
|
||||
double_insn = false;
|
||||
continue;
|
||||
}
|
||||
double_insn = insn[i].code == (BPF_LD | BPF_IMM | BPF_DW);
|
||||
|
||||
jsonw_start_object(json_wtr);
|
||||
jsonw_name(json_wtr, "disasm");
|
||||
print_bpf_insn(print_insn_json, NULL, insn + i, true);
|
||||
|
||||
if (opcodes) {
|
||||
jsonw_name(json_wtr, "opcodes");
|
||||
jsonw_start_object(json_wtr);
|
||||
|
||||
jsonw_name(json_wtr, "code");
|
||||
jsonw_printf(json_wtr, "\"0x%02hhx\"", insn[i].code);
|
||||
|
||||
jsonw_name(json_wtr, "src_reg");
|
||||
jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].src_reg);
|
||||
|
||||
jsonw_name(json_wtr, "dst_reg");
|
||||
jsonw_printf(json_wtr, "\"0x%hhx\"", insn[i].dst_reg);
|
||||
|
||||
jsonw_name(json_wtr, "off");
|
||||
print_hex_data_json((uint8_t *)(&insn[i].off), 2);
|
||||
|
||||
jsonw_name(json_wtr, "imm");
|
||||
if (double_insn && i < len - 1)
|
||||
print_hex_data_json((uint8_t *)(&insn[i].imm),
|
||||
12);
|
||||
else
|
||||
print_hex_data_json((uint8_t *)(&insn[i].imm),
|
||||
4);
|
||||
jsonw_end_object(json_wtr);
|
||||
}
|
||||
jsonw_end_object(json_wtr);
|
||||
}
|
||||
jsonw_end_array(json_wtr);
|
||||
}
|
||||
|
||||
static int do_dump(int argc, char **argv)
|
||||
{
|
||||
struct bpf_prog_info info = {};
|
||||
|
@ -360,7 +497,7 @@ static int do_dump(int argc, char **argv)
|
|||
member_len = &info.xlated_prog_len;
|
||||
member_ptr = &info.xlated_prog_insns;
|
||||
} else {
|
||||
err("expected 'xlated' or 'jited', got: %s\n", *argv);
|
||||
p_err("expected 'xlated' or 'jited', got: %s", *argv);
|
||||
return -1;
|
||||
}
|
||||
NEXT_ARG();
|
||||
|
@ -375,7 +512,7 @@ static int do_dump(int argc, char **argv)
|
|||
if (is_prefix(*argv, "file")) {
|
||||
NEXT_ARG();
|
||||
if (!argc) {
|
||||
err("expected file path\n");
|
||||
p_err("expected file path");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -393,12 +530,12 @@ static int do_dump(int argc, char **argv)
|
|||
|
||||
err = bpf_obj_get_info_by_fd(fd, &info, &len);
|
||||
if (err) {
|
||||
err("can't get prog info: %s\n", strerror(errno));
|
||||
p_err("can't get prog info: %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!*member_len) {
|
||||
info("no instructions returned\n");
|
||||
p_info("no instructions returned");
|
||||
close(fd);
|
||||
return 0;
|
||||
}
|
||||
|
@ -407,7 +544,7 @@ static int do_dump(int argc, char **argv)
|
|||
|
||||
buf = malloc(buf_size);
|
||||
if (!buf) {
|
||||
err("mem alloc failed\n");
|
||||
p_err("mem alloc failed");
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
@ -420,35 +557,38 @@ static int do_dump(int argc, char **argv)
|
|||
err = bpf_obj_get_info_by_fd(fd, &info, &len);
|
||||
close(fd);
|
||||
if (err) {
|
||||
err("can't get prog info: %s\n", strerror(errno));
|
||||
p_err("can't get prog info: %s", strerror(errno));
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
if (*member_len > buf_size) {
|
||||
err("too many instructions returned\n");
|
||||
p_err("too many instructions returned");
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
if (filepath) {
|
||||
fd = open(filepath, O_WRONLY | O_CREAT | O_TRUNC, 0600);
|
||||
if (fd < 0) {
|
||||
err("can't open file %s: %s\n", filepath,
|
||||
strerror(errno));
|
||||
p_err("can't open file %s: %s", filepath,
|
||||
strerror(errno));
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
n = write(fd, buf, *member_len);
|
||||
close(fd);
|
||||
if (n != *member_len) {
|
||||
err("error writing output file: %s\n",
|
||||
n < 0 ? strerror(errno) : "short write");
|
||||
p_err("error writing output file: %s",
|
||||
n < 0 ? strerror(errno) : "short write");
|
||||
goto err_free;
|
||||
}
|
||||
} else {
|
||||
if (member_len == &info.jited_prog_len)
|
||||
disasm_print_insn(buf, *member_len, opcodes);
|
||||
else
|
||||
dump_xlated(buf, *member_len, opcodes);
|
||||
if (json_output)
|
||||
dump_xlated_json(buf, *member_len, opcodes);
|
||||
else
|
||||
dump_xlated_plain(buf, *member_len, opcodes);
|
||||
}
|
||||
|
||||
free(buf);
|
||||
|
@ -462,11 +602,21 @@ static int do_dump(int argc, char **argv)
|
|||
|
||||
static int do_pin(int argc, char **argv)
|
||||
{
|
||||
return do_pin_any(argc, argv, bpf_prog_get_fd_by_id);
|
||||
int err;
|
||||
|
||||
err = do_pin_any(argc, argv, bpf_prog_get_fd_by_id);
|
||||
if (!err && json_output)
|
||||
jsonw_null(json_wtr);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int do_help(int argc, char **argv)
|
||||
{
|
||||
if (json_output) {
|
||||
jsonw_null(json_wtr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
fprintf(stderr,
|
||||
"Usage: %s %s show [PROG]\n"
|
||||
" %s %s dump xlated PROG [{ file FILE | opcodes }]\n"
|
||||
|
@ -475,6 +625,7 @@ static int do_help(int argc, char **argv)
|
|||
" %s %s help\n"
|
||||
"\n"
|
||||
" " HELP_SPEC_PROGRAM "\n"
|
||||
" " HELP_SPEC_OPTIONS "\n"
|
||||
"",
|
||||
bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
|
||||
bin_name, argv[-2], bin_name, argv[-2]);
|
||||
|
|
Loading…
Reference in a new issue