Check if the size is different than sizeof(int), which should be good
enough for now for both 64-bit and 32-bit targets.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
I.e. honour conf_fprintf.suppress_aligned_attribute, noticed with
btfdiff, as BTF doesn't carries the alignment attribute, so can't
regenerate it, we need to suppress it so as to compare the output of
DWARF with that of the equivalent BTF.
Fixes: b42d77b0bb ("fprintf: Print __attribute__((__aligned__(N))) for structs/classes")
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
To match the case when we really have just one dimension, so the
--flat-arrays should show for zero sized arrays, [], not [0]:
Noticed with btfdiff.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
We want to reach array__fprintf() from here, with the class_member
name, as __tag__name() isn't handling arrays properly.
I.e. to print an array when we have its name we can't use __tag__name().
This also stops printing 0 for zero sized arrays and trows away the
extra DW_TAG_const_type that comes with zero sized arrays, where we
have:
class_member type: DW_TAG_const_type 1
DW_TAG_const_type 1: DW_TAG_array_type 2
DW_TAG_array_type 2: 0 entries, type: DW_TAG_const_type 3
DW_TAG_const_type 3: real type of the zero sized array
For instance, after this patch we get a sane reconstruction of this
type:
$ pahole -C filename /home/acme/git/build/v5.0-rc2+/ipc/mqueue.o
struct filename {
const char * name; /* 0 8 */
const char * uptr; /* 8 8 */
int refcnt; /* 16 4 */
/* XXX 4 bytes hole, try to pack */
struct audit_names * aname; /* 24 8 */
const char iname[]; /* 32 0 */
/* size: 32, cachelines: 1, members: 5 */
/* sum members: 28, holes: 1, sum holes: 4 */
/* last cacheline: 32 bytes */
};
$
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
I.e. when we find that the last member has a bit_hole, i.e. it is part
of a bitfield, and the current field has a bitfield_size, i.e. it _also_
is part of a bitfield, the only explanation is that they were
artificially put in different base types, i.e. like in these fields
in the linux kernel 'struct task_struct', here reconstructed by pahole:
$ pahole -C task_struct ~/git/build/v5.1-rc2+/kernel/sched/core.o | grep :0 -B9 -A12
unsigned int personality; /* 1128 4 */
unsigned int sched_reset_on_fork:1; /* 1132: 0 4 */
unsigned int sched_contributes_to_load:1; /* 1132: 1 4 */
unsigned int sched_migrated:1; /* 1132: 2 4 */
unsigned int sched_remote_wakeup:1; /* 1132: 3 4 */
/* XXX 28 bits hole, try to pack */
/* Force alignment to the next boundary: */
unsigned int :0;
unsigned int in_execve:1; /* 1136: 0 4 */
unsigned int in_iowait:1; /* 1136: 1 4 */
unsigned int restore_sigmask:1; /* 1136: 2 4 */
unsigned int in_user_fault:1; /* 1136: 3 4 */
unsigned int no_cgroup_migration:1; /* 1136: 4 4 */
unsigned int use_memdelay:1; /* 1136: 5 4 */
/* XXX 26 bits hole, try to pack */
/* XXX 4 bytes hole, try to pack */
long unsigned int atomic_flags; /* 1144 8 */
$
This matches the original definition in the original kernel sources, and
further more, the following sequence proves that with this and DW_AT_alignment,
we can go full circle, i.e.:
1. from an object file reconstruct the source code for all the types that
appears in function signatures, if pointers, them they will be fully defined,
not just forward declared:
$ pfunct --compile=sched_change_group ~/git/build/v5.1-rc2+/kernel/sched/core.o | egrep -w 'sched_change_group|task_struct {' -B10 -A5
/* --- cacheline 3 boundary (192 bytes) --- */
struct fpu fpu __attribute__((__aligned__(64))); /* 192 4160 */
/* size: 4352, cachelines: 68, members: 21 */
/* sum members: 4316, holes: 2, sum holes: 32 */
/* sum bitfield members: 2 bits, bit holes: 1, sum bit holes: 30 bits */
/* forced alignments: 1, forced holes: 1, sum forced holes: 28 */
};
struct task_struct {
struct thread_info thread_info; /* 0 16 */
/* XXX last struct has 4 bytes of padding */
volatile long int state; /* 16 8 */
--
/* --- cacheline 104 boundary (6656 bytes) --- */
struct thread_struct thread __attribute__((__aligned__(64))); /* 6656 4352 */
/* size: 11008, cachelines: 172, members: 207 */
/* sum members: 10902, holes: 16, sum holes: 98 */
/* sum bitfield members: 10 bits, bit holes: 2, sum bit holes: 54 bits */
/* paddings: 3, sum paddings: 14 */
/* forced alignments: 6, forced holes: 1, sum forced holes: 40 */
};
void sched_change_group(struct task_struct * tsk, int type)
{
}
$
2. Build the regenerated skeleton function + its types:
$ pfunct --compile=sched_change_group ~/git/build/v5.1-rc2+/kernel/sched/core.o > sched_change_group.c
$ gcc -g -c sched_change_group.c
$ file sched_change_group.o
sched_change_group.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), with debug_info, not stripped
$
3. Now lets see if the original 'struct task_struct' printed by pahole, matches
the the output printed by pahole for the DWARF info generated for the regenerated
'struct task_struct' source code in sched_change_group.c:
$ pahole -C task_struct sched_change_group.o | tail
/* --- cacheline 104 boundary (6656 bytes) --- */
struct thread_struct thread __attribute__((__aligned__(64))); /* 6656 4352 */
/* size: 11008, cachelines: 172, members: 207 */
/* sum members: 10902, holes: 16, sum holes: 98 */
/* sum bitfield members: 10 bits, bit holes: 2, sum bit holes: 54 bits */
/* paddings: 3, sum paddings: 14 */
/* forced alignments: 6, forced holes: 1, sum forced holes: 40 */
};
$ pahole -C task_struct ~/git/build/v5.1-rc2+/kernel/sched/core.o | tail
/* --- cacheline 104 boundary (6656 bytes) --- */
struct thread_struct thread __attribute__((__aligned__(64))); /* 6656 4352 */
/* size: 11008, cachelines: 172, members: 207 */
/* sum members: 10902, holes: 16, sum holes: 98 */
/* sum bitfield members: 10 bits, bit holes: 2, sum bit holes: 54 bits */
/* paddings: 3, sum paddings: 14 */
/* forced alignments: 6, forced holes: 1, sum forced holes: 40 */
};
$
Furthermore:
$ pahole -C task_struct ~/git/build/v5.1-rc2+/kernel/sched/core.o > /tmp/original
$ pahole -C task_struct sched_change_group.o > /tmp/regenerated
$ diff -u /tmp/original /tmp/regenerated
$
So one of the most complex data structures in the Linux kernel seems to be under control,
and it uses zero sized unnamed bitfields and __attribute__((aligned(N))), a DWARF5 goodie,
time to go tag v1.13!
Cc: Alexei Starovoitov <ast@fb.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>
I.e. 'union {};', 'struct {};' members were always appearing as having
been removed, as we normally do lookup by member name, to find out if
its offset, size, type, etc changed.
For unnamed members, try a different heuristic, i.e. look for the nth
anonymous member, this way we're just trying to compare the first
unnamed member of, say, struct OLD with the first unnamed member of
struct NEW, etc.
For OLD == NEW, this works well, for OLD != NEW because some non
anonymous field got added, removed or moved around, ditto, and when the
number of unnamed fields gets decreased, then we can mix things up, and
compare the previously first in A with the previously first in B.
For the current intended use case of:
1) compile a .c file into a .o file with debugging info, say FILE.o
2) use 'pfunct --compile FILE.o > regenerated-FILE.c'
3) compile regenerated-FILE.c into regenerated-FILE.o with debugging info
4) codiff --struct FILE.o regenerated-FILE.o and find out if they match
This gets us moving forward as we'll spot differences with this algo.
For the future we can use a few more heuristics or stop using search by
name members, instead traversing both structs in tandem, spotting the
differences by comparing the fields that way.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
I.e. when we have two object files with debugging info, and one of them
jas just one CU, then compare all the CUs in the other file to this
unique CU.
Case in hand: encode BTF in a file, then the BTF info has everything in
just one "compile unit", so when looking at the types in the DWARF
originals, we should just compare its types to what is in the single BTF
"compile unit".
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Will be used when considering comparing multiple CU entries in a struct
cus to the sole compile unit in a second file, like when comparing the
types in a multi-CU DWARF file like vmlinux against the combined,
deduplicated entries in that vmlinux .BTF section.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
For the usual idiom to ask if a tag is a pointer, removing a bit of
DWARFism and shortening the operation.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
The pdwtags prints all tags, so call class__find_holes() for structs so
that we don't print BFAs.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
The last problem with 'pfunct --compile' at least for tcp.o:
Before:
$ pfunct --compile examples/tcp.o > tcp.pahole.c
$ gcc -c tcp.pahole.c -g
tcp.pahole.c:1808:48: error: unknown type name ‘u8const’; did you mean ‘const’?
inline void tcp_set_ca_state(struct sock * sk, u8const ca_state)
^~~~~~~
const
tcp.pahole.c:5346:56: error: unknown type name ‘intconst’; did you mean ‘const’?
inline void skb_set_tail_pointer(struct sk_buff * skb, intconst offset)
^~~~~~~~
const
tcp.pahole.c:5914:37: error: unknown type name ‘gfp_tconst’; did you mean ‘gfp_t’?
inline bool gfpflags_allow_blocking(gfp_tconst gfp_flags)
^~~~~~~~~~
gfp_t
tcp.pahole.c:5926:24: error: unknown type name ‘ktime_tconst’; did you mean ‘ktime_t’?
inline s64 ktime_to_ns(ktime_tconst kt)
^~~~~~~~~~~~
ktime_t
tcp.pahole.c:5939:54: warning: ‘struct timespec64const’ declared inside parameter list will not be visible outside of this definition or declaration
inline struct timespec timespec64_to_timespec(struct timespec64const ts64)
^~~~~~~~~~~~~~~
tcp.pahole.c:5939:70: error: parameter 1 (‘ts64’) has incomplete type
inline struct timespec timespec64_to_timespec(struct timespec64const ts64)
~~~~~~~~~~~~~~~~~~~~~~~^~~~
$
After:
$ pfunct --compile examples/tcp.o > tcp.pahole.c
$ gcc -c tcp.pahole.c -g
Because:
$ grep -A2 tcp_set_ca_state tcp.pahole.c
inline void tcp_set_ca_state(struct sock * sk, const u8 ca_state)
{
}
$
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
If we first reconstruct all the types needed for a typedef to then
reconstruct it, we may end up with the unnamed struct correctly
reconstructed and then this:
tcp.pahole.c:1987:17: warning: useless storage class specifier in empty declaration
typedef struct read_descriptor_t;
I.e.:
typedef struct {
size_t written; /* 0 8 */
size_t count; /* 8 8 */
union {
char * buf; /* 16 8 */
void * data; /* 16 8 */
} arg; /* 16 8 */
int error; /* 24 4 */
/* size: 32, cachelines: 1, members: 4 */
/* padding: 4 */
/* last cacheline: 32 bytes */
} read_descriptor_t;
typedef struct read_descriptor_t;
So special case it.
XXX
I'll revisit this, looks suboptimal, we manage to get this right
when reconstructing nameless struct typedefs found as the types for
members of structs or unions...
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
For instance, with the existing code we ended up with:
typedef __kernel_size_t size_t;
size_t tcp_opt_stats_get_size(void)
{
}
Which lacks unwinding what a __kernel_size_t is, i.e.
type__emit_definitions() was only emitting definitions for the members
of structs and unions, do it for typedefs too, and then we end up what
we need, which is:
typedef long unsigned int __kernel_ulong_t;
typedef __kernel_ulong_t __kernel_size_t;
typedef __kernel_size_t size_t;
size_t tcp_opt_stats_get_size(void)
{
}
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
We need to check if tag__type(type)->definition_emitted is set before
asking for that type to be emitted, otherwise we get type redefinition
errors when trying to compile the output from pahole --expand_types.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
I.e. --compile is similar to --expand_types but also makes sure the
function have empty function bodies, which ends up making at least gcc
to generate the DWARF info for the types referenced by the function
arguments and return types.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Previously we wouldn't expand types if the user didn't provide a
function name, make it expand the types for all function arguments if
no function name is provided.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Take:
int tcp_md5_hash_key(struct tcp_md5sig_pool *hp, const struct tcp_md5sig_key *key)
We were not generating the 'struct tcp_md5sig_key' forward decl or
struct definition, stopping at 'const', which made this uncompilable.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Not just for the function arguments, so that we are able to get
something closer to buildable.
So far we got it buildable when the return types were reconstructed
because they also appeared in one of the function arguments or
in structs used by them.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Just add it to the current lexblock.
This removes the warnings that started to appear with the fix in the
previous patch, i.e. these:
die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3aefafe> not handled!
die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3aeff8a> not handled!
die__process_inline_expansion: DW_TAG_label (0xa) @ <0x3af02e0> not handled!
Reported-by: Jiri Olsa <jolsa@kernel.org>
Cc: Andrii Nakryiko <andriin@fb.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Yonghong Song <yhs@fb.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
We need to fix some bugs introduced recently, till then, disable steps
that try to demote the base type of bitfields and those that
move/combine bitfields to save space.
We'll revisit those later, bringing them back to the reorg codebase.
Acked-by: Andrii Nakryiko <andriin@fb.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Mark Wielaard <mark@klomp.org>
Cc: Yonghong Song <yhs@fb.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>#
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
So that we can use it in things like btfdiff.
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Andrii Nakryiko <andriin@fb.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Mark Wielaard <mark@klomp.org>
Cc: Yonghong Song <yhs@fb.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
DWARF got a DW_AT_alignment as described in:
http://dwarfstd.org/ShowIssue.php?issue=140528.1
This appeared first in DWARF5:
http://dwarfstd.org/doc/DWARF5.pdf
In:
----------------------------------------------------------------------
Chapter 2.
General Description
2.24 Alignment
A debugging information entry may have a DW_AT_alignment attribute whose
value of class constant is a positive, non-zero, integer describing the
alignment of the entity.
For example, an alignment attribute whose value is 8 indicates that the
entity to which it applies occurs at an address that is a multiple of
eight (not a multiple of 8 or 256)
----------------------------------------------------------------------
Use it on a struct present in the running kernel, i.e. not specifying
which ELF file to look for the DWARF info to use:
$ pahole -C inet_timewait_death_row
struct inet_timewait_death_row {
atomic_t tw_count; /* 0 4 */
/* XXX 60 bytes hole, try to pack */
/* --- cacheline 1 boundary (64 bytes) --- */
struct inet_hashinfo * hashinfo __attribute__((__aligned__(64)); /* 64 8 */
int sysctl_max_tw_buckets; /* 72 4 */
/* size: 128, cachelines: 2, members: 3 */
/* sum members: 16, holes: 1, sum holes: 60 */
/* padding: 52 */
};
$
Now to do some tweaking to get that "__attribute..." part nicely, hum,
aligned in the pahole output :-)
BTW: the original struct is in the kernel sources:
include/net/netns/ipv4.h
struct inet_timewait_death_row {
atomic_t tw_count;
struct inet_hashinfo *hashinfo ____cacheline_aligned_in_smp;
int sysctl_max_tw_buckets;
};
Reported-by: Mark Wielaard <mark@klomp.org>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Andrii Nakryiko <andriin@fb.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Yonghong Song <yhs@fb.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Instead of relying on error-prone adjustment of bit/byte holes, use
class__find_holes() to re-calculate them after members are moved around.
As part of that change, fix bug with not adjusting bit_offset, when
changing byte_offset.
Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Yonghong Song <yhs@fb.com>
Cc: dwarves@vger.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
pahole --reorganize heavily depends on member's bit_hole and hole fields
to denote bit/byte holes *after* member. Previous commit "dwarves: use
bit sizes and bit/byte hole info in __class__fprintf" changed its
meaning to bit/byte hole *before* member to accomodate possible bit/byte
holes at the beginning of a struct. This change broke reorganization
algorithm, though, which is quite involved and isn't trivially
modifiable to accomodate new semantics.
This patch reverts the meaning of bit_hole and hole, but also introduces
per class pre_bit_hole/pre_hole to record initial bit/byte hole of a
struct. This allows to fix reorg code more easily and still handle
initial holes cases, if at the expense of being not as elegant.
Committer testing:
$ time pahole -F btf --packable vmlinux | sort -nr -k4 | head
bts_ctx 12288 8192 4096
swsusp_info 4096 432 3664
vc_data 792 496 296
pci_dev 2488 2320 168
rcu_state 3392 3240 152
ptr_ring 192 40 152
xdp_sock 960 840 120
zone 1664 1552 112
rcu_data 576 472 104
rcu_node 576 480 96
real 0m0.038s
user 0m0.029s
sys 0m0.017s
$
Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Reported-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Yonghong Song <yhs@fb.com>
Cc: dwarves@vger.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Counting field sizes only in bits causes confusion and lots of differing
output, when compared to previous logic. This commit changes logic so
that it counts bit size of bitfield fields separately from byte size of
non-bitfield fields. In the end, if there were bit holes, this bit size
is emitted explicitly. This makes output for struct/unions not using
bitfields identical, while also preserving correctness (and data
completeness) for cases with bitfields and bit holes.
Example (-before/+after):
struct cfg80211_pmsr_request_peer {
u8 addr[6]; /* 0 6 */
/* XXX 2 bytes hole, try to pack */
struct cfg80211_chan_def chandef; /* 8 24 */
/* XXX last struct has 4 bytes of padding */
u8 report_ap_tsf:1; /* 32: 0 1 */
/* XXX 7 bits hole, try to pack */
/* XXX 3 bytes hole, try to pack */
struct cfg80211_pmsr_ftm_request_peer ftm; /* 36 12 */
/* XXX last struct has 1 byte of padding */
/* size: 48, cachelines: 1, members: 4 */
- /* sum members: 43, holes: 2, sum holes: 5 */
- /* bit holes: 1, sum bit holes: 7 bits */
+ /* sum members: 42, holes: 2, sum holes: 5 */
+ /* sum bitfield members: 1 bits, bit holes: 1, sum bit holes: 7 bits */
/* paddings: 2, sum paddings: 5 */
/* last cacheline: 48 bytes */
};
For cases where there is only byte or bit hole, we still emit total byte and
bit sizes of all members as to not mislead user:
struct sched_dl_entity {
... <snip ...
unsigned int dl_non_contending:1; /* 84: 3 4 */
unsigned int dl_overrun:1; /* 84: 4 4 */
/* XXX 27 bits hole, try to pack */
struct hrtimer dl_timer; /* 88 64 */
/* XXX last struct has 5 bytes of padding */
/* --- cacheline 2 boundary (128 bytes) was 24 bytes ago --- */
struct hrtimer inactive_timer; /* 152 64 */
/* XXX last struct has 5 bytes of padding */
/* size: 216, cachelines: 4, members: 16 */
- /* bit holes: 1, sum bit holes: 27 bits */
+ /* sum members: 212 */
+ /* sum bitfield members: 5 bits, bit holes: 1, sum bit holes: 27 bits */
/* paddings: 2, sum paddings: 10 */
/* last cacheline: 24 bytes */
};
For structs with tightly packed bitfield, we emit total number of bits and also
convert them to bytes. E.g., for struct sock output :
struct sock {
... <snip ...
/* size: 720, cachelines: 12, members: 84 */
- /* sum members: 712, holes: 4, sum holes: 8 */
+ /* sum members: 707, holes: 4, sum holes: 8 */
+ /* sum bitfield members: 40 bits (5 bytes) */
/* paddings: 1, sum paddings: 4 */
/* last cacheline: 16 bytes */
};
Suggested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Yonghong Song <yhs@fb.com>
Cc: dwarves@vger.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This patch records for each CU whether it's in little-endian or
big-endian data format. This flag will be used in subsequent commits to
adjust bit offsets where necessary, to make them uniform across
endianness. This patch doesn't have any effect on pahole's output.
Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Mark Wielaard <mark@klomp.org>
Cc: Martin KaFai Lau <kafai@fb.com>
Cc: Yonghong Song <yhs@fb.com>
Cc: dwarves@vger.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This patch changes __class__fprintf to rely on bit_size/bitfield_size,
calculated in corresponding loaders, instead of trying to guess correct
sizes on its own.
Bit and byte hole information is now stored in current field member and
stores information about holes *before* field. Previously hole
information was stored in previous field and was meant to represent
"holes after a field". Such approach makes it hard to report bit holes
at the very beginning of the struct:
struct s {
int:4; /* this is bit hole, not a field in DWARF/BTF */
int x:8;
};
With this change and previous bitfield calculation/fixup logic fixes,
there are no more discrepancies between DWARF/BTF for allyesconfig
kernel. There are also no more BRAIN FART ALERTS!
Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Mark Wielaard <mark@klomp.org>
Cc: Martin KaFai Lau <kafai@fb.com>
Cc: Yonghong Song <yhs@fb.com>
Cc: dwarves@vger.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>