diff options
| author | 2019-11-05 11:30:51 -0300 | |
|---|---|---|
| committer | 2019-11-05 12:04:23 -0300 | |
| commit | ccf3eebfcd9cc1defd8e5f347fee8631e33383a3 (patch) | |
| tree | ca41f75e3b29e184a78f81d35acd6e29e5b1e611 | |
| parent | f95fd85f7b539a6c5b9748e7a33ff7620ed4406e (diff) | |
| download | pahole-ccf3eebfcd9cc1defd8e5f347fee8631e33383a3.tar.gz | |
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>
| -rw-r--r-- | btf_loader.c | 30 | ||||
| -rw-r--r-- | dwarves.c | 7 | ||||
| -rw-r--r-- | dwarves.h | 3 | ||||
| -rw-r--r-- | dwarves_fprintf.c | 30 | ||||
| -rw-r--r-- | pfunct.c | 5 |
5 files changed, 47 insertions, 28 deletions
diff --git a/btf_loader.c b/btf_loader.c index 4ad0c3f..e61e4d0 100644 --- a/btf_loader.c +++ b/btf_loader.c @@ -83,6 +83,25 @@ out_free_parameters: 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, 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); break; case BTF_KIND_FUNC: - /* BTF_KIND_FUNC corresponding to a defined subprogram. - * This is not really a type and it won't be referred by any other types - * 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; + // BTF_KIND_FUNC corresponding to a defined subprogram. + vlen = create_new_function(btfe, type_ptr, size, type_index); break; default: 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; + 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) { 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); size_t ftype__fprintf(const struct ftype *ftype, const struct cu *cu, 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); size_t ftype__fprintf_parms(const struct ftype *ftype, const struct cu *cu, int indent, @@ -785,6 +785,7 @@ struct function { uint8_t accessibility:2; /* DW_ACCESS_{public,protected,private} */ uint8_t virtuality:2; /* DW_VIRTUALITY_{none,virtual,pure_virtual} */ uint8_t declaration:1; + uint8_t btf:1; int32_t vtable_entry; struct list_head vtable_node; /* fields used by tools */ diff --git a/dwarves_fprintf.c b/dwarves_fprintf.c index 31ea338..62ac292 100644 --- a/dwarves_fprintf.c +++ b/dwarves_fprintf.c @@ -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, type__name(type, cu), 0, is_pointer, 0, - pconf, fp); + true, pconf, fp); case DW_TAG_class_type: case DW_TAG_structure_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"); if (bfp != NULL) { - ftype__fprintf(tag__ftype(tag), cu, NULL, 0, 0, 0, - pconf, bfp); + ftype__fprintf(tag__ftype(tag), cu, NULL, 0, 0, 0, true, pconf, bfp); fclose(bfp); } else snprintf(bf, len, "<ERROR(%s): fmemopen failed!>", @@ -700,7 +699,7 @@ next_type: if (ptype->tag == DW_TAG_subroutine_type) { printed += ftype__fprintf(tag__ftype(ptype), cu, name, 0, 1, - tconf.type_spacing, + tconf.type_spacing, true, &tconf, fp); break; } @@ -724,7 +723,7 @@ print_default: break; case DW_TAG_subroutine_type: printed += ftype__fprintf(tag__ftype(type), cu, name, 0, 0, - tconf.type_spacing, &tconf, fp); + tconf.type_spacing, true, &tconf, fp); break; case DW_TAG_const_type: { 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"); 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); fclose(bfp); } else @@ -1051,13 +1050,13 @@ size_t ftype__fprintf_parms(const struct ftype *ftype, printed += ftype__fprintf(tag__ftype(ptype), cu, name, 0, 1, 0, - conf, fp); + true, conf, fp); continue; } } } else if (type->tag == DW_TAG_subroutine_type) { printed += ftype__fprintf(tag__ftype(type), cu, name, - 0, 0, 0, conf, fp); + true, 0, 0, 0, conf, fp); continue; } 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, 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) { 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", inlined ? "inline " : "", type_spacing, stype, - ftype->tag.tag == DW_TAG_subroutine_type ? - "(" : "", + is_prototype ? "(" : "", is_pointer ? "*" : "", name ?: "", - ftype->tag.tag == DW_TAG_subroutine_type ? - ")" : ""); + is_prototype ? ")" : ""); 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) { 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; 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) printed += fprintf(fp, "virtual "); - printed += ftype__fprintf(&func->proto, cu, function__name(func, cu), - inlined, 0, 0, conf, fp); + printed += ftype__fprintf(ftype, cu, function__name(func, cu), + inlined, 0, 0, false, conf, fp); if (func->virtuality == DW_VIRTUALITY_pure_virtual) 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); break; 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; case DW_TAG_namespace: printed += namespace__fprintf(tag, cu, pconf, fp); @@ -323,7 +323,8 @@ static int function__emit_type_definitions(struct function *func, struct cu *cu, FILE *fp) { 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: /* type == NULL means the return is void */ @@ -340,7 +341,7 @@ retry_return_type: type__emit(type, cu, NULL, NULL, fp); } do_parameters: - function__for_each_parameter(func, pos) { + ftype__for_each_parameter(proto, pos) { type = cu__type(cu, pos->tag.type); try_again: if (type == NULL) |
