Commit c6ebae4c authored by Alexei Starovoitov's avatar Alexei Starovoitov
Browse files

Merge branch 'bpftool: Add inline annotations when dumping program CFGs'



Quentin Monnet says:

====================

This set contains some improvements for bpftool's "visual" program dump
option, which produces the control flow graph in a DOT format. The main
objective is to add support for inline annotations on such graphs, so that
we can have the C source code for the program showing up alongside the
instructions, when available. The last commits also make it possible to
display the line numbers or the bare opcodes in the graph, as supported by
regular program dumps.

v3:
- Fixed formatting of DOT graph: escape spaces, and remove indent that
  would cause some unwanted spaces to show up in the resulting graph.
- Don't print line information if the record is empty.
- Add '<' and ' ' to the list of escaped characters for generting the
  DOT graph.
- Truncate long file paths, use shorter field names ("line", "col") for
  code location information in the graph, add missing separator space.
- Add a commit to return an error if JSON output and CFG are both
  required.
- Add a drive-by, clean up commit for bash completion (avoid unnecessary
  calls to _bpftool_once_attr()).

v2: Replace fputc(..., stdout) with putchar(...) in dotlabel_puts().
====================

Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parents 5af607a8 73192968
Loading
Loading
Loading
Loading
+8 −10
Original line number Diff line number Diff line
@@ -28,8 +28,8 @@ PROG COMMANDS
=============

|	**bpftool** **prog** { **show** | **list** } [*PROG*]
|	**bpftool** **prog dump xlated** *PROG* [{**file** *FILE* | **opcodes** | **visual** | **linum**}]
|	**bpftool** **prog dump jited**  *PROG* [{**file** *FILE* | **opcodes** | **linum**}]
|	**bpftool** **prog dump xlated** *PROG* [{ **file** *FILE* | [**opcodes**] [**linum**] [**visual**] }]
|	**bpftool** **prog dump jited**  *PROG* [{ **file** *FILE* | [**opcodes**] [**linum**] }]
|	**bpftool** **prog pin** *PROG* *FILE*
|	**bpftool** **prog** { **load** | **loadall** } *OBJ* *PATH* [**type** *TYPE*] [**map** {**idx** *IDX* | **name** *NAME*} *MAP*] [**dev** *NAME*] [**pinmaps** *MAP_DIR*] [**autoattach**]
|	**bpftool** **prog attach** *PROG* *ATTACH_TYPE* [*MAP*]
@@ -88,7 +88,7 @@ DESCRIPTION
		  programs. On such kernels bpftool will automatically emit this
		  information as well.

	**bpftool prog dump xlated** *PROG* [{ **file** *FILE* | **opcodes** | **visual** | **linum** }]
	**bpftool prog dump xlated** *PROG* [{ **file** *FILE* | [**opcodes**] [**linum**] [**visual**] }]
		  Dump eBPF instructions of the programs from the kernel. By
		  default, eBPF will be disassembled and printed to standard
		  output in human-readable format. In this case, **opcodes**
@@ -106,11 +106,10 @@ DESCRIPTION
		  CFG in DOT format, on standard output.

		  If the programs have line_info available, the source line will
		  be displayed by default.  If **linum** is specified,
		  the filename, line number and line column will also be
		  displayed on top of the source line.
		  be displayed.  If **linum** is specified, the filename, line
		  number and line column will also be displayed.

	**bpftool prog dump jited**  *PROG* [{ **file** *FILE* | **opcodes** | **linum** }]
	**bpftool prog dump jited**  *PROG* [{ **file** *FILE* | [**opcodes**] [**linum**] }]
		  Dump jited image (host machine code) of the program.

		  If *FILE* is specified image will be written to a file,
@@ -120,9 +119,8 @@ DESCRIPTION
		  **opcodes** controls if raw opcodes will be printed.

		  If the prog has line_info available, the source line will
		  be displayed by default.  If **linum** is specified,
		  the filename, line number and line column will also be
		  displayed on top of the source line.
		  be displayed.  If **linum** is specified, the filename, line
		  number and line column will also be displayed.

	**bpftool prog pin** *PROG* *FILE*
		  Pin program *PROG* as *FILE*.
+19 −23
Original line number Diff line number Diff line
@@ -255,7 +255,7 @@ _bpftool_map_update_get_name()

_bpftool()
{
    local cur prev words objword
    local cur prev words objword json=0
    _init_completion || return

    # Deal with options
@@ -265,10 +265,13 @@ _bpftool()
        COMPREPLY=( $( compgen -W "$c" -- "$cur" ) )
        return 0
    fi
    if _bpftool_search_list -j --json -p --pretty; then
        json=1
    fi

    # Deal with simplest keywords
    case $prev in
        help|hex|opcodes|visual|linum)
        help|hex)
            return 0
            ;;
        tag)
@@ -366,13 +369,16 @@ _bpftool()
                            return 0
                            ;;
                        *)
                            # "file" is not compatible with other keywords here
                            if _bpftool_search_list 'file'; then
                                return 0
                            fi
                            if ! _bpftool_search_list 'linum opcodes visual'; then
                                _bpftool_once_attr 'file'
                            if _bpftool_search_list 'xlated'; then
                                COMPREPLY+=( $( compgen -W 'opcodes visual linum' -- \
                                    "$cur" ) )
                            else
                                COMPREPLY+=( $( compgen -W 'opcodes linum' -- \
                                    "$cur" ) )
                            fi
                            _bpftool_once_attr 'linum opcodes'
                            if _bpftool_search_list 'xlated' && [[ "$json" == 0 ]]; then
                                _bpftool_once_attr 'visual'
                            fi
                            return 0
                            ;;
@@ -502,10 +508,7 @@ _bpftool()
                            ;;
                        *)
                            COMPREPLY=( $( compgen -W "map" -- "$cur" ) )
                            _bpftool_once_attr 'type'
                            _bpftool_once_attr 'dev'
                            _bpftool_once_attr 'pinmaps'
                            _bpftool_once_attr 'autoattach'
                            _bpftool_once_attr 'type dev pinmaps autoattach'
                            return 0
                            ;;
                    esac
@@ -730,16 +733,10 @@ _bpftool()
                            esac
                            ;;
                        *)
                            _bpftool_once_attr 'type'
                            _bpftool_once_attr 'key'
                            _bpftool_once_attr 'value'
                            _bpftool_once_attr 'entries'
                            _bpftool_once_attr 'name'
                            _bpftool_once_attr 'flags'
                            _bpftool_once_attr 'type key value entries name flags dev'
                            if _bpftool_search_list 'array_of_maps' 'hash_of_maps'; then
                                _bpftool_once_attr 'inner_map'
                            fi
                            _bpftool_once_attr 'dev'
                            return 0
                            ;;
                    esac
@@ -880,8 +877,7 @@ _bpftool()
                            return 0
                            ;;
                        *)
                            _bpftool_once_attr 'cpu'
                            _bpftool_once_attr 'index'
                            _bpftool_once_attr 'cpu index'
                            return 0
                            ;;
                    esac
+83 −0
Original line number Diff line number Diff line
@@ -821,3 +821,86 @@ void btf_dump_linfo_json(const struct btf *btf,
					BPF_LINE_INFO_LINE_COL(linfo->line_col));
	}
}

static void dotlabel_puts(const char *s)
{
	for (; *s; ++s) {
		switch (*s) {
		case '\\':
		case '"':
		case '{':
		case '}':
		case '<':
		case '>':
		case '|':
		case ' ':
			putchar('\\');
			__fallthrough;
		default:
			putchar(*s);
		}
	}
}

static const char *shorten_path(const char *path)
{
	const unsigned int MAX_PATH_LEN = 32;
	size_t len = strlen(path);
	const char *shortpath;

	if (len <= MAX_PATH_LEN)
		return path;

	/* Search for last '/' under the MAX_PATH_LEN limit */
	shortpath = strchr(path + len - MAX_PATH_LEN, '/');
	if (shortpath) {
		if (shortpath < path + strlen("..."))
			/* We removed a very short prefix, e.g. "/w", and we'll
			 * make the path longer by prefixing with the ellipsis.
			 * Not worth it, keep initial path.
			 */
			return path;
		return shortpath;
	}

	/* File base name length is > MAX_PATH_LEN, search for last '/' */
			shortpath = strrchr(path, '/');
	if (shortpath)
		return shortpath;

	return path;
}

void btf_dump_linfo_dotlabel(const struct btf *btf,
			     const struct bpf_line_info *linfo, bool linum)
{
	const char *line = btf__name_by_offset(btf, linfo->line_off);

	if (!line || !strlen(line))
		return;
	line = ltrim(line);

	if (linum) {
		const char *file = btf__name_by_offset(btf, linfo->file_name_off);
		const char *shortfile;

		/* More forgiving on file because linum option is
		 * expected to provide more info than the already
		 * available src line.
		 */
		if (!file)
			shortfile = "";
		else
			shortfile = shorten_path(file);

		printf("; [%s", shortfile > file ? "..." : "");
		dotlabel_puts(shortfile);
		printf(" line:%u col:%u]\\l\\\n",
		       BPF_LINE_INFO_LINE_NUM(linfo->line_col),
		       BPF_LINE_INFO_LINE_COL(linfo->line_col));
	}

	printf("; ");
	dotlabel_puts(line);
	printf("\\l\\\n");
}
+16 −13
Original line number Diff line number Diff line
@@ -380,7 +380,9 @@ static void cfg_destroy(struct cfg *cfg)
	}
}

static void draw_bb_node(struct func_node *func, struct bb_node *bb)
static void
draw_bb_node(struct func_node *func, struct bb_node *bb, struct dump_data *dd,
	     bool opcodes, bool linum)
{
	const char *shape;

@@ -398,13 +400,10 @@ static void draw_bb_node(struct func_node *func, struct bb_node *bb)
		printf("EXIT");
	} else {
		unsigned int start_idx;
		struct dump_data dd = {};

		printf("{");
		kernel_syms_load(&dd);
		printf("{\\\n");
		start_idx = bb->head - func->start;
		dump_xlated_for_graph(&dd, bb->head, bb->tail, start_idx);
		kernel_syms_destroy(&dd);
		dump_xlated_for_graph(dd, bb->head, bb->tail, start_idx,
				      opcodes, linum);
		printf("}");
	}

@@ -430,12 +429,14 @@ static void draw_bb_succ_edges(struct func_node *func, struct bb_node *bb)
	}
}

static void func_output_bb_def(struct func_node *func)
static void
func_output_bb_def(struct func_node *func, struct dump_data *dd,
		   bool opcodes, bool linum)
{
	struct bb_node *bb;

	list_for_each_entry(bb, &func->bbs, l) {
		draw_bb_node(func, bb);
		draw_bb_node(func, bb, dd, opcodes, linum);
	}
}

@@ -455,7 +456,8 @@ static void func_output_edges(struct func_node *func)
	       func_idx, ENTRY_BLOCK_INDEX, func_idx, EXIT_BLOCK_INDEX);
}

static void cfg_dump(struct cfg *cfg)
static void
cfg_dump(struct cfg *cfg, struct dump_data *dd, bool opcodes, bool linum)
{
	struct func_node *func;

@@ -463,14 +465,15 @@ static void cfg_dump(struct cfg *cfg)
	list_for_each_entry(func, &cfg->funcs, l) {
		printf("subgraph \"cluster_%d\" {\n\tstyle=\"dashed\";\n\tcolor=\"black\";\n\tlabel=\"func_%d ()\";\n",
		       func->idx, func->idx);
		func_output_bb_def(func);
		func_output_bb_def(func, dd, opcodes, linum);
		func_output_edges(func);
		printf("}\n");
	}
	printf("}\n");
}

void dump_xlated_cfg(void *buf, unsigned int len)
void dump_xlated_cfg(struct dump_data *dd, void *buf, unsigned int len,
		     bool opcodes, bool linum)
{
	struct bpf_insn *insn = buf;
	struct cfg cfg;
@@ -479,7 +482,7 @@ void dump_xlated_cfg(void *buf, unsigned int len)
	if (cfg_build(&cfg, insn, len))
		return;

	cfg_dump(&cfg);
	cfg_dump(&cfg, dd, opcodes, linum);

	cfg_destroy(&cfg);
}
+4 −1
Original line number Diff line number Diff line
@@ -4,6 +4,9 @@
#ifndef __BPF_TOOL_CFG_H
#define __BPF_TOOL_CFG_H

void dump_xlated_cfg(void *buf, unsigned int len);
#include "xlated_dumper.h"

void dump_xlated_cfg(struct dump_data *dd, void *buf, unsigned int len,
		     bool opcodes, bool linum);

#endif /* __BPF_TOOL_CFG_H */
Loading