-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
selftests/bpf: Add checking of raw type dump in BTF writer APIs selft…
…ests Add re-usable btf_helpers.{c,h} to provide BTF-related testing routines. Start with adding a raw BTF dumping helpers. Raw BTF dump is the most succinct and at the same time a very human-friendly way to validate exact contents of BTF types. Cross-validate raw BTF dump and writable BTF in a single selftest. Raw type dump checks also serve as a good self-documentation. Signed-off-by: Andrii Nakryiko <andrii@kernel.org> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Acked-by: Song Liu <songliubraving@fb.com> Link: https://lore.kernel.org/bpf/20201105043402.2530976-7-andrii@kernel.org
- Loading branch information
Andrii Nakryiko
authored and
Alexei Starovoitov
committed
Nov 6, 2020
1 parent
197389d
commit 1306c98
Showing
4 changed files
with
256 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
// SPDX-License-Identifier: GPL-2.0 | ||
/* Copyright (c) 2020 Facebook */ | ||
#include <stdio.h> | ||
#include <errno.h> | ||
#include <bpf/btf.h> | ||
|
||
static const char * const btf_kind_str_mapping[] = { | ||
[BTF_KIND_UNKN] = "UNKNOWN", | ||
[BTF_KIND_INT] = "INT", | ||
[BTF_KIND_PTR] = "PTR", | ||
[BTF_KIND_ARRAY] = "ARRAY", | ||
[BTF_KIND_STRUCT] = "STRUCT", | ||
[BTF_KIND_UNION] = "UNION", | ||
[BTF_KIND_ENUM] = "ENUM", | ||
[BTF_KIND_FWD] = "FWD", | ||
[BTF_KIND_TYPEDEF] = "TYPEDEF", | ||
[BTF_KIND_VOLATILE] = "VOLATILE", | ||
[BTF_KIND_CONST] = "CONST", | ||
[BTF_KIND_RESTRICT] = "RESTRICT", | ||
[BTF_KIND_FUNC] = "FUNC", | ||
[BTF_KIND_FUNC_PROTO] = "FUNC_PROTO", | ||
[BTF_KIND_VAR] = "VAR", | ||
[BTF_KIND_DATASEC] = "DATASEC", | ||
}; | ||
|
||
static const char *btf_kind_str(__u16 kind) | ||
{ | ||
if (kind > BTF_KIND_DATASEC) | ||
return "UNKNOWN"; | ||
return btf_kind_str_mapping[kind]; | ||
} | ||
|
||
static const char *btf_int_enc_str(__u8 encoding) | ||
{ | ||
switch (encoding) { | ||
case 0: | ||
return "(none)"; | ||
case BTF_INT_SIGNED: | ||
return "SIGNED"; | ||
case BTF_INT_CHAR: | ||
return "CHAR"; | ||
case BTF_INT_BOOL: | ||
return "BOOL"; | ||
default: | ||
return "UNKN"; | ||
} | ||
} | ||
|
||
static const char *btf_var_linkage_str(__u32 linkage) | ||
{ | ||
switch (linkage) { | ||
case BTF_VAR_STATIC: | ||
return "static"; | ||
case BTF_VAR_GLOBAL_ALLOCATED: | ||
return "global-alloc"; | ||
default: | ||
return "(unknown)"; | ||
} | ||
} | ||
|
||
static const char *btf_func_linkage_str(const struct btf_type *t) | ||
{ | ||
switch (btf_vlen(t)) { | ||
case BTF_FUNC_STATIC: | ||
return "static"; | ||
case BTF_FUNC_GLOBAL: | ||
return "global"; | ||
case BTF_FUNC_EXTERN: | ||
return "extern"; | ||
default: | ||
return "(unknown)"; | ||
} | ||
} | ||
|
||
static const char *btf_str(const struct btf *btf, __u32 off) | ||
{ | ||
if (!off) | ||
return "(anon)"; | ||
return btf__str_by_offset(btf, off) ?: "(invalid)"; | ||
} | ||
|
||
int fprintf_btf_type_raw(FILE *out, const struct btf *btf, __u32 id) | ||
{ | ||
const struct btf_type *t; | ||
int kind, i; | ||
__u32 vlen; | ||
|
||
t = btf__type_by_id(btf, id); | ||
if (!t) | ||
return -EINVAL; | ||
|
||
vlen = btf_vlen(t); | ||
kind = btf_kind(t); | ||
|
||
fprintf(out, "[%u] %s '%s'", id, btf_kind_str(kind), btf_str(btf, t->name_off)); | ||
|
||
switch (kind) { | ||
case BTF_KIND_INT: | ||
fprintf(out, " size=%u bits_offset=%u nr_bits=%u encoding=%s", | ||
t->size, btf_int_offset(t), btf_int_bits(t), | ||
btf_int_enc_str(btf_int_encoding(t))); | ||
break; | ||
case BTF_KIND_PTR: | ||
case BTF_KIND_CONST: | ||
case BTF_KIND_VOLATILE: | ||
case BTF_KIND_RESTRICT: | ||
case BTF_KIND_TYPEDEF: | ||
fprintf(out, " type_id=%u", t->type); | ||
break; | ||
case BTF_KIND_ARRAY: { | ||
const struct btf_array *arr = btf_array(t); | ||
|
||
fprintf(out, " type_id=%u index_type_id=%u nr_elems=%u", | ||
arr->type, arr->index_type, arr->nelems); | ||
break; | ||
} | ||
case BTF_KIND_STRUCT: | ||
case BTF_KIND_UNION: { | ||
const struct btf_member *m = btf_members(t); | ||
|
||
fprintf(out, " size=%u vlen=%u", t->size, vlen); | ||
for (i = 0; i < vlen; i++, m++) { | ||
__u32 bit_off, bit_sz; | ||
|
||
bit_off = btf_member_bit_offset(t, i); | ||
bit_sz = btf_member_bitfield_size(t, i); | ||
fprintf(out, "\n\t'%s' type_id=%u bits_offset=%u", | ||
btf_str(btf, m->name_off), m->type, bit_off); | ||
if (bit_sz) | ||
fprintf(out, " bitfield_size=%u", bit_sz); | ||
} | ||
break; | ||
} | ||
case BTF_KIND_ENUM: { | ||
const struct btf_enum *v = btf_enum(t); | ||
|
||
fprintf(out, " size=%u vlen=%u", t->size, vlen); | ||
for (i = 0; i < vlen; i++, v++) { | ||
fprintf(out, "\n\t'%s' val=%u", | ||
btf_str(btf, v->name_off), v->val); | ||
} | ||
break; | ||
} | ||
case BTF_KIND_FWD: | ||
fprintf(out, " fwd_kind=%s", btf_kflag(t) ? "union" : "struct"); | ||
break; | ||
case BTF_KIND_FUNC: | ||
fprintf(out, " type_id=%u linkage=%s", t->type, btf_func_linkage_str(t)); | ||
break; | ||
case BTF_KIND_FUNC_PROTO: { | ||
const struct btf_param *p = btf_params(t); | ||
|
||
fprintf(out, " ret_type_id=%u vlen=%u", t->type, vlen); | ||
for (i = 0; i < vlen; i++, p++) { | ||
fprintf(out, "\n\t'%s' type_id=%u", | ||
btf_str(btf, p->name_off), p->type); | ||
} | ||
break; | ||
} | ||
case BTF_KIND_VAR: | ||
fprintf(out, " type_id=%u, linkage=%s", | ||
t->type, btf_var_linkage_str(btf_var(t)->linkage)); | ||
break; | ||
case BTF_KIND_DATASEC: { | ||
const struct btf_var_secinfo *v = btf_var_secinfos(t); | ||
|
||
fprintf(out, " size=%u vlen=%u", t->size, vlen); | ||
for (i = 0; i < vlen; i++, v++) { | ||
fprintf(out, "\n\ttype_id=%u offset=%u size=%u", | ||
v->type, v->offset, v->size); | ||
} | ||
break; | ||
} | ||
default: | ||
break; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
/* Print raw BTF type dump into a local buffer and return string pointer back. | ||
* Buffer *will* be overwritten by subsequent btf_type_raw_dump() calls | ||
*/ | ||
const char *btf_type_raw_dump(const struct btf *btf, int type_id) | ||
{ | ||
static char buf[16 * 1024]; | ||
FILE *buf_file; | ||
|
||
buf_file = fmemopen(buf, sizeof(buf) - 1, "w"); | ||
if (!buf_file) { | ||
fprintf(stderr, "Failed to open memstream: %d\n", errno); | ||
return NULL; | ||
} | ||
|
||
fprintf_btf_type_raw(buf_file, btf, type_id); | ||
fflush(buf_file); | ||
fclose(buf_file); | ||
|
||
return buf; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
/* SPDX-License-Identifier: GPL-2.0 */ | ||
/* Copyright (c) 2020 Facebook */ | ||
#ifndef __BTF_HELPERS_H | ||
#define __BTF_HELPERS_H | ||
|
||
#include <stdio.h> | ||
#include <bpf/btf.h> | ||
|
||
int fprintf_btf_type_raw(FILE *out, const struct btf *btf, __u32 id); | ||
const char *btf_type_raw_dump(const struct btf *btf, int type_id); | ||
|
||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters