aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArnaldo Carvalho de Melo <acme@redhat.com>2019-11-05 11:30:51 -0300
committerArnaldo Carvalho de Melo <acme@redhat.com>2019-11-05 12:04:23 -0300
commitccf3eebfcd9cc1defd8e5f347fee8631e33383a3 (patch)
treeca41f75e3b29e184a78f81d35acd6e29e5b1e611
parentf95fd85f7b539a6c5b9748e7a33ff7620ed4406e (diff)
downloadpahole-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.c30
-rw-r--r--dwarves.c7
-rw-r--r--dwarves.h3
-rw-r--r--dwarves_fprintf.c30
-rw-r--r--pfunct.c5
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",
diff --git a/dwarves.c b/dwarves.c
index 76bdf61..846c65e 100644
--- a/dwarves.c
+++ b/dwarves.c
@@ -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);
diff --git a/dwarves.h b/dwarves.h
index b53bdfc..cd44381 100644
--- a/dwarves.h
+++ b/dwarves.h
@@ -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);
diff --git a/pfunct.c b/pfunct.c
index 1a6c943..f4ac200 100644
--- a/pfunct.c
+++ b/pfunct.c
@@ -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)