btf_loader: Add support for BTF_KIND_FUNC
Some changes to the fprintf routines were needed, as BTF has as the function type just a BTF_KIND_FUNC_PROTO, while DWARF has as the type for a function its return value type. With a function->btf flag this was overcome and all the other goodies in pfunct are present, for instance: $ pahole -JV examples/tcp.o | grep -w FUNC | head [4068] FUNC tcp_init type_id=4067 [4070] FUNC tcp_abort type_id=4069 [4072] FUNC tcp_done type_id=4071 [4074] FUNC tcp_md5_hash_key type_id=4073 [4076] FUNC tcp_md5_hash_skb_data type_id=4075 [4078] FUNC tcp_get_md5sig_pool type_id=4077 [4080] FUNC tcp_alloc_md5sig_pool type_id=4079 [4082] FUNC compat_tcp_getsockopt type_id=4081 [4084] FUNC tcp_getsockopt type_id=4083 [4086] FUNC tcp_get_timestamping_opt_stats type_id=4085 $ $ pfunct -F btf examples/tcp.o | head memset memcpy tcp_enter_memory_pressure tcp_leave_memory_pressure tcp_init_sock tcp_init_transfer tcp_poll tcp_ioctl tcp_splice_read sk_stream_alloc_skb $ $ pfunct --prototype -F btf examples/tcp.o | head void * memset(void * p, int c, __kernel_size_t size); void * memcpy(void * p, const void * q, __kernel_size_t size); void tcp_enter_memory_pressure(struct sock * sk); void tcp_leave_memory_pressure(struct sock * sk); void tcp_init_sock(struct sock * sk); void tcp_init_transfer(struct sock * sk, int bpf_op); __poll_t tcp_poll(struct file * file, struct socket * sock, poll_table * wait); int tcp_ioctl(struct sock * sk, int cmd, long unsigned int arg); ssize_t tcp_splice_read(struct socket * sock, loff_t * ppos, struct pipe_inode_info * pipe, size_t len, unsigned int flags); struct sk_buff * sk_stream_alloc_skb(struct sock * sk, int size, gfp_t gfp, bool force_schedule); $ Now to ask just for the 'struct sock' 'methods', i.e. functions that have as one of its arguments a pointer to the given 'class' name: $ pfunct --class sock -F btf examples/tcp.o | head tcp_abort tcp_done compat_tcp_getsockopt tcp_getsockopt tcp_get_info compat_tcp_setsockopt tcp_setsockopt tcp_disconnect tcp_write_queue_purge tcp_close $ Then ask for the prototypes, which requires -V, should have that fixed: $ pfunct -V --prototypes --class sock -F btf examples/tcp.o | head int tcp_abort(struct sock * sk, int err); void tcp_done(struct sock * sk); int compat_tcp_getsockopt(struct sock * sk, int level, int optname, char * optval, int * optlen); int tcp_getsockopt(struct sock * sk, int level, int optname, char * optval, int * optlen); void tcp_get_info(struct sock * sk, struct tcp_info * info); int compat_tcp_setsockopt(struct sock * sk, int level, int optname, char * optval, unsigned int optlen); int tcp_setsockopt(struct sock * sk, int level, int optname, char * optval, unsigned int optlen); int tcp_disconnect(struct sock * sk, int flags); void tcp_write_queue_purge(struct sock * sk); void tcp_close(struct sock * sk, long int timeout); $ Don't like prototypes with parm names, got you covered: $ pfunct --no_parm_names -V --prototypes --class sock -F btf examples/tcp.o | head int tcp_abort(struct sock *, int); void tcp_done(struct sock *); int compat_tcp_getsockopt(struct sock *, int, int, char *, int *); int tcp_getsockopt(struct sock *, int, int, char *, int *); void tcp_get_info(struct sock *, struct tcp_info *); int compat_tcp_setsockopt(struct sock *, int, int, char *, unsigned int); int tcp_setsockopt(struct sock *, int, int, char *, unsigned int); int tcp_disconnect(struct sock *, int); void tcp_write_queue_purge(struct sock *); void tcp_close(struct sock *, long int); $ Don't like long options and want just one function? $ pfunct -f tcp_setsockopt -F btf examples/tcp.o int tcp_setsockopt(struct sock * sk, int level, int optname, char * optval, unsigned int optlen); $ Want to generate compileable code for all of those functions, full with the necessary types, etc? $ pfunct -F btf --compile examples/tcp.o > a.c $ gcc -c -o a.o a.c $ pfunct -F dwarf --prototypes --class sock a.o | head pfunct: a.o: No debugging information found $ gcc -g -c -o a.o a.c $ pfunct -V -F dwarf --prototypes --class sock a.o | head void tcp_enter_memory_pressure(struct sock * sk); void tcp_leave_memory_pressure(struct sock * sk); void tcp_init_sock(struct sock * sk); void tcp_init_transfer(struct sock * sk, int bpf_op); int tcp_ioctl(struct sock * sk, int cmd, long unsigned int arg); struct sk_buff * sk_stream_alloc_skb(struct sock * sk, int size, gfp_t gfp, bool force_schedule); ssize_t do_tcp_sendpages(struct sock * sk, struct page * page, int offset, size_t size, int flags); int tcp_sendpage_locked(struct sock * sk, struct page * page, int offset, size_t size, int flags); int tcp_sendpage(struct sock * sk, struct page * page, int offset, size_t size, int flags); int tcp_sendmsg_locked(struct sock * sk, struct msghdr * msg, size_t size); $ Now lets go full circle and encode BTF for this a.o generated from source code generated from the original BTF info in that examples/tcp.o file: $ pahole -JV a.o | tail [465] FUNC_PROTO (anon) return=35 args=(392 hp, 393 skb, 5 header_len) [466] FUNC tcp_md5_hash_skb_data type_id=465 [467] FUNC_PROTO (anon) return=35 args=(392 hp, 394 key) [468] FUNC tcp_md5_hash_key type_id=467 [469] FUNC_PROTO (anon) return=0 args=(49 sk) [470] FUNC tcp_done type_id=469 [471] FUNC_PROTO (anon) return=35 args=(49 sk, 35 err) [472] FUNC tcp_abort type_id=471 [473] FUNC_PROTO (anon) return=0 args=(void) [474] FUNC tcp_init type_id=473 $ $ pfunct -F btf -V --prototypes --class=sock a.o | head void tcp_enter_memory_pressure(struct sock * sk); void tcp_leave_memory_pressure(struct sock * sk); void tcp_init_sock(struct sock * sk); void tcp_init_transfer(struct sock * sk, int bpf_op); int tcp_ioctl(struct sock * sk, int cmd, long unsigned int arg); struct sk_buff * sk_stream_alloc_skb(struct sock * sk, int size, gfp_t gfp, bool force_schedule); ssize_t do_tcp_sendpages(struct sock * sk, struct page * page, int offset, size_t size, int flags); int tcp_sendpage_locked(struct sock * sk, struct page * page, int offset, size_t size, int flags); int tcp_sendpage(struct sock * sk, struct page * page, int offset, size_t size, int flags); int tcp_sendmsg_locked(struct sock * sk, struct msghdr * msg, size_t size); $ Curious about the code generated by 'pfunct -F btf --compile examples/tcp.o? http://vger.kernel.org/~acme/pahole/pfunct-F-BTF--compile-examples-tcp.o.txt Cc: Alexei Starovoitov <ast@fb.com> Cc: Alexei Starovoitov <ast@kernel.org> Cc: Andrii Nakryiko <andrii.nakryiko@gmail.com> Cc: Andrii Nakryiko <andriin@fb.com> Cc: Jiri Olsa <jolsa@kernel.org> Cc: Yonghong Song <yhs@fb.com> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
f95fd85f7b
commit
ccf3eebfcd
30
btf_loader.c
30
btf_loader.c
|
@ -83,6 +83,25 @@ out_free_parameters:
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int create_new_function(struct btf_elf *btfe, struct btf_type *tp, uint64_t size, uint32_t id)
|
||||||
|
{
|
||||||
|
strings_t name = btf_elf__get32(btfe, &tp->name_off);
|
||||||
|
unsigned int type_id = btf_elf__get32(btfe, &tp->type);
|
||||||
|
struct function *func = tag__alloc(sizeof(*func));
|
||||||
|
|
||||||
|
if (func == NULL)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
// for BTF this is not really the type of the return of the function,
|
||||||
|
// but the prototype, the return type is the one in type_id
|
||||||
|
func->btf = 1;
|
||||||
|
func->proto.tag.tag = DW_TAG_subprogram;
|
||||||
|
func->proto.tag.type = type_id;
|
||||||
|
func->name = name;
|
||||||
|
cu__add_tag_with_id(btfe->priv, &func->proto.tag, id);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static struct base_type *base_type__new(strings_t name, uint32_t attrs,
|
static struct base_type *base_type__new(strings_t name, uint32_t attrs,
|
||||||
uint8_t float_type, size_t size)
|
uint8_t float_type, size_t size)
|
||||||
|
@ -482,15 +501,8 @@ static int btf_elf__load_types(struct btf_elf *btfe)
|
||||||
vlen = create_new_subroutine_type(btfe, ptr, vlen, type_ptr, type_index);
|
vlen = create_new_subroutine_type(btfe, ptr, vlen, type_ptr, type_index);
|
||||||
break;
|
break;
|
||||||
case BTF_KIND_FUNC:
|
case BTF_KIND_FUNC:
|
||||||
/* BTF_KIND_FUNC corresponding to a defined subprogram.
|
// BTF_KIND_FUNC corresponding to a defined subprogram.
|
||||||
* This is not really a type and it won't be referred by any other types
|
vlen = create_new_function(btfe, type_ptr, size, type_index);
|
||||||
* either. Since types cannot be skipped, let us replace it with
|
|
||||||
* a nullify_type_entry.
|
|
||||||
*
|
|
||||||
* No warning here since BTF_KIND_FUNC is a legal entry in BTF.
|
|
||||||
*/
|
|
||||||
cu__table_nullify_type_entry(btfe->priv, type_index);
|
|
||||||
vlen = 0;
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "BTF: idx: %d, off: %zd, Unknown kind %d\n",
|
fprintf(stderr, "BTF: idx: %d, off: %zd, Unknown kind %d\n",
|
||||||
|
|
|
@ -1120,6 +1120,13 @@ int ftype__has_parm_of_type(const struct ftype *ftype, const type_id_t target,
|
||||||
{
|
{
|
||||||
struct parameter *pos;
|
struct parameter *pos;
|
||||||
|
|
||||||
|
if (ftype->tag.tag == DW_TAG_subprogram) {
|
||||||
|
struct function *func = (struct function *)ftype;
|
||||||
|
|
||||||
|
if (func->btf)
|
||||||
|
ftype = tag__ftype(cu__type(cu, ftype->tag.type));
|
||||||
|
}
|
||||||
|
|
||||||
ftype__for_each_parameter(ftype, pos) {
|
ftype__for_each_parameter(ftype, pos) {
|
||||||
struct tag *type = cu__type(cu, pos->tag.type);
|
struct tag *type = cu__type(cu, pos->tag.type);
|
||||||
|
|
||||||
|
|
|
@ -763,7 +763,7 @@ void ftype__delete(struct ftype *ftype, struct cu *cu);
|
||||||
void ftype__add_parameter(struct ftype *ftype, struct parameter *parm);
|
void ftype__add_parameter(struct ftype *ftype, struct parameter *parm);
|
||||||
size_t ftype__fprintf(const struct ftype *ftype, const struct cu *cu,
|
size_t ftype__fprintf(const struct ftype *ftype, const struct cu *cu,
|
||||||
const char *name, const int inlined,
|
const char *name, const int inlined,
|
||||||
const int is_pointer, const int type_spacing,
|
const int is_pointer, const int type_spacing, bool is_prototype,
|
||||||
const struct conf_fprintf *conf, FILE *fp);
|
const struct conf_fprintf *conf, FILE *fp);
|
||||||
size_t ftype__fprintf_parms(const struct ftype *ftype,
|
size_t ftype__fprintf_parms(const struct ftype *ftype,
|
||||||
const struct cu *cu, int indent,
|
const struct cu *cu, int indent,
|
||||||
|
@ -785,6 +785,7 @@ struct function {
|
||||||
uint8_t accessibility:2; /* DW_ACCESS_{public,protected,private} */
|
uint8_t accessibility:2; /* DW_ACCESS_{public,protected,private} */
|
||||||
uint8_t virtuality:2; /* DW_VIRTUALITY_{none,virtual,pure_virtual} */
|
uint8_t virtuality:2; /* DW_VIRTUALITY_{none,virtual,pure_virtual} */
|
||||||
uint8_t declaration:1;
|
uint8_t declaration:1;
|
||||||
|
uint8_t btf:1;
|
||||||
int32_t vtable_entry;
|
int32_t vtable_entry;
|
||||||
struct list_head vtable_node;
|
struct list_head vtable_node;
|
||||||
/* fields used by tools */
|
/* fields used by tools */
|
||||||
|
|
|
@ -297,7 +297,7 @@ size_t typedef__fprintf(const struct tag *tag, const struct cu *cu,
|
||||||
return printed + ftype__fprintf(tag__ftype(tag_type), cu,
|
return printed + ftype__fprintf(tag__ftype(tag_type), cu,
|
||||||
type__name(type, cu),
|
type__name(type, cu),
|
||||||
0, is_pointer, 0,
|
0, is_pointer, 0,
|
||||||
pconf, fp);
|
true, pconf, fp);
|
||||||
case DW_TAG_class_type:
|
case DW_TAG_class_type:
|
||||||
case DW_TAG_structure_type: {
|
case DW_TAG_structure_type: {
|
||||||
struct type *ctype = tag__type(tag_type);
|
struct type *ctype = tag__type(tag_type);
|
||||||
|
@ -510,8 +510,7 @@ static const char *__tag__name(const struct tag *tag, const struct cu *cu,
|
||||||
FILE *bfp = fmemopen(bf, len, "w");
|
FILE *bfp = fmemopen(bf, len, "w");
|
||||||
|
|
||||||
if (bfp != NULL) {
|
if (bfp != NULL) {
|
||||||
ftype__fprintf(tag__ftype(tag), cu, NULL, 0, 0, 0,
|
ftype__fprintf(tag__ftype(tag), cu, NULL, 0, 0, 0, true, pconf, bfp);
|
||||||
pconf, bfp);
|
|
||||||
fclose(bfp);
|
fclose(bfp);
|
||||||
} else
|
} else
|
||||||
snprintf(bf, len, "<ERROR(%s): fmemopen failed!>",
|
snprintf(bf, len, "<ERROR(%s): fmemopen failed!>",
|
||||||
|
@ -700,7 +699,7 @@ next_type:
|
||||||
if (ptype->tag == DW_TAG_subroutine_type) {
|
if (ptype->tag == DW_TAG_subroutine_type) {
|
||||||
printed += ftype__fprintf(tag__ftype(ptype),
|
printed += ftype__fprintf(tag__ftype(ptype),
|
||||||
cu, name, 0, 1,
|
cu, name, 0, 1,
|
||||||
tconf.type_spacing,
|
tconf.type_spacing, true,
|
||||||
&tconf, fp);
|
&tconf, fp);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -724,7 +723,7 @@ print_default:
|
||||||
break;
|
break;
|
||||||
case DW_TAG_subroutine_type:
|
case DW_TAG_subroutine_type:
|
||||||
printed += ftype__fprintf(tag__ftype(type), cu, name, 0, 0,
|
printed += ftype__fprintf(tag__ftype(type), cu, name, 0, 0,
|
||||||
tconf.type_spacing, &tconf, fp);
|
tconf.type_spacing, true, &tconf, fp);
|
||||||
break;
|
break;
|
||||||
case DW_TAG_const_type: {
|
case DW_TAG_const_type: {
|
||||||
size_t const_printed = fprintf(fp, "%s ", "const");
|
size_t const_printed = fprintf(fp, "%s ", "const");
|
||||||
|
@ -998,7 +997,7 @@ const char *function__prototype(const struct function *func,
|
||||||
FILE *bfp = fmemopen(bf, len, "w");
|
FILE *bfp = fmemopen(bf, len, "w");
|
||||||
|
|
||||||
if (bfp != NULL) {
|
if (bfp != NULL) {
|
||||||
ftype__fprintf(&func->proto, cu, NULL, 0, 0, 0,
|
ftype__fprintf(&func->proto, cu, NULL, 0, 0, 0, true,
|
||||||
&conf_fprintf__defaults, bfp);
|
&conf_fprintf__defaults, bfp);
|
||||||
fclose(bfp);
|
fclose(bfp);
|
||||||
} else
|
} else
|
||||||
|
@ -1051,13 +1050,13 @@ size_t ftype__fprintf_parms(const struct ftype *ftype,
|
||||||
printed +=
|
printed +=
|
||||||
ftype__fprintf(tag__ftype(ptype),
|
ftype__fprintf(tag__ftype(ptype),
|
||||||
cu, name, 0, 1, 0,
|
cu, name, 0, 1, 0,
|
||||||
conf, fp);
|
true, conf, fp);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (type->tag == DW_TAG_subroutine_type) {
|
} else if (type->tag == DW_TAG_subroutine_type) {
|
||||||
printed += ftype__fprintf(tag__ftype(type), cu, name,
|
printed += ftype__fprintf(tag__ftype(type), cu, name,
|
||||||
0, 0, 0, conf, fp);
|
true, 0, 0, 0, conf, fp);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
stype = tag__name(type, cu, sbf, sizeof(sbf), conf);
|
stype = tag__name(type, cu, sbf, sizeof(sbf), conf);
|
||||||
|
@ -1190,7 +1189,7 @@ size_t lexblock__fprintf(const struct lexblock *block, const struct cu *cu,
|
||||||
|
|
||||||
size_t ftype__fprintf(const struct ftype *ftype, const struct cu *cu,
|
size_t ftype__fprintf(const struct ftype *ftype, const struct cu *cu,
|
||||||
const char *name, const int inlined,
|
const char *name, const int inlined,
|
||||||
const int is_pointer, int type_spacing,
|
const int is_pointer, int type_spacing, bool is_prototype,
|
||||||
const struct conf_fprintf *conf, FILE *fp)
|
const struct conf_fprintf *conf, FILE *fp)
|
||||||
{
|
{
|
||||||
struct tag *type = cu__type(cu, ftype->tag.type);
|
struct tag *type = cu__type(cu, ftype->tag.type);
|
||||||
|
@ -1199,11 +1198,9 @@ size_t ftype__fprintf(const struct ftype *ftype, const struct cu *cu,
|
||||||
size_t printed = fprintf(fp, "%s%-*s %s%s%s%s",
|
size_t printed = fprintf(fp, "%s%-*s %s%s%s%s",
|
||||||
inlined ? "inline " : "",
|
inlined ? "inline " : "",
|
||||||
type_spacing, stype,
|
type_spacing, stype,
|
||||||
ftype->tag.tag == DW_TAG_subroutine_type ?
|
is_prototype ? "(" : "",
|
||||||
"(" : "",
|
|
||||||
is_pointer ? "*" : "", name ?: "",
|
is_pointer ? "*" : "", name ?: "",
|
||||||
ftype->tag.tag == DW_TAG_subroutine_type ?
|
is_prototype ? ")" : "");
|
||||||
")" : "");
|
|
||||||
|
|
||||||
return printed + ftype__fprintf_parms(ftype, cu, 0, conf, fp);
|
return printed + ftype__fprintf_parms(ftype, cu, 0, conf, fp);
|
||||||
}
|
}
|
||||||
|
@ -1212,6 +1209,7 @@ static size_t function__fprintf(const struct tag *tag, const struct cu *cu,
|
||||||
const struct conf_fprintf *conf, FILE *fp)
|
const struct conf_fprintf *conf, FILE *fp)
|
||||||
{
|
{
|
||||||
struct function *func = tag__function(tag);
|
struct function *func = tag__function(tag);
|
||||||
|
struct ftype *ftype = func->btf ? tag__ftype(cu__type(cu, func->proto.tag.type)) : &func->proto;
|
||||||
size_t printed = 0;
|
size_t printed = 0;
|
||||||
bool inlined = !conf->strip_inline && function__declared_inline(func);
|
bool inlined = !conf->strip_inline && function__declared_inline(func);
|
||||||
|
|
||||||
|
@ -1219,8 +1217,8 @@ static size_t function__fprintf(const struct tag *tag, const struct cu *cu,
|
||||||
func->virtuality == DW_VIRTUALITY_pure_virtual)
|
func->virtuality == DW_VIRTUALITY_pure_virtual)
|
||||||
printed += fprintf(fp, "virtual ");
|
printed += fprintf(fp, "virtual ");
|
||||||
|
|
||||||
printed += ftype__fprintf(&func->proto, cu, function__name(func, cu),
|
printed += ftype__fprintf(ftype, cu, function__name(func, cu),
|
||||||
inlined, 0, 0, conf, fp);
|
inlined, 0, 0, false, conf, fp);
|
||||||
|
|
||||||
if (func->virtuality == DW_VIRTUALITY_pure_virtual)
|
if (func->virtuality == DW_VIRTUALITY_pure_virtual)
|
||||||
printed += fprintf(fp, " = 0");
|
printed += fprintf(fp, " = 0");
|
||||||
|
@ -1845,7 +1843,7 @@ size_t tag__fprintf(struct tag *tag, const struct cu *cu,
|
||||||
printed += __class__fprintf(tag__class(tag), cu, pconf, fp);
|
printed += __class__fprintf(tag__class(tag), cu, pconf, fp);
|
||||||
break;
|
break;
|
||||||
case DW_TAG_subroutine_type:
|
case DW_TAG_subroutine_type:
|
||||||
printed += ftype__fprintf(tag__ftype(tag), cu, NULL, false, false, 0, pconf, fp);
|
printed += ftype__fprintf(tag__ftype(tag), cu, NULL, false, false, 0, true, pconf, fp);
|
||||||
break;
|
break;
|
||||||
case DW_TAG_namespace:
|
case DW_TAG_namespace:
|
||||||
printed += namespace__fprintf(tag, cu, pconf, fp);
|
printed += namespace__fprintf(tag, cu, pconf, fp);
|
||||||
|
|
5
pfunct.c
5
pfunct.c
|
@ -323,7 +323,8 @@ static int function__emit_type_definitions(struct function *func,
|
||||||
struct cu *cu, FILE *fp)
|
struct cu *cu, FILE *fp)
|
||||||
{
|
{
|
||||||
struct parameter *pos;
|
struct parameter *pos;
|
||||||
struct tag *type = cu__type(cu, func->proto.tag.type);
|
struct ftype *proto = func->btf ? tag__ftype(cu__type(cu, func->proto.tag.type)) : &func->proto;
|
||||||
|
struct tag *type = cu__type(cu, proto->tag.type);
|
||||||
|
|
||||||
retry_return_type:
|
retry_return_type:
|
||||||
/* type == NULL means the return is void */
|
/* type == NULL means the return is void */
|
||||||
|
@ -340,7 +341,7 @@ retry_return_type:
|
||||||
type__emit(type, cu, NULL, NULL, fp);
|
type__emit(type, cu, NULL, NULL, fp);
|
||||||
}
|
}
|
||||||
do_parameters:
|
do_parameters:
|
||||||
function__for_each_parameter(func, pos) {
|
ftype__for_each_parameter(proto, pos) {
|
||||||
type = cu__type(cu, pos->tag.type);
|
type = cu__type(cu, pos->tag.type);
|
||||||
try_again:
|
try_again:
|
||||||
if (type == NULL)
|
if (type == NULL)
|
||||||
|
|
Loading…
Reference in New Issue