mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	tools: bpftool: add "prog run" subcommand to test-run programs
Add a new "bpftool prog run" subcommand to run a loaded program on input
data (and possibly with input context) passed by the user.
Print output data (and output context if relevant) into a file or into
the console. Print return value and duration for the test run into the
console.
A "repeat" argument can be passed to run the program several times in a
row.
The command does not perform any kind of verification based on program
type (Is this program type allowed to use an input context?) or on data
consistency (Can I work with empty input data?), this is left to the
kernel.
Example invocation:
    # perl -e 'print "\x0" x 14' | ./bpftool prog run \
            pinned /sys/fs/bpf/sample_ret0 \
            data_in - data_out - repeat 5
    0000000 0000 0000 0000 0000 0000 0000 0000      | ........ ......
    Return value: 0, duration (average): 260ns
When one of data_in or ctx_in is "-", bpftool reads from standard input,
in binary format. Other formats (JSON, hexdump) might be supported (via
an optional command line keyword like "data_fmt_in") in the future if
relevant, but this would require doing more parsing in bpftool.
v2:
- Fix argument names for function check_single_stdin(). (Yonghong)
Signed-off-by: Quentin Monnet <quentin.monnet@netronome.com>
Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Acked-by: Yonghong Song <yhs@fb.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
			
			
This commit is contained in:
		
							parent
							
								
									e232cb6ff7
								
							
						
					
					
						commit
						ba95c74524
					
				
					 6 changed files with 485 additions and 3 deletions
				
			
		| 
						 | 
				
			
			@ -29,6 +29,7 @@ PROG COMMANDS
 | 
			
		|||
|	**bpftool** **prog attach** *PROG* *ATTACH_TYPE* [*MAP*]
 | 
			
		||||
|	**bpftool** **prog detach** *PROG* *ATTACH_TYPE* [*MAP*]
 | 
			
		||||
|	**bpftool** **prog tracelog**
 | 
			
		||||
|	**bpftool** **prog run** *PROG* **data_in** *FILE* [**data_out** *FILE* [**data_size_out** *L*]] [**ctx_in** *FILE* [**ctx_out** *FILE* [**ctx_size_out** *M*]]] [**repeat** *N*]
 | 
			
		||||
|	**bpftool** **prog help**
 | 
			
		||||
|
 | 
			
		||||
|	*MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
 | 
			
		||||
| 
						 | 
				
			
			@ -146,6 +147,39 @@ DESCRIPTION
 | 
			
		|||
		  streaming data from BPF programs to user space, one can use
 | 
			
		||||
		  perf events (see also **bpftool-map**\ (8)).
 | 
			
		||||
 | 
			
		||||
	**bpftool prog run** *PROG* **data_in** *FILE* [**data_out** *FILE* [**data_size_out** *L*]] [**ctx_in** *FILE* [**ctx_out** *FILE* [**ctx_size_out** *M*]]] [**repeat** *N*]
 | 
			
		||||
		  Run BPF program *PROG* in the kernel testing infrastructure
 | 
			
		||||
		  for BPF, meaning that the program works on the data and
 | 
			
		||||
		  context provided by the user, and not on actual packets or
 | 
			
		||||
		  monitored functions etc. Return value and duration for the
 | 
			
		||||
		  test run are printed out to the console.
 | 
			
		||||
 | 
			
		||||
		  Input data is read from the *FILE* passed with **data_in**.
 | 
			
		||||
		  If this *FILE* is "**-**", input data is read from standard
 | 
			
		||||
		  input. Input context, if any, is read from *FILE* passed with
 | 
			
		||||
		  **ctx_in**. Again, "**-**" can be used to read from standard
 | 
			
		||||
		  input, but only if standard input is not already in use for
 | 
			
		||||
		  input data. If a *FILE* is passed with **data_out**, output
 | 
			
		||||
		  data is written to that file. Similarly, output context is
 | 
			
		||||
		  written to the *FILE* passed with **ctx_out**. For both
 | 
			
		||||
		  output flows, "**-**" can be used to print to the standard
 | 
			
		||||
		  output (as plain text, or JSON if relevant option was
 | 
			
		||||
		  passed). If output keywords are omitted, output data and
 | 
			
		||||
		  context are discarded. Keywords **data_size_out** and
 | 
			
		||||
		  **ctx_size_out** are used to pass the size (in bytes) for the
 | 
			
		||||
		  output buffers to the kernel, although the default of 32 kB
 | 
			
		||||
		  should be more than enough for most cases.
 | 
			
		||||
 | 
			
		||||
		  Keyword **repeat** is used to indicate the number of
 | 
			
		||||
		  consecutive runs to perform. Note that output data and
 | 
			
		||||
		  context printed to files correspond to the last of those
 | 
			
		||||
		  runs. The duration printed out at the end of the runs is an
 | 
			
		||||
		  average over all runs performed by the command.
 | 
			
		||||
 | 
			
		||||
		  Not all program types support test run. Among those which do,
 | 
			
		||||
		  not all of them can take the **ctx_in**/**ctx_out**
 | 
			
		||||
		  arguments. bpftool does not perform checks on program types.
 | 
			
		||||
 | 
			
		||||
	**bpftool prog help**
 | 
			
		||||
		  Print short help message.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -408,10 +408,34 @@ _bpftool()
 | 
			
		|||
                tracelog)
 | 
			
		||||
                    return 0
 | 
			
		||||
                    ;;
 | 
			
		||||
                run)
 | 
			
		||||
                    if [[ ${#words[@]} -lt 5 ]]; then
 | 
			
		||||
                        _filedir
 | 
			
		||||
                        return 0
 | 
			
		||||
                    fi
 | 
			
		||||
                    case $prev in
 | 
			
		||||
                        id)
 | 
			
		||||
                            _bpftool_get_prog_ids
 | 
			
		||||
                            return 0
 | 
			
		||||
                            ;;
 | 
			
		||||
                        data_in|data_out|ctx_in|ctx_out)
 | 
			
		||||
                            _filedir
 | 
			
		||||
                            return 0
 | 
			
		||||
                            ;;
 | 
			
		||||
                        repeat|data_size_out|ctx_size_out)
 | 
			
		||||
                            return 0
 | 
			
		||||
                            ;;
 | 
			
		||||
                        *)
 | 
			
		||||
                            _bpftool_once_attr 'data_in data_out data_size_out \
 | 
			
		||||
                                ctx_in ctx_out ctx_size_out repeat'
 | 
			
		||||
                            return 0
 | 
			
		||||
                            ;;
 | 
			
		||||
                    esac
 | 
			
		||||
                    ;;
 | 
			
		||||
                *)
 | 
			
		||||
                    [[ $prev == $object ]] && \
 | 
			
		||||
                        COMPREPLY=( $( compgen -W 'dump help pin attach detach load \
 | 
			
		||||
                            show list tracelog' -- "$cur" ) )
 | 
			
		||||
                        COMPREPLY=( $( compgen -W 'dump help pin attach detach \
 | 
			
		||||
                            load show list tracelog run' -- "$cur" ) )
 | 
			
		||||
                    ;;
 | 
			
		||||
            esac
 | 
			
		||||
            ;;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -117,6 +117,35 @@ bool is_prefix(const char *pfx, const char *str)
 | 
			
		|||
	return !memcmp(str, pfx, strlen(pfx));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Last argument MUST be NULL pointer */
 | 
			
		||||
int detect_common_prefix(const char *arg, ...)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int count = 0;
 | 
			
		||||
	const char *ref;
 | 
			
		||||
	char msg[256];
 | 
			
		||||
	va_list ap;
 | 
			
		||||
 | 
			
		||||
	snprintf(msg, sizeof(msg), "ambiguous prefix: '%s' could be '", arg);
 | 
			
		||||
	va_start(ap, arg);
 | 
			
		||||
	while ((ref = va_arg(ap, const char *))) {
 | 
			
		||||
		if (!is_prefix(arg, ref))
 | 
			
		||||
			continue;
 | 
			
		||||
		count++;
 | 
			
		||||
		if (count > 1)
 | 
			
		||||
			strncat(msg, "' or '", sizeof(msg) - strlen(msg) - 1);
 | 
			
		||||
		strncat(msg, ref, sizeof(msg) - strlen(msg) - 1);
 | 
			
		||||
	}
 | 
			
		||||
	va_end(ap);
 | 
			
		||||
	strncat(msg, "'", sizeof(msg) - strlen(msg) - 1);
 | 
			
		||||
 | 
			
		||||
	if (count >= 2) {
 | 
			
		||||
		p_err(msg);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep)
 | 
			
		||||
{
 | 
			
		||||
	unsigned char *data = arg;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -101,6 +101,7 @@ void p_err(const char *fmt, ...);
 | 
			
		|||
void p_info(const char *fmt, ...);
 | 
			
		||||
 | 
			
		||||
bool is_prefix(const char *pfx, const char *str);
 | 
			
		||||
int detect_common_prefix(const char *arg, ...);
 | 
			
		||||
void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep);
 | 
			
		||||
void usage(void) __noreturn;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,6 +15,7 @@
 | 
			
		|||
#include <sys/stat.h>
 | 
			
		||||
 | 
			
		||||
#include <linux/err.h>
 | 
			
		||||
#include <linux/sizes.h>
 | 
			
		||||
 | 
			
		||||
#include <bpf.h>
 | 
			
		||||
#include <btf.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -748,6 +749,344 @@ static int do_detach(int argc, char **argv)
 | 
			
		|||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int check_single_stdin(char *file_data_in, char *file_ctx_in)
 | 
			
		||||
{
 | 
			
		||||
	if (file_data_in && file_ctx_in &&
 | 
			
		||||
	    !strcmp(file_data_in, "-") && !strcmp(file_ctx_in, "-")) {
 | 
			
		||||
		p_err("cannot use standard input for both data_in and ctx_in");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int get_run_data(const char *fname, void **data_ptr, unsigned int *size)
 | 
			
		||||
{
 | 
			
		||||
	size_t block_size = 256;
 | 
			
		||||
	size_t buf_size = block_size;
 | 
			
		||||
	size_t nb_read = 0;
 | 
			
		||||
	void *tmp;
 | 
			
		||||
	FILE *f;
 | 
			
		||||
 | 
			
		||||
	if (!fname) {
 | 
			
		||||
		*data_ptr = NULL;
 | 
			
		||||
		*size = 0;
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!strcmp(fname, "-"))
 | 
			
		||||
		f = stdin;
 | 
			
		||||
	else
 | 
			
		||||
		f = fopen(fname, "r");
 | 
			
		||||
	if (!f) {
 | 
			
		||||
		p_err("failed to open %s: %s", fname, strerror(errno));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	*data_ptr = malloc(block_size);
 | 
			
		||||
	if (!*data_ptr) {
 | 
			
		||||
		p_err("failed to allocate memory for data_in/ctx_in: %s",
 | 
			
		||||
		      strerror(errno));
 | 
			
		||||
		goto err_fclose;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	while ((nb_read += fread(*data_ptr + nb_read, 1, block_size, f))) {
 | 
			
		||||
		if (feof(f))
 | 
			
		||||
			break;
 | 
			
		||||
		if (ferror(f)) {
 | 
			
		||||
			p_err("failed to read data_in/ctx_in from %s: %s",
 | 
			
		||||
			      fname, strerror(errno));
 | 
			
		||||
			goto err_free;
 | 
			
		||||
		}
 | 
			
		||||
		if (nb_read > buf_size - block_size) {
 | 
			
		||||
			if (buf_size == UINT32_MAX) {
 | 
			
		||||
				p_err("data_in/ctx_in is too long (max: %d)",
 | 
			
		||||
				      UINT32_MAX);
 | 
			
		||||
				goto err_free;
 | 
			
		||||
			}
 | 
			
		||||
			/* No space for fread()-ing next chunk; realloc() */
 | 
			
		||||
			buf_size *= 2;
 | 
			
		||||
			tmp = realloc(*data_ptr, buf_size);
 | 
			
		||||
			if (!tmp) {
 | 
			
		||||
				p_err("failed to reallocate data_in/ctx_in: %s",
 | 
			
		||||
				      strerror(errno));
 | 
			
		||||
				goto err_free;
 | 
			
		||||
			}
 | 
			
		||||
			*data_ptr = tmp;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if (f != stdin)
 | 
			
		||||
		fclose(f);
 | 
			
		||||
 | 
			
		||||
	*size = nb_read;
 | 
			
		||||
	return 0;
 | 
			
		||||
 | 
			
		||||
err_free:
 | 
			
		||||
	free(*data_ptr);
 | 
			
		||||
	*data_ptr = NULL;
 | 
			
		||||
err_fclose:
 | 
			
		||||
	if (f != stdin)
 | 
			
		||||
		fclose(f);
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void hex_print(void *data, unsigned int size, FILE *f)
 | 
			
		||||
{
 | 
			
		||||
	size_t i, j;
 | 
			
		||||
	char c;
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < size; i += 16) {
 | 
			
		||||
		/* Row offset */
 | 
			
		||||
		fprintf(f, "%07zx\t", i);
 | 
			
		||||
 | 
			
		||||
		/* Hexadecimal values */
 | 
			
		||||
		for (j = i; j < i + 16 && j < size; j++)
 | 
			
		||||
			fprintf(f, "%02x%s", *(uint8_t *)(data + j),
 | 
			
		||||
				j % 2 ? " " : "");
 | 
			
		||||
		for (; j < i + 16; j++)
 | 
			
		||||
			fprintf(f, "  %s", j % 2 ? " " : "");
 | 
			
		||||
 | 
			
		||||
		/* ASCII values (if relevant), '.' otherwise */
 | 
			
		||||
		fprintf(f, "| ");
 | 
			
		||||
		for (j = i; j < i + 16 && j < size; j++) {
 | 
			
		||||
			c = *(char *)(data + j);
 | 
			
		||||
			if (c < ' ' || c > '~')
 | 
			
		||||
				c = '.';
 | 
			
		||||
			fprintf(f, "%c%s", c, j == i + 7 ? " " : "");
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		fprintf(f, "\n");
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
print_run_output(void *data, unsigned int size, const char *fname,
 | 
			
		||||
		 const char *json_key)
 | 
			
		||||
{
 | 
			
		||||
	size_t nb_written;
 | 
			
		||||
	FILE *f;
 | 
			
		||||
 | 
			
		||||
	if (!fname)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	if (!strcmp(fname, "-")) {
 | 
			
		||||
		f = stdout;
 | 
			
		||||
		if (json_output) {
 | 
			
		||||
			jsonw_name(json_wtr, json_key);
 | 
			
		||||
			print_data_json(data, size);
 | 
			
		||||
		} else {
 | 
			
		||||
			hex_print(data, size, f);
 | 
			
		||||
		}
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	f = fopen(fname, "w");
 | 
			
		||||
	if (!f) {
 | 
			
		||||
		p_err("failed to open %s: %s", fname, strerror(errno));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	nb_written = fwrite(data, 1, size, f);
 | 
			
		||||
	fclose(f);
 | 
			
		||||
	if (nb_written != size) {
 | 
			
		||||
		p_err("failed to write output data/ctx: %s", strerror(errno));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int alloc_run_data(void **data_ptr, unsigned int size_out)
 | 
			
		||||
{
 | 
			
		||||
	*data_ptr = calloc(size_out, 1);
 | 
			
		||||
	if (!*data_ptr) {
 | 
			
		||||
		p_err("failed to allocate memory for output data/ctx: %s",
 | 
			
		||||
		      strerror(errno));
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int do_run(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	char *data_fname_in = NULL, *data_fname_out = NULL;
 | 
			
		||||
	char *ctx_fname_in = NULL, *ctx_fname_out = NULL;
 | 
			
		||||
	struct bpf_prog_test_run_attr test_attr = {0};
 | 
			
		||||
	const unsigned int default_size = SZ_32K;
 | 
			
		||||
	void *data_in = NULL, *data_out = NULL;
 | 
			
		||||
	void *ctx_in = NULL, *ctx_out = NULL;
 | 
			
		||||
	unsigned int repeat = 1;
 | 
			
		||||
	int fd, err;
 | 
			
		||||
 | 
			
		||||
	if (!REQ_ARGS(4))
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	fd = prog_parse_fd(&argc, &argv);
 | 
			
		||||
	if (fd < 0)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	while (argc) {
 | 
			
		||||
		if (detect_common_prefix(*argv, "data_in", "data_out",
 | 
			
		||||
					 "data_size_out", NULL))
 | 
			
		||||
			return -1;
 | 
			
		||||
		if (detect_common_prefix(*argv, "ctx_in", "ctx_out",
 | 
			
		||||
					 "ctx_size_out", NULL))
 | 
			
		||||
			return -1;
 | 
			
		||||
 | 
			
		||||
		if (is_prefix(*argv, "data_in")) {
 | 
			
		||||
			NEXT_ARG();
 | 
			
		||||
			if (!REQ_ARGS(1))
 | 
			
		||||
				return -1;
 | 
			
		||||
 | 
			
		||||
			data_fname_in = GET_ARG();
 | 
			
		||||
			if (check_single_stdin(data_fname_in, ctx_fname_in))
 | 
			
		||||
				return -1;
 | 
			
		||||
		} else if (is_prefix(*argv, "data_out")) {
 | 
			
		||||
			NEXT_ARG();
 | 
			
		||||
			if (!REQ_ARGS(1))
 | 
			
		||||
				return -1;
 | 
			
		||||
 | 
			
		||||
			data_fname_out = GET_ARG();
 | 
			
		||||
		} else if (is_prefix(*argv, "data_size_out")) {
 | 
			
		||||
			char *endptr;
 | 
			
		||||
 | 
			
		||||
			NEXT_ARG();
 | 
			
		||||
			if (!REQ_ARGS(1))
 | 
			
		||||
				return -1;
 | 
			
		||||
 | 
			
		||||
			test_attr.data_size_out = strtoul(*argv, &endptr, 0);
 | 
			
		||||
			if (*endptr) {
 | 
			
		||||
				p_err("can't parse %s as output data size",
 | 
			
		||||
				      *argv);
 | 
			
		||||
				return -1;
 | 
			
		||||
			}
 | 
			
		||||
			NEXT_ARG();
 | 
			
		||||
		} else if (is_prefix(*argv, "ctx_in")) {
 | 
			
		||||
			NEXT_ARG();
 | 
			
		||||
			if (!REQ_ARGS(1))
 | 
			
		||||
				return -1;
 | 
			
		||||
 | 
			
		||||
			ctx_fname_in = GET_ARG();
 | 
			
		||||
			if (check_single_stdin(data_fname_in, ctx_fname_in))
 | 
			
		||||
				return -1;
 | 
			
		||||
		} else if (is_prefix(*argv, "ctx_out")) {
 | 
			
		||||
			NEXT_ARG();
 | 
			
		||||
			if (!REQ_ARGS(1))
 | 
			
		||||
				return -1;
 | 
			
		||||
 | 
			
		||||
			ctx_fname_out = GET_ARG();
 | 
			
		||||
		} else if (is_prefix(*argv, "ctx_size_out")) {
 | 
			
		||||
			char *endptr;
 | 
			
		||||
 | 
			
		||||
			NEXT_ARG();
 | 
			
		||||
			if (!REQ_ARGS(1))
 | 
			
		||||
				return -1;
 | 
			
		||||
 | 
			
		||||
			test_attr.ctx_size_out = strtoul(*argv, &endptr, 0);
 | 
			
		||||
			if (*endptr) {
 | 
			
		||||
				p_err("can't parse %s as output context size",
 | 
			
		||||
				      *argv);
 | 
			
		||||
				return -1;
 | 
			
		||||
			}
 | 
			
		||||
			NEXT_ARG();
 | 
			
		||||
		} else if (is_prefix(*argv, "repeat")) {
 | 
			
		||||
			char *endptr;
 | 
			
		||||
 | 
			
		||||
			NEXT_ARG();
 | 
			
		||||
			if (!REQ_ARGS(1))
 | 
			
		||||
				return -1;
 | 
			
		||||
 | 
			
		||||
			repeat = strtoul(*argv, &endptr, 0);
 | 
			
		||||
			if (*endptr) {
 | 
			
		||||
				p_err("can't parse %s as repeat number",
 | 
			
		||||
				      *argv);
 | 
			
		||||
				return -1;
 | 
			
		||||
			}
 | 
			
		||||
			NEXT_ARG();
 | 
			
		||||
		} else {
 | 
			
		||||
			p_err("expected no more arguments, 'data_in', 'data_out', 'data_size_out', 'ctx_in', 'ctx_out', 'ctx_size_out' or 'repeat', got: '%s'?",
 | 
			
		||||
			      *argv);
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = get_run_data(data_fname_in, &data_in, &test_attr.data_size_in);
 | 
			
		||||
	if (err)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	if (data_in) {
 | 
			
		||||
		if (!test_attr.data_size_out)
 | 
			
		||||
			test_attr.data_size_out = default_size;
 | 
			
		||||
		err = alloc_run_data(&data_out, test_attr.data_size_out);
 | 
			
		||||
		if (err)
 | 
			
		||||
			goto free_data_in;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = get_run_data(ctx_fname_in, &ctx_in, &test_attr.ctx_size_in);
 | 
			
		||||
	if (err)
 | 
			
		||||
		goto free_data_out;
 | 
			
		||||
 | 
			
		||||
	if (ctx_in) {
 | 
			
		||||
		if (!test_attr.ctx_size_out)
 | 
			
		||||
			test_attr.ctx_size_out = default_size;
 | 
			
		||||
		err = alloc_run_data(&ctx_out, test_attr.ctx_size_out);
 | 
			
		||||
		if (err)
 | 
			
		||||
			goto free_ctx_in;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	test_attr.prog_fd	= fd;
 | 
			
		||||
	test_attr.repeat	= repeat;
 | 
			
		||||
	test_attr.data_in	= data_in;
 | 
			
		||||
	test_attr.data_out	= data_out;
 | 
			
		||||
	test_attr.ctx_in	= ctx_in;
 | 
			
		||||
	test_attr.ctx_out	= ctx_out;
 | 
			
		||||
 | 
			
		||||
	err = bpf_prog_test_run_xattr(&test_attr);
 | 
			
		||||
	if (err) {
 | 
			
		||||
		p_err("failed to run program: %s", strerror(errno));
 | 
			
		||||
		goto free_ctx_out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = 0;
 | 
			
		||||
 | 
			
		||||
	if (json_output)
 | 
			
		||||
		jsonw_start_object(json_wtr);	/* root */
 | 
			
		||||
 | 
			
		||||
	/* Do not exit on errors occurring when printing output data/context,
 | 
			
		||||
	 * we still want to print return value and duration for program run.
 | 
			
		||||
	 */
 | 
			
		||||
	if (test_attr.data_size_out)
 | 
			
		||||
		err += print_run_output(test_attr.data_out,
 | 
			
		||||
					test_attr.data_size_out,
 | 
			
		||||
					data_fname_out, "data_out");
 | 
			
		||||
	if (test_attr.ctx_size_out)
 | 
			
		||||
		err += print_run_output(test_attr.ctx_out,
 | 
			
		||||
					test_attr.ctx_size_out,
 | 
			
		||||
					ctx_fname_out, "ctx_out");
 | 
			
		||||
 | 
			
		||||
	if (json_output) {
 | 
			
		||||
		jsonw_uint_field(json_wtr, "retval", test_attr.retval);
 | 
			
		||||
		jsonw_uint_field(json_wtr, "duration", test_attr.duration);
 | 
			
		||||
		jsonw_end_object(json_wtr);	/* root */
 | 
			
		||||
	} else {
 | 
			
		||||
		fprintf(stdout, "Return value: %u, duration%s: %uns\n",
 | 
			
		||||
			test_attr.retval,
 | 
			
		||||
			repeat > 1 ? " (average)" : "", test_attr.duration);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
free_ctx_out:
 | 
			
		||||
	free(ctx_out);
 | 
			
		||||
free_ctx_in:
 | 
			
		||||
	free(ctx_in);
 | 
			
		||||
free_data_out:
 | 
			
		||||
	free(data_out);
 | 
			
		||||
free_data_in:
 | 
			
		||||
	free(data_in);
 | 
			
		||||
 | 
			
		||||
	return err;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int load_with_options(int argc, char **argv, bool first_prog_only)
 | 
			
		||||
{
 | 
			
		||||
	struct bpf_object_load_attr load_attr = { 0 };
 | 
			
		||||
| 
						 | 
				
			
			@ -1058,6 +1397,11 @@ static int do_help(int argc, char **argv)
 | 
			
		|||
		"                         [pinmaps MAP_DIR]\n"
 | 
			
		||||
		"       %s %s attach PROG ATTACH_TYPE [MAP]\n"
 | 
			
		||||
		"       %s %s detach PROG ATTACH_TYPE [MAP]\n"
 | 
			
		||||
		"       %s %s run PROG \\\n"
 | 
			
		||||
		"                         data_in FILE \\\n"
 | 
			
		||||
		"                         [data_out FILE [data_size_out L]] \\\n"
 | 
			
		||||
		"                         [ctx_in FILE [ctx_out FILE [ctx_size_out M]]] \\\n"
 | 
			
		||||
		"                         [repeat N]\n"
 | 
			
		||||
		"       %s %s tracelog\n"
 | 
			
		||||
		"       %s %s help\n"
 | 
			
		||||
		"\n"
 | 
			
		||||
| 
						 | 
				
			
			@ -1079,7 +1423,8 @@ static int do_help(int argc, char **argv)
 | 
			
		|||
		"",
 | 
			
		||||
		bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
 | 
			
		||||
		bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
 | 
			
		||||
		bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2]);
 | 
			
		||||
		bin_name, argv[-2], bin_name, argv[-2], bin_name, argv[-2],
 | 
			
		||||
		bin_name, argv[-2]);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1095,6 +1440,7 @@ static const struct cmd cmds[] = {
 | 
			
		|||
	{ "attach",	do_attach },
 | 
			
		||||
	{ "detach",	do_detach },
 | 
			
		||||
	{ "tracelog",	do_tracelog },
 | 
			
		||||
	{ "run",	do_run },
 | 
			
		||||
	{ 0 }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										48
									
								
								tools/include/linux/sizes.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								tools/include/linux/sizes.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,48 @@
 | 
			
		|||
/* SPDX-License-Identifier: GPL-2.0-only */
 | 
			
		||||
/*
 | 
			
		||||
 * include/linux/sizes.h
 | 
			
		||||
 */
 | 
			
		||||
#ifndef __LINUX_SIZES_H__
 | 
			
		||||
#define __LINUX_SIZES_H__
 | 
			
		||||
 | 
			
		||||
#include <linux/const.h>
 | 
			
		||||
 | 
			
		||||
#define SZ_1				0x00000001
 | 
			
		||||
#define SZ_2				0x00000002
 | 
			
		||||
#define SZ_4				0x00000004
 | 
			
		||||
#define SZ_8				0x00000008
 | 
			
		||||
#define SZ_16				0x00000010
 | 
			
		||||
#define SZ_32				0x00000020
 | 
			
		||||
#define SZ_64				0x00000040
 | 
			
		||||
#define SZ_128				0x00000080
 | 
			
		||||
#define SZ_256				0x00000100
 | 
			
		||||
#define SZ_512				0x00000200
 | 
			
		||||
 | 
			
		||||
#define SZ_1K				0x00000400
 | 
			
		||||
#define SZ_2K				0x00000800
 | 
			
		||||
#define SZ_4K				0x00001000
 | 
			
		||||
#define SZ_8K				0x00002000
 | 
			
		||||
#define SZ_16K				0x00004000
 | 
			
		||||
#define SZ_32K				0x00008000
 | 
			
		||||
#define SZ_64K				0x00010000
 | 
			
		||||
#define SZ_128K				0x00020000
 | 
			
		||||
#define SZ_256K				0x00040000
 | 
			
		||||
#define SZ_512K				0x00080000
 | 
			
		||||
 | 
			
		||||
#define SZ_1M				0x00100000
 | 
			
		||||
#define SZ_2M				0x00200000
 | 
			
		||||
#define SZ_4M				0x00400000
 | 
			
		||||
#define SZ_8M				0x00800000
 | 
			
		||||
#define SZ_16M				0x01000000
 | 
			
		||||
#define SZ_32M				0x02000000
 | 
			
		||||
#define SZ_64M				0x04000000
 | 
			
		||||
#define SZ_128M				0x08000000
 | 
			
		||||
#define SZ_256M				0x10000000
 | 
			
		||||
#define SZ_512M				0x20000000
 | 
			
		||||
 | 
			
		||||
#define SZ_1G				0x40000000
 | 
			
		||||
#define SZ_2G				0x80000000
 | 
			
		||||
 | 
			
		||||
#define SZ_4G				_AC(0x100000000, ULL)
 | 
			
		||||
 | 
			
		||||
#endif /* __LINUX_SIZES_H__ */
 | 
			
		||||
		Loading…
	
		Reference in a new issue