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:
Arnaldo Carvalho de Melo 2019-11-05 11:30:51 -03:00
parent f95fd85f7b
commit ccf3eebfcd
5 changed files with 47 additions and 28 deletions

View File

@ -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",

View File

@ -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);

View File

@ -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 */

View File

@ -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);

View File

@ -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)