Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bpftool: Support dumping metadata

Dump metadata in the 'bpftool prog' list if it's present.
For some formatting some BTF code is put directly in the
metadata dumping. Sanity checks on the map and the kind of the btf_type
to make sure we are actually dumping what we are expecting.

A helper jsonw_reset is added to json writer so we can reuse the same
json writer without having extraneous commas.

Sample output:

$ bpftool prog
6: cgroup_skb name prog tag bcf7977d3b93787c gpl
[...]
btf_id 4
metadata:
a = "foo"
b = 1

$ bpftool prog --json --pretty
[{
"id": 6,
[...]
"btf_id": 4,
"metadata": {
"a": "foo",
"b": 1
}
}
]

Signed-off-by: YiFei Zhu <zhuyifei@google.com>
Signed-off-by: Stanislav Fomichev <sdf@google.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Cc: YiFei Zhu <zhuyifei1999@gmail.com>
Link: https://lore.kernel.org/bpf/20200915234543.3220146-5-sdf@google.com

authored by

YiFei Zhu and committed by
Alexei Starovoitov
aff52e68 5d23328d

+208
+6
tools/bpf/bpftool/json_writer.c
··· 119 119 self->pretty = on; 120 120 } 121 121 122 + void jsonw_reset(json_writer_t *self) 123 + { 124 + assert(self->depth == 0); 125 + self->sep = '\0'; 126 + } 127 + 122 128 /* Basic blocks */ 123 129 static void jsonw_begin(json_writer_t *self, int c) 124 130 {
+3
tools/bpf/bpftool/json_writer.h
··· 27 27 /* Cause output to have pretty whitespace */ 28 28 void jsonw_pretty(json_writer_t *self, bool on); 29 29 30 + /* Reset separator to create new JSON */ 31 + void jsonw_reset(json_writer_t *self); 32 + 30 33 /* Add property name */ 31 34 void jsonw_name(json_writer_t *self, const char *name); 32 35
+199
tools/bpf/bpftool/prog.c
··· 29 29 #include "main.h" 30 30 #include "xlated_dumper.h" 31 31 32 + #define BPF_METADATA_PREFIX "bpf_metadata_" 33 + #define BPF_METADATA_PREFIX_LEN (sizeof(BPF_METADATA_PREFIX) - 1) 34 + 32 35 const char * const prog_type_name[] = { 33 36 [BPF_PROG_TYPE_UNSPEC] = "unspec", 34 37 [BPF_PROG_TYPE_SOCKET_FILTER] = "socket_filter", ··· 154 151 } 155 152 } 156 153 154 + static void *find_metadata(int prog_fd, struct bpf_map_info *map_info) 155 + { 156 + struct bpf_prog_info prog_info; 157 + __u32 prog_info_len; 158 + __u32 map_info_len; 159 + void *value = NULL; 160 + __u32 *map_ids; 161 + int nr_maps; 162 + int key = 0; 163 + int map_fd; 164 + int ret; 165 + __u32 i; 166 + 167 + memset(&prog_info, 0, sizeof(prog_info)); 168 + prog_info_len = sizeof(prog_info); 169 + ret = bpf_obj_get_info_by_fd(prog_fd, &prog_info, &prog_info_len); 170 + if (ret) 171 + return NULL; 172 + 173 + if (!prog_info.nr_map_ids) 174 + return NULL; 175 + 176 + map_ids = calloc(prog_info.nr_map_ids, sizeof(__u32)); 177 + if (!map_ids) 178 + return NULL; 179 + 180 + nr_maps = prog_info.nr_map_ids; 181 + memset(&prog_info, 0, sizeof(prog_info)); 182 + prog_info.nr_map_ids = nr_maps; 183 + prog_info.map_ids = ptr_to_u64(map_ids); 184 + prog_info_len = sizeof(prog_info); 185 + 186 + ret = bpf_obj_get_info_by_fd(prog_fd, &prog_info, &prog_info_len); 187 + if (ret) 188 + goto free_map_ids; 189 + 190 + for (i = 0; i < prog_info.nr_map_ids; i++) { 191 + map_fd = bpf_map_get_fd_by_id(map_ids[i]); 192 + if (map_fd < 0) 193 + goto free_map_ids; 194 + 195 + memset(map_info, 0, sizeof(*map_info)); 196 + map_info_len = sizeof(*map_info); 197 + ret = bpf_obj_get_info_by_fd(map_fd, map_info, &map_info_len); 198 + if (ret < 0) { 199 + close(map_fd); 200 + goto free_map_ids; 201 + } 202 + 203 + if (map_info->type != BPF_MAP_TYPE_ARRAY || 204 + map_info->key_size != sizeof(int) || 205 + map_info->max_entries != 1 || 206 + !map_info->btf_value_type_id || 207 + !strstr(map_info->name, ".rodata")) { 208 + close(map_fd); 209 + continue; 210 + } 211 + 212 + value = malloc(map_info->value_size); 213 + if (!value) { 214 + close(map_fd); 215 + goto free_map_ids; 216 + } 217 + 218 + if (bpf_map_lookup_elem(map_fd, &key, value)) { 219 + close(map_fd); 220 + free(value); 221 + value = NULL; 222 + goto free_map_ids; 223 + } 224 + 225 + close(map_fd); 226 + break; 227 + } 228 + 229 + free_map_ids: 230 + free(map_ids); 231 + return value; 232 + } 233 + 234 + static bool has_metadata_prefix(const char *s) 235 + { 236 + return strncmp(s, BPF_METADATA_PREFIX, BPF_METADATA_PREFIX_LEN) == 0; 237 + } 238 + 239 + static void show_prog_metadata(int fd, __u32 num_maps) 240 + { 241 + const struct btf_type *t_datasec, *t_var; 242 + struct bpf_map_info map_info; 243 + struct btf_var_secinfo *vsi; 244 + bool printed_header = false; 245 + struct btf *btf = NULL; 246 + unsigned int i, vlen; 247 + void *value = NULL; 248 + const char *name; 249 + int err; 250 + 251 + if (!num_maps) 252 + return; 253 + 254 + memset(&map_info, 0, sizeof(map_info)); 255 + value = find_metadata(fd, &map_info); 256 + if (!value) 257 + return; 258 + 259 + err = btf__get_from_id(map_info.btf_id, &btf); 260 + if (err || !btf) 261 + goto out_free; 262 + 263 + t_datasec = btf__type_by_id(btf, map_info.btf_value_type_id); 264 + if (!btf_is_datasec(t_datasec)) 265 + goto out_free; 266 + 267 + vlen = btf_vlen(t_datasec); 268 + vsi = btf_var_secinfos(t_datasec); 269 + 270 + /* We don't proceed to check the kinds of the elements of the DATASEC. 271 + * The verifier enforces them to be BTF_KIND_VAR. 272 + */ 273 + 274 + if (json_output) { 275 + struct btf_dumper d = { 276 + .btf = btf, 277 + .jw = json_wtr, 278 + .is_plain_text = false, 279 + }; 280 + 281 + for (i = 0; i < vlen; i++, vsi++) { 282 + t_var = btf__type_by_id(btf, vsi->type); 283 + name = btf__name_by_offset(btf, t_var->name_off); 284 + 285 + if (!has_metadata_prefix(name)) 286 + continue; 287 + 288 + if (!printed_header) { 289 + jsonw_name(json_wtr, "metadata"); 290 + jsonw_start_object(json_wtr); 291 + printed_header = true; 292 + } 293 + 294 + jsonw_name(json_wtr, name + BPF_METADATA_PREFIX_LEN); 295 + err = btf_dumper_type(&d, t_var->type, value + vsi->offset); 296 + if (err) { 297 + p_err("btf dump failed: %d", err); 298 + break; 299 + } 300 + } 301 + if (printed_header) 302 + jsonw_end_object(json_wtr); 303 + } else { 304 + json_writer_t *btf_wtr = jsonw_new(stdout); 305 + struct btf_dumper d = { 306 + .btf = btf, 307 + .jw = btf_wtr, 308 + .is_plain_text = true, 309 + }; 310 + 311 + if (!btf_wtr) { 312 + p_err("jsonw alloc failed"); 313 + goto out_free; 314 + } 315 + 316 + for (i = 0; i < vlen; i++, vsi++) { 317 + t_var = btf__type_by_id(btf, vsi->type); 318 + name = btf__name_by_offset(btf, t_var->name_off); 319 + 320 + if (!has_metadata_prefix(name)) 321 + continue; 322 + 323 + if (!printed_header) { 324 + printf("\tmetadata:"); 325 + printed_header = true; 326 + } 327 + 328 + printf("\n\t\t%s = ", name + BPF_METADATA_PREFIX_LEN); 329 + 330 + jsonw_reset(btf_wtr); 331 + err = btf_dumper_type(&d, t_var->type, value + vsi->offset); 332 + if (err) { 333 + p_err("btf dump failed: %d", err); 334 + break; 335 + } 336 + } 337 + if (printed_header) 338 + jsonw_destroy(&btf_wtr); 339 + } 340 + 341 + out_free: 342 + btf__free(btf); 343 + free(value); 344 + } 345 + 157 346 static void print_prog_header_json(struct bpf_prog_info *info) 158 347 { 159 348 jsonw_uint_field(json_wtr, "id", info->id); ··· 423 228 424 229 emit_obj_refs_json(&refs_table, info->id, json_wtr); 425 230 231 + show_prog_metadata(fd, info->nr_map_ids); 232 + 426 233 jsonw_end_object(json_wtr); 427 234 } 428 235 ··· 494 297 emit_obj_refs_plain(&refs_table, info->id, "\n\tpids "); 495 298 496 299 printf("\n"); 300 + 301 + show_prog_metadata(fd, info->nr_map_ids); 497 302 } 498 303 499 304 static int show_prog(int fd)