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 attach** *PROG* *ATTACH_TYPE* [*MAP*]
 | 
				
			||||||
|	**bpftool** **prog detach** *PROG* *ATTACH_TYPE* [*MAP*]
 | 
					|	**bpftool** **prog detach** *PROG* *ATTACH_TYPE* [*MAP*]
 | 
				
			||||||
|	**bpftool** **prog tracelog**
 | 
					|	**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**
 | 
					|	**bpftool** **prog help**
 | 
				
			||||||
|
 | 
					|
 | 
				
			||||||
|	*MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
 | 
					|	*MAP* := { **id** *MAP_ID* | **pinned** *FILE* }
 | 
				
			||||||
| 
						 | 
					@ -146,6 +147,39 @@ DESCRIPTION
 | 
				
			||||||
		  streaming data from BPF programs to user space, one can use
 | 
							  streaming data from BPF programs to user space, one can use
 | 
				
			||||||
		  perf events (see also **bpftool-map**\ (8)).
 | 
							  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**
 | 
						**bpftool prog help**
 | 
				
			||||||
		  Print short help message.
 | 
							  Print short help message.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -408,10 +408,34 @@ _bpftool()
 | 
				
			||||||
                tracelog)
 | 
					                tracelog)
 | 
				
			||||||
                    return 0
 | 
					                    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 ]] && \
 | 
					                    [[ $prev == $object ]] && \
 | 
				
			||||||
                        COMPREPLY=( $( compgen -W 'dump help pin attach detach load \
 | 
					                        COMPREPLY=( $( compgen -W 'dump help pin attach detach \
 | 
				
			||||||
                            show list tracelog' -- "$cur" ) )
 | 
					                            load show list tracelog run' -- "$cur" ) )
 | 
				
			||||||
                    ;;
 | 
					                    ;;
 | 
				
			||||||
            esac
 | 
					            esac
 | 
				
			||||||
            ;;
 | 
					            ;;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -117,6 +117,35 @@ bool is_prefix(const char *pfx, const char *str)
 | 
				
			||||||
	return !memcmp(str, pfx, strlen(pfx));
 | 
						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)
 | 
					void fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	unsigned char *data = arg;
 | 
						unsigned char *data = arg;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -101,6 +101,7 @@ void p_err(const char *fmt, ...);
 | 
				
			||||||
void p_info(const char *fmt, ...);
 | 
					void p_info(const char *fmt, ...);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool is_prefix(const char *pfx, const char *str);
 | 
					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 fprint_hex(FILE *f, void *arg, unsigned int n, const char *sep);
 | 
				
			||||||
void usage(void) __noreturn;
 | 
					void usage(void) __noreturn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,6 +15,7 @@
 | 
				
			||||||
#include <sys/stat.h>
 | 
					#include <sys/stat.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <linux/err.h>
 | 
					#include <linux/err.h>
 | 
				
			||||||
 | 
					#include <linux/sizes.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <bpf.h>
 | 
					#include <bpf.h>
 | 
				
			||||||
#include <btf.h>
 | 
					#include <btf.h>
 | 
				
			||||||
| 
						 | 
					@ -748,6 +749,344 @@ static int do_detach(int argc, char **argv)
 | 
				
			||||||
	return 0;
 | 
						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)
 | 
					static int load_with_options(int argc, char **argv, bool first_prog_only)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	struct bpf_object_load_attr load_attr = { 0 };
 | 
						struct bpf_object_load_attr load_attr = { 0 };
 | 
				
			||||||
| 
						 | 
					@ -1058,6 +1397,11 @@ static int do_help(int argc, char **argv)
 | 
				
			||||||
		"                         [pinmaps MAP_DIR]\n"
 | 
							"                         [pinmaps MAP_DIR]\n"
 | 
				
			||||||
		"       %s %s attach PROG ATTACH_TYPE [MAP]\n"
 | 
							"       %s %s attach PROG ATTACH_TYPE [MAP]\n"
 | 
				
			||||||
		"       %s %s detach 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 tracelog\n"
 | 
				
			||||||
		"       %s %s help\n"
 | 
							"       %s %s help\n"
 | 
				
			||||||
		"\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], 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;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1095,6 +1440,7 @@ static const struct cmd cmds[] = {
 | 
				
			||||||
	{ "attach",	do_attach },
 | 
						{ "attach",	do_attach },
 | 
				
			||||||
	{ "detach",	do_detach },
 | 
						{ "detach",	do_detach },
 | 
				
			||||||
	{ "tracelog",	do_tracelog },
 | 
						{ "tracelog",	do_tracelog },
 | 
				
			||||||
 | 
						{ "run",	do_run },
 | 
				
			||||||
	{ 0 }
 | 
						{ 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