2009-04-19 18:48:51 +02:00
|
|
|
/*
|
2019-01-15 18:28:24 +01:00
|
|
|
SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
|
2009-04-19 18:48:51 +02:00
|
|
|
Copyright (C) 2006 Mandriva Conectiva S.A.
|
|
|
|
Copyright (C) 2006 Arnaldo Carvalho de Melo <acme@mandriva.com>
|
|
|
|
Copyright (C) 2007..2009 Red Hat Inc.
|
|
|
|
Copyright (C) 2007..2009 Arnaldo Carvalho de Melo <acme@redhat.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <dwarf.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <stdio.h>
|
2021-08-13 16:45:45 +02:00
|
|
|
#include <stdlib.h>
|
2009-04-19 18:48:51 +02:00
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
2013-03-19 21:55:01 +01:00
|
|
|
#include <inttypes.h>
|
2014-11-19 22:06:41 +01:00
|
|
|
#include <elfutils/version.h>
|
2021-08-13 16:45:45 +02:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
2009-04-19 18:48:51 +02:00
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
#include "dwarves.h"
|
|
|
|
|
|
|
|
static const char *dwarf_tag_names[] = {
|
|
|
|
[DW_TAG_array_type] = "array_type",
|
|
|
|
[DW_TAG_class_type] = "class_type",
|
|
|
|
[DW_TAG_entry_point] = "entry_point",
|
|
|
|
[DW_TAG_enumeration_type] = "enumeration_type",
|
|
|
|
[DW_TAG_formal_parameter] = "formal_parameter",
|
|
|
|
[DW_TAG_imported_declaration] = "imported_declaration",
|
|
|
|
[DW_TAG_label] = "label",
|
|
|
|
[DW_TAG_lexical_block] = "lexical_block",
|
|
|
|
[DW_TAG_member] = "member",
|
|
|
|
[DW_TAG_pointer_type] = "pointer_type",
|
|
|
|
[DW_TAG_reference_type] = "reference_type",
|
|
|
|
[DW_TAG_compile_unit] = "compile_unit",
|
|
|
|
[DW_TAG_string_type] = "string_type",
|
|
|
|
[DW_TAG_structure_type] = "structure_type",
|
|
|
|
[DW_TAG_subroutine_type] = "subroutine_type",
|
|
|
|
[DW_TAG_typedef] = "typedef",
|
|
|
|
[DW_TAG_union_type] = "union_type",
|
|
|
|
[DW_TAG_unspecified_parameters] = "unspecified_parameters",
|
|
|
|
[DW_TAG_variant] = "variant",
|
|
|
|
[DW_TAG_common_block] = "common_block",
|
|
|
|
[DW_TAG_common_inclusion] = "common_inclusion",
|
|
|
|
[DW_TAG_inheritance] = "inheritance",
|
|
|
|
[DW_TAG_inlined_subroutine] = "inlined_subroutine",
|
|
|
|
[DW_TAG_module] = "module",
|
|
|
|
[DW_TAG_ptr_to_member_type] = "ptr_to_member_type",
|
|
|
|
[DW_TAG_set_type] = "set_type",
|
|
|
|
[DW_TAG_subrange_type] = "subrange_type",
|
|
|
|
[DW_TAG_with_stmt] = "with_stmt",
|
|
|
|
[DW_TAG_access_declaration] = "access_declaration",
|
|
|
|
[DW_TAG_base_type] = "base_type",
|
|
|
|
[DW_TAG_catch_block] = "catch_block",
|
|
|
|
[DW_TAG_const_type] = "const_type",
|
|
|
|
[DW_TAG_constant] = "constant",
|
|
|
|
[DW_TAG_enumerator] = "enumerator",
|
|
|
|
[DW_TAG_file_type] = "file_type",
|
|
|
|
[DW_TAG_friend] = "friend",
|
|
|
|
[DW_TAG_namelist] = "namelist",
|
|
|
|
[DW_TAG_namelist_item] = "namelist_item",
|
|
|
|
[DW_TAG_packed_type] = "packed_type",
|
|
|
|
[DW_TAG_subprogram] = "subprogram",
|
|
|
|
[DW_TAG_template_type_parameter] = "template_type_parameter",
|
|
|
|
[DW_TAG_template_value_parameter] = "template_value_parameter",
|
|
|
|
[DW_TAG_thrown_type] = "thrown_type",
|
|
|
|
[DW_TAG_try_block] = "try_block",
|
|
|
|
[DW_TAG_variant_part] = "variant_part",
|
|
|
|
[DW_TAG_variable] = "variable",
|
|
|
|
[DW_TAG_volatile_type] = "volatile_type",
|
|
|
|
[DW_TAG_dwarf_procedure] = "dwarf_procedure",
|
|
|
|
[DW_TAG_restrict_type] = "restrict_type",
|
|
|
|
[DW_TAG_interface_type] = "interface_type",
|
|
|
|
[DW_TAG_namespace] = "namespace",
|
|
|
|
[DW_TAG_imported_module] = "imported_module",
|
|
|
|
[DW_TAG_unspecified_type] = "unspecified_type",
|
|
|
|
[DW_TAG_partial_unit] = "partial_unit",
|
|
|
|
[DW_TAG_imported_unit] = "imported_unit",
|
|
|
|
[DW_TAG_condition] = "condition",
|
|
|
|
[DW_TAG_shared_type] = "shared_type",
|
2011-03-17 19:33:28 +01:00
|
|
|
#ifdef STB_GNU_UNIQUE
|
2010-11-21 01:23:14 +01:00
|
|
|
[DW_TAG_type_unit] = "type_unit",
|
|
|
|
[DW_TAG_rvalue_reference_type] = "rvalue_reference_type",
|
2011-03-17 19:33:28 +01:00
|
|
|
#endif
|
2010-11-21 01:23:14 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
static const char *dwarf_gnu_tag_names[] = {
|
|
|
|
[DW_TAG_MIPS_loop - DW_TAG_MIPS_loop] = "MIPS_loop",
|
|
|
|
[DW_TAG_format_label - DW_TAG_MIPS_loop] = "format_label",
|
|
|
|
[DW_TAG_function_template - DW_TAG_MIPS_loop] = "function_template",
|
|
|
|
[DW_TAG_class_template - DW_TAG_MIPS_loop] = "class_template",
|
2011-03-17 19:33:28 +01:00
|
|
|
#ifdef STB_GNU_UNIQUE
|
2014-11-19 22:07:50 +01:00
|
|
|
[DW_TAG_GNU_BINCL - DW_TAG_MIPS_loop] = "GNU_BINCL",
|
|
|
|
[DW_TAG_GNU_EINCL - DW_TAG_MIPS_loop] = "GNU_EINCL",
|
|
|
|
[DW_TAG_GNU_template_template_param - DW_TAG_MIPS_loop] = "GNU_template_template_param",
|
|
|
|
[DW_TAG_GNU_template_parameter_pack - DW_TAG_MIPS_loop] = "GNU_template_parameter_pack",
|
|
|
|
[DW_TAG_GNU_formal_parameter_pack - DW_TAG_MIPS_loop] = "GNU_formal_parameter_pack",
|
2011-03-17 19:33:28 +01:00
|
|
|
#endif
|
2014-11-19 22:06:41 +01:00
|
|
|
#if _ELFUTILS_PREREQ(0, 153)
|
|
|
|
[DW_TAG_GNU_call_site - DW_TAG_MIPS_loop] = "GNU_call_site",
|
|
|
|
[DW_TAG_GNU_call_site_parameter - DW_TAG_MIPS_loop] = "GNU_call_site_parameter",
|
|
|
|
#endif
|
2009-04-19 18:48:51 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
const char *dwarf_tag_name(const uint32_t tag)
|
|
|
|
{
|
2011-03-17 19:33:28 +01:00
|
|
|
if (tag >= DW_TAG_array_type && tag <=
|
|
|
|
#ifdef STB_GNU_UNIQUE
|
|
|
|
DW_TAG_rvalue_reference_type
|
|
|
|
#else
|
|
|
|
DW_TAG_shared_type
|
|
|
|
#endif
|
|
|
|
)
|
2009-04-19 18:48:51 +02:00
|
|
|
return dwarf_tag_names[tag];
|
2011-03-17 19:33:28 +01:00
|
|
|
else if (tag >= DW_TAG_MIPS_loop && tag <=
|
2014-11-19 22:06:41 +01:00
|
|
|
#if _ELFUTILS_PREREQ(0, 153)
|
|
|
|
DW_TAG_GNU_call_site_parameter
|
|
|
|
#elif STB_GNU_UNIQUE
|
2011-03-17 19:33:28 +01:00
|
|
|
DW_TAG_GNU_formal_parameter_pack
|
|
|
|
#else
|
|
|
|
DW_TAG_class_template
|
|
|
|
#endif
|
|
|
|
)
|
2010-11-21 01:23:14 +01:00
|
|
|
return dwarf_gnu_tag_names[tag - DW_TAG_MIPS_loop];
|
2009-04-19 18:48:51 +02:00
|
|
|
return "INVALID";
|
|
|
|
}
|
|
|
|
|
2021-10-28 14:27:08 +02:00
|
|
|
static struct conf_fprintf conf_fprintf__defaults = {
|
2009-04-19 18:48:51 +02:00
|
|
|
.name_spacing = 23,
|
|
|
|
.type_spacing = 26,
|
|
|
|
.emit_stats = 1,
|
|
|
|
};
|
|
|
|
|
2020-07-15 14:22:19 +02:00
|
|
|
const char tabs[] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
|
2009-04-19 18:48:51 +02:00
|
|
|
|
|
|
|
|
2021-10-28 14:27:08 +02:00
|
|
|
size_t tag__nr_cachelines(const struct conf_fprintf *conf, const struct tag *tag, const struct cu *cu)
|
2009-04-19 18:48:51 +02:00
|
|
|
{
|
2021-10-28 14:27:08 +02:00
|
|
|
return (tag__size(tag, cu) + conf->cacheline_size - 1) / conf->cacheline_size;
|
2009-04-19 18:48:51 +02:00
|
|
|
}
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
static const char *tag__accessibility(const struct tag *tag)
|
2009-04-19 18:48:51 +02:00
|
|
|
{
|
|
|
|
int a;
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
switch (tag->tag) {
|
2009-04-19 18:48:51 +02:00
|
|
|
case DW_TAG_inheritance:
|
|
|
|
case DW_TAG_member:
|
2012-08-17 23:47:15 +02:00
|
|
|
a = tag__class_member(tag)->accessibility;
|
2009-04-19 18:48:51 +02:00
|
|
|
break;
|
|
|
|
case DW_TAG_subprogram:
|
2012-08-17 23:47:15 +02:00
|
|
|
a = tag__function(tag)->accessibility;
|
2009-04-19 18:48:51 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (a) {
|
|
|
|
case DW_ACCESS_public: return "public";
|
|
|
|
case DW_ACCESS_private: return "private";
|
|
|
|
case DW_ACCESS_protected: return "protected";
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
pahole: Use 32-bit integers for type ID iterations within CU
Existing code base assumes that single CU doesn't have more than 65535
types per each CU, which might be a reasonable assumption for DWARF
data. With BTF, though, all we get is single, potentially huge, CU which
can easily have more than 65k types. For example, this is the case for
allyesconfig version of Linux kernel, which has >200k types.
Due to this assumption, libdwarves and other parts of pahole are using
16-bit counters to iterate over entities within CU. This can cause
infinite loops when iterating BTF data, if there are more than 65535
types. This patch changes non-public variables to use 32-bit integers,
where appropriate.
This still leads to invalid reported data when using BTF loader (due to using
(X & 0xFFFF) type ID, instead of X, when X > 65535) and loading huge files,
but at least it's not stuck in an infinite loop anymore.
Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Reported-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Yonghong Song <yhs@fb.com>
Cc: bpf@vger.kernel.org
Cc: dwarves@vger.kernel.org
[ Removed non type ID conversions, for instance for the kind of tag, like in type->namespace.tag.tag, that can remain a uint16_t ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-03-07 01:23:21 +01:00
|
|
|
static size_t __tag__id_not_found_snprintf(char *bf, size_t len, uint32_t id,
|
2009-09-11 20:10:43 +02:00
|
|
|
const char *fn, int line)
|
2009-04-19 18:48:51 +02:00
|
|
|
{
|
2009-09-11 20:10:43 +02:00
|
|
|
return snprintf(bf, len, "<ERROR(%s:%d): %#llx not found!>", fn, line,
|
2009-04-19 18:48:51 +02:00
|
|
|
(unsigned long long)id);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define tag__id_not_found_snprintf(bf, len, id) \
|
2009-09-11 20:10:43 +02:00
|
|
|
__tag__id_not_found_snprintf(bf, len, id, __func__, __LINE__)
|
2009-04-19 18:48:51 +02:00
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
size_t tag__fprintf_decl_info(const struct tag *tag,
|
2009-04-19 18:48:51 +02:00
|
|
|
const struct cu *cu, FILE *fp)
|
|
|
|
{
|
2012-08-17 23:47:15 +02:00
|
|
|
return fprintf(fp, "/* <%llx> %s:%u */\n", tag__orig_id(tag, cu),
|
|
|
|
tag__decl_file(tag, cu), tag__decl_line(tag, cu));
|
2009-04-19 18:48:51 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-11-19 14:32:00 +01:00
|
|
|
static size_t __class__fprintf(struct class *class, const struct cu *cu,
|
|
|
|
const struct conf_fprintf *conf, FILE *fp);
|
2009-04-19 18:48:51 +02:00
|
|
|
static size_t type__fprintf(struct tag *type, const struct cu *cu,
|
|
|
|
const char *name, const struct conf_fprintf *conf,
|
|
|
|
FILE *fp);
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
static size_t array_type__fprintf(const struct tag *tag,
|
2009-04-19 18:48:51 +02:00
|
|
|
const struct cu *cu, const char *name,
|
|
|
|
const struct conf_fprintf *conf,
|
|
|
|
FILE *fp)
|
|
|
|
{
|
2012-08-17 23:47:15 +02:00
|
|
|
struct array_type *at = tag__array_type(tag);
|
|
|
|
struct tag *type = cu__type(cu, tag->type);
|
2009-04-19 18:48:51 +02:00
|
|
|
size_t printed;
|
|
|
|
unsigned long long flat_dimensions = 0;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (type == NULL)
|
2012-08-17 23:47:15 +02:00
|
|
|
return tag__id_not_found_fprintf(fp, tag->type);
|
2009-04-19 18:48:51 +02:00
|
|
|
|
fprintf: Print "const" for class members more early, in type__fprintf()
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>
2019-04-09 23:07:24 +02:00
|
|
|
/* Zero sized arrays? */
|
fprintf: Fixup multi-dimensional zero sized arrays const handling
Before:
$ pahole -C piix_map_db /home/acme/git/build/v5.1-rc4+/drivers/ata/ata_piix.o
struct piix_map_db {
const u32 mask; /* 0 4 */
const u16 port_enable; /* 4 2 */
/* XXX 2 bytes hole, try to pack */
const const int map[][4]; /* 8 0 */
/* size: 8, cachelines: 1, members: 3 */
/* sum members: 6, holes: 1, sum holes: 2 */
/* last cacheline: 8 bytes */
};
$
After:
$ pahole -C piix_map_db /home/acme/git/build/v5.1-rc4+/drivers/ata/ata_piix.o
struct piix_map_db {
const u32 mask; /* 0 4 */
const u16 port_enable; /* 4 2 */
/* XXX 2 bytes hole, try to pack */
const int map[][4]; /* 8 0 */
/* size: 8, cachelines: 1, members: 3 */
/* sum members: 6, holes: 1, sum holes: 2 */
/* last cacheline: 8 bytes */
};
$
The DWARF tag sequence:
<2><17e50>: Abbrev Number: 12 (DW_TAG_member)
<17e51> DW_AT_name : map
<17e55> DW_AT_decl_file : 1
<17e56> DW_AT_decl_line : 160
<17e57> DW_AT_decl_column : 12
<17e58> DW_AT_type : <0x17e78>
<17e5c> DW_AT_data_member_location: 8
<1><17e78>: Abbrev Number: 15 (DW_TAG_const_type)
<17e79> DW_AT_type : <0x17e63>
<1><17e63>: Abbrev Number: 11 (DW_TAG_array_type)
<17e64> DW_AT_type : <0xd8>
<1><d8>: Abbrev Number: 15 (DW_TAG_const_type)
<d9> DW_AT_type : <0xd1>
<1><d1>: Abbrev Number: 120 (DW_TAG_base_type)
<d2> DW_AT_byte_size : 4
<d3> DW_AT_encoding : 5 (signed)
<d4> DW_AT_name : int
const -> array -> const -> int
So just make the check be at least one dimension, if the number of
elements is zero, then drop the double const.
With this btfdiff for the allyesconfig ppc64 reference kernel we're
using is again clean.
$ pahole -F btf --sizes examples/vmlinux-aarch64 | wc -l
51023
$
> 50K types with output from BTF and DWARF matching.
Fixes: ccd67bdb205b ("fprintf: Print "const" for class members more early, in type__fprintf()")
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-15 20:35:12 +02:00
|
|
|
if (at->dimensions >= 1 && at->nr_entries[0] == 0 && tag__is_const(type))
|
fprintf: Print "const" for class members more early, in type__fprintf()
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>
2019-04-09 23:07:24 +02:00
|
|
|
type = cu__type(cu, type->type);
|
|
|
|
|
2009-04-19 18:48:51 +02:00
|
|
|
printed = type__fprintf(type, cu, name, conf, fp);
|
2012-08-17 23:47:15 +02:00
|
|
|
for (i = 0; i < at->dimensions; ++i) {
|
|
|
|
if (conf->flat_arrays || at->is_vector) {
|
2009-04-19 18:48:51 +02:00
|
|
|
/*
|
|
|
|
* Seen on the Linux kernel on tun_filter:
|
|
|
|
*
|
|
|
|
* __u8 addr[0][ETH_ALEN];
|
|
|
|
*/
|
2012-08-17 23:47:15 +02:00
|
|
|
if (at->nr_entries[i] == 0 && i == 0)
|
2009-04-19 18:48:51 +02:00
|
|
|
break;
|
|
|
|
if (!flat_dimensions)
|
2012-08-17 23:47:15 +02:00
|
|
|
flat_dimensions = at->nr_entries[i];
|
2009-04-19 18:48:51 +02:00
|
|
|
else
|
2012-08-17 23:47:15 +02:00
|
|
|
flat_dimensions *= at->nr_entries[i];
|
fprintf: Print "const" for class members more early, in type__fprintf()
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>
2019-04-09 23:07:24 +02:00
|
|
|
} else {
|
2019-04-15 22:20:11 +02:00
|
|
|
bool single_member = conf->last_member && conf->first_member;
|
|
|
|
|
|
|
|
if (at->nr_entries[i] != 0 || !conf->last_member || single_member || conf->union_member)
|
fprintf: Print "const" for class members more early, in type__fprintf()
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>
2019-04-09 23:07:24 +02:00
|
|
|
printed += fprintf(fp, "[%u]", at->nr_entries[i]);
|
|
|
|
else
|
|
|
|
printed += fprintf(fp, "[]");
|
|
|
|
}
|
2009-04-19 18:48:51 +02:00
|
|
|
}
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
if (at->is_vector) {
|
|
|
|
type = tag__follow_typedef(tag, cu);
|
2009-04-19 18:48:51 +02:00
|
|
|
|
|
|
|
if (flat_dimensions == 0)
|
|
|
|
flat_dimensions = 1;
|
|
|
|
printed += fprintf(fp, " __attribute__ ((__vector_size__ (%llu)))",
|
|
|
|
flat_dimensions * tag__size(type, cu));
|
2019-04-10 23:25:37 +02:00
|
|
|
} else if (conf->flat_arrays) {
|
2019-04-15 22:20:11 +02:00
|
|
|
bool single_member = conf->last_member && conf->first_member;
|
|
|
|
|
|
|
|
if (flat_dimensions != 0 || !conf->last_member || single_member || conf->union_member)
|
2019-04-10 23:25:37 +02:00
|
|
|
printed += fprintf(fp, "[%llu]", flat_dimensions);
|
|
|
|
else
|
|
|
|
printed += fprintf(fp, "[]");
|
|
|
|
}
|
2009-04-19 18:48:51 +02:00
|
|
|
|
|
|
|
return printed;
|
|
|
|
}
|
|
|
|
|
2021-06-30 15:07:14 +02:00
|
|
|
static size_t string_type__fprintf(const struct tag *tag, const char *name,
|
2020-09-18 23:19:22 +02:00
|
|
|
const struct conf_fprintf *conf,
|
|
|
|
FILE *fp)
|
|
|
|
{
|
|
|
|
struct string_type *st = tag__string_type(tag);
|
|
|
|
|
|
|
|
return fprintf(fp, "string %*s[%u]", conf->type_spacing - 5, name, st->nr_entries);
|
|
|
|
}
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
size_t typedef__fprintf(const struct tag *tag, const struct cu *cu,
|
2009-04-19 18:48:51 +02:00
|
|
|
const struct conf_fprintf *conf, FILE *fp)
|
|
|
|
{
|
2012-08-17 23:47:15 +02:00
|
|
|
struct type *type = tag__type(tag);
|
2009-04-19 18:48:51 +02:00
|
|
|
const struct conf_fprintf *pconf = conf ?: &conf_fprintf__defaults;
|
2012-08-17 23:47:15 +02:00
|
|
|
const struct tag *tag_type;
|
2009-04-19 18:48:51 +02:00
|
|
|
const struct tag *ptr_type;
|
|
|
|
char bf[512];
|
|
|
|
int is_pointer = 0;
|
|
|
|
size_t printed;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check for void (humm, perhaps we should have a fake void tag instance
|
|
|
|
* to avoid all these checks?
|
|
|
|
*/
|
2012-08-17 23:47:15 +02:00
|
|
|
if (tag->type == 0)
|
2021-06-25 01:41:37 +02:00
|
|
|
return fprintf(fp, "typedef void %s", type__name(type));
|
2009-04-19 18:48:51 +02:00
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
tag_type = cu__type(cu, tag->type);
|
|
|
|
if (tag_type == NULL) {
|
2009-04-19 18:48:51 +02:00
|
|
|
printed = fprintf(fp, "typedef ");
|
2012-08-17 23:47:15 +02:00
|
|
|
printed += tag__id_not_found_fprintf(fp, tag->type);
|
2021-06-25 01:41:37 +02:00
|
|
|
return printed + fprintf(fp, " %s", type__name(type));
|
2009-04-19 18:48:51 +02:00
|
|
|
}
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
switch (tag_type->tag) {
|
2009-04-19 18:48:51 +02:00
|
|
|
case DW_TAG_array_type:
|
|
|
|
printed = fprintf(fp, "typedef ");
|
2021-06-25 01:41:37 +02:00
|
|
|
return printed + array_type__fprintf(tag_type, cu, type__name(type), pconf, fp);
|
2009-04-19 18:48:51 +02:00
|
|
|
case DW_TAG_pointer_type:
|
2012-08-17 23:47:15 +02:00
|
|
|
if (tag_type->type == 0) /* void pointer */
|
2009-04-19 18:48:51 +02:00
|
|
|
break;
|
2012-08-17 23:47:15 +02:00
|
|
|
ptr_type = cu__type(cu, tag_type->type);
|
2009-04-19 18:48:51 +02:00
|
|
|
if (ptr_type == NULL) {
|
|
|
|
printed = fprintf(fp, "typedef ");
|
2012-08-17 23:47:15 +02:00
|
|
|
printed += tag__id_not_found_fprintf(fp, tag_type->type);
|
2021-06-25 01:41:37 +02:00
|
|
|
return printed + fprintf(fp, " *%s", type__name(type));
|
2009-04-19 18:48:51 +02:00
|
|
|
}
|
|
|
|
if (ptr_type->tag != DW_TAG_subroutine_type)
|
|
|
|
break;
|
2012-08-17 23:47:15 +02:00
|
|
|
tag_type = ptr_type;
|
2009-04-19 18:48:51 +02:00
|
|
|
is_pointer = 1;
|
|
|
|
/* Fall thru */
|
|
|
|
case DW_TAG_subroutine_type:
|
|
|
|
printed = fprintf(fp, "typedef ");
|
2021-06-25 01:41:37 +02:00
|
|
|
return printed + ftype__fprintf(tag__ftype(tag_type), cu, type__name(type),
|
|
|
|
0, is_pointer, 0, true, pconf, fp);
|
2009-04-19 18:48:51 +02:00
|
|
|
case DW_TAG_class_type:
|
|
|
|
case DW_TAG_structure_type: {
|
2012-08-17 23:47:15 +02:00
|
|
|
struct type *ctype = tag__type(tag_type);
|
2009-04-19 18:48:51 +02:00
|
|
|
|
2021-06-25 01:41:37 +02:00
|
|
|
if (type__name(ctype) != NULL)
|
|
|
|
return fprintf(fp, "typedef struct %s %s", type__name(ctype), type__name(type));
|
2020-11-19 14:32:00 +01:00
|
|
|
|
|
|
|
struct conf_fprintf tconf = *pconf;
|
|
|
|
|
2021-06-25 01:41:37 +02:00
|
|
|
tconf.suffix = type__name(type);
|
2020-11-19 14:32:00 +01:00
|
|
|
return fprintf(fp, "typedef ") + __class__fprintf(tag__class(tag_type), cu, &tconf, fp);
|
2009-04-19 18:48:51 +02:00
|
|
|
}
|
fprintf: Make typedef__fprintf print anonymous enums
In all the examples the kernel BTF info is being used (/sys/kernel/btf/vmlinux).
Before:
$ pahole ZSTD_strategy
typedef enum ZSTD_strategy;
$
After:
$ pahole ZSTD_strategy
typedef enum {
ZSTD_fast = 0,
ZSTD_dfast = 1,
ZSTD_greedy = 2,
ZSTD_lazy = 3,
ZSTD_lazy2 = 4,
ZSTD_btlazy2 = 5,
ZSTD_btopt = 6,
ZSTD_btopt2 = 7,
} ZSTD_strategy;
$
Named ones continue to work as before:
$ pahole timespec_type
enum timespec_type {
TT_NONE = 0,
TT_NATIVE = 1,
TT_COMPAT = 2,
};
$
And the ones inside structs, when expanded, as well:
$ pahole ZSTD_parameters
typedef struct {
ZSTD_compressionParameters cParams; /* 0 28 */
ZSTD_frameParameters fParams; /* 28 12 */
/* size: 40, cachelines: 1, members: 2 */
/* last cacheline: 40 bytes */
} ZSTD_parameters;
$ pahole -E ZSTD_parameters
typedef struct {
/* typedef ZSTD_compressionParameters */ struct {
unsigned int windowLog; /* 0 4 */
unsigned int chainLog; /* 4 4 */
unsigned int hashLog; /* 8 4 */
unsigned int searchLog; /* 12 4 */
unsigned int searchLength; /* 16 4 */
unsigned int targetLength; /* 20 4 */
/* typedef ZSTD_strategy */ enum {
ZSTD_fast = 0,
ZSTD_dfast = 1,
ZSTD_greedy = 2,
ZSTD_lazy = 3,
ZSTD_lazy2 = 4,
ZSTD_btlazy2 = 5,
ZSTD_btopt = 6,
ZSTD_btopt2 = 7,
} strategy; /* 24 4 */
} cParams; /* 0 28 */
/* typedef ZSTD_frameParameters */ struct {
unsigned int contentSizeFlag; /* 28 4 */
unsigned int checksumFlag; /* 32 4 */
unsigned int noDictIDFlag; /* 36 4 */
} fParams; /* 28 12 */
/* size: 40, cachelines: 1, members: 2 */
/* last cacheline: 40 bytes */
} ZSTD_parameters;
$
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-11-19 15:41:18 +01:00
|
|
|
case DW_TAG_enumeration_type: {
|
|
|
|
struct type *ctype = tag__type(tag_type);
|
|
|
|
|
2021-06-25 01:41:37 +02:00
|
|
|
if (type__name(ctype) != NULL)
|
|
|
|
return fprintf(fp, "typedef enum %s %s", type__name(ctype), type__name(type));
|
fprintf: Make typedef__fprintf print anonymous enums
In all the examples the kernel BTF info is being used (/sys/kernel/btf/vmlinux).
Before:
$ pahole ZSTD_strategy
typedef enum ZSTD_strategy;
$
After:
$ pahole ZSTD_strategy
typedef enum {
ZSTD_fast = 0,
ZSTD_dfast = 1,
ZSTD_greedy = 2,
ZSTD_lazy = 3,
ZSTD_lazy2 = 4,
ZSTD_btlazy2 = 5,
ZSTD_btopt = 6,
ZSTD_btopt2 = 7,
} ZSTD_strategy;
$
Named ones continue to work as before:
$ pahole timespec_type
enum timespec_type {
TT_NONE = 0,
TT_NATIVE = 1,
TT_COMPAT = 2,
};
$
And the ones inside structs, when expanded, as well:
$ pahole ZSTD_parameters
typedef struct {
ZSTD_compressionParameters cParams; /* 0 28 */
ZSTD_frameParameters fParams; /* 28 12 */
/* size: 40, cachelines: 1, members: 2 */
/* last cacheline: 40 bytes */
} ZSTD_parameters;
$ pahole -E ZSTD_parameters
typedef struct {
/* typedef ZSTD_compressionParameters */ struct {
unsigned int windowLog; /* 0 4 */
unsigned int chainLog; /* 4 4 */
unsigned int hashLog; /* 8 4 */
unsigned int searchLog; /* 12 4 */
unsigned int searchLength; /* 16 4 */
unsigned int targetLength; /* 20 4 */
/* typedef ZSTD_strategy */ enum {
ZSTD_fast = 0,
ZSTD_dfast = 1,
ZSTD_greedy = 2,
ZSTD_lazy = 3,
ZSTD_lazy2 = 4,
ZSTD_btlazy2 = 5,
ZSTD_btopt = 6,
ZSTD_btopt2 = 7,
} strategy; /* 24 4 */
} cParams; /* 0 28 */
/* typedef ZSTD_frameParameters */ struct {
unsigned int contentSizeFlag; /* 28 4 */
unsigned int checksumFlag; /* 32 4 */
unsigned int noDictIDFlag; /* 36 4 */
} fParams; /* 28 12 */
/* size: 40, cachelines: 1, members: 2 */
/* last cacheline: 40 bytes */
} ZSTD_parameters;
$
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-11-19 15:41:18 +01:00
|
|
|
|
|
|
|
struct conf_fprintf tconf = *pconf;
|
|
|
|
|
2021-06-25 01:41:37 +02:00
|
|
|
tconf.suffix = type__name(type);
|
2021-06-25 14:56:00 +02:00
|
|
|
return fprintf(fp, "typedef ") + enumeration__fprintf(tag_type, &tconf, fp);
|
fprintf: Make typedef__fprintf print anonymous enums
In all the examples the kernel BTF info is being used (/sys/kernel/btf/vmlinux).
Before:
$ pahole ZSTD_strategy
typedef enum ZSTD_strategy;
$
After:
$ pahole ZSTD_strategy
typedef enum {
ZSTD_fast = 0,
ZSTD_dfast = 1,
ZSTD_greedy = 2,
ZSTD_lazy = 3,
ZSTD_lazy2 = 4,
ZSTD_btlazy2 = 5,
ZSTD_btopt = 6,
ZSTD_btopt2 = 7,
} ZSTD_strategy;
$
Named ones continue to work as before:
$ pahole timespec_type
enum timespec_type {
TT_NONE = 0,
TT_NATIVE = 1,
TT_COMPAT = 2,
};
$
And the ones inside structs, when expanded, as well:
$ pahole ZSTD_parameters
typedef struct {
ZSTD_compressionParameters cParams; /* 0 28 */
ZSTD_frameParameters fParams; /* 28 12 */
/* size: 40, cachelines: 1, members: 2 */
/* last cacheline: 40 bytes */
} ZSTD_parameters;
$ pahole -E ZSTD_parameters
typedef struct {
/* typedef ZSTD_compressionParameters */ struct {
unsigned int windowLog; /* 0 4 */
unsigned int chainLog; /* 4 4 */
unsigned int hashLog; /* 8 4 */
unsigned int searchLog; /* 12 4 */
unsigned int searchLength; /* 16 4 */
unsigned int targetLength; /* 20 4 */
/* typedef ZSTD_strategy */ enum {
ZSTD_fast = 0,
ZSTD_dfast = 1,
ZSTD_greedy = 2,
ZSTD_lazy = 3,
ZSTD_lazy2 = 4,
ZSTD_btlazy2 = 5,
ZSTD_btopt = 6,
ZSTD_btopt2 = 7,
} strategy; /* 24 4 */
} cParams; /* 0 28 */
/* typedef ZSTD_frameParameters */ struct {
unsigned int contentSizeFlag; /* 28 4 */
unsigned int checksumFlag; /* 32 4 */
unsigned int noDictIDFlag; /* 36 4 */
} fParams; /* 28 12 */
/* size: 40, cachelines: 1, members: 2 */
/* last cacheline: 40 bytes */
} ZSTD_parameters;
$
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2020-11-19 15:41:18 +01:00
|
|
|
}
|
2009-04-19 18:48:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return fprintf(fp, "typedef %s %s",
|
2021-06-25 01:41:37 +02:00
|
|
|
tag__name(tag_type, cu, bf, sizeof(bf), pconf), type__name(type));
|
2009-04-19 18:48:51 +02:00
|
|
|
}
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
static size_t imported_declaration__fprintf(const struct tag *tag,
|
2009-04-19 18:48:51 +02:00
|
|
|
const struct cu *cu, FILE *fp)
|
|
|
|
{
|
dwarves_fprintf: Bump the size passed to tag__name in imported_declaration__fprintf
As it was not enough to cover some, hum, limits in the way C++ encodes piles of
abstractions:
+++ /media/tb/pahole/regtest/after/usr/bin/enfuse.pahole -A.c 2009-08-21 09:04:15.000000000 -0300
@@ -10414,7 +10414,7 @@ struct _Vector_base<enblend::enfuseMain(
using ::_M_deallocate;
- using ::__uninitialized_move_a<enblend::enfuseMain(std::list<vigra::ImageImportInfo*, std::allocator<vigra::ImageImportInfo*> >&, vigra::ImageExportInfo&, vigra::Rect2D&) [with ImagePixelType = vigra::RGBValue<unsigned int, 0u, 1u, 2u>]::ImagePyramidType**, enblend::enfuseMain(std::list<vigra::ImageImportInfo*, std::allocator<vigra::ImageImportInfo*> >&, vigra::ImageExportInfo&, vigra::Rect2D&) [with ImagePixelType = vigra::RGBValue<unsigned int, 0u, 1u, 2u>]::ImagePyramidType**, std::allocator<enblend::enfuseMain(ð7K^A;
+ using ::__uninitialized_move_a<enblend::enfuseMain(std::list<vigra::ImageImportInfo*, std::allocator<vigra::ImageImportInfo*> >&, vigra::ImageExportInfo&, vigra::Rect2D&) [with ImagePixelType = vigra::RGBValue<unsigned int, 0u, 1u, 2u>]::ImagePyramidType**, enblend::enfuseMain(std::list<vigra::ImageImportInfo*, std::allocator<vigra::ImageImportInfo*> >&, vigra::ImageExportInfo&, vigra::Rect2D&) [with ImagePixelType = vigra::RGBValue<unsigned int, 0u, 1u, 2u>]::ImagePyramidType**, std::allocator<enblend::enfuseMain(std::list<vigra::ImageImportInfo*, std::allocator<vigra::ImageImportInfo*> >&, vigra::ImageExportInfo&, vigra::Rect2D&) [with ImagePixelType = vigra::RGBValue<unsigned int, 0u, 1u, 2u>]::ImagePyramidType*> >;
using ::_M_get_Tp_allocator;
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2009-08-21 14:05:15 +02:00
|
|
|
char bf[BUFSIZ];
|
2009-04-19 18:48:51 +02:00
|
|
|
size_t printed = fprintf(fp, "using ::");
|
2012-08-17 23:47:15 +02:00
|
|
|
const struct tag *decl = cu__function(cu, tag->type);
|
2009-04-19 18:48:51 +02:00
|
|
|
|
2009-08-20 23:33:52 +02:00
|
|
|
if (decl == NULL) {
|
2012-08-17 23:47:15 +02:00
|
|
|
decl = cu__tag(cu, tag->type);
|
2009-08-20 23:33:52 +02:00
|
|
|
if (decl == NULL)
|
2012-08-17 23:47:15 +02:00
|
|
|
return printed + tag__id_not_found_fprintf(fp, tag->type);
|
2009-08-20 23:33:52 +02:00
|
|
|
}
|
2009-04-19 18:48:51 +02:00
|
|
|
|
2009-08-24 22:22:43 +02:00
|
|
|
return printed + fprintf(fp, "%s", tag__name(decl, cu, bf, sizeof(bf), NULL));
|
2009-04-19 18:48:51 +02:00
|
|
|
}
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
static size_t imported_module__fprintf(const struct tag *tag,
|
2009-04-19 18:48:51 +02:00
|
|
|
const struct cu *cu, FILE *fp)
|
|
|
|
{
|
2012-08-17 23:47:15 +02:00
|
|
|
const struct tag *module = cu__tag(cu, tag->type);
|
2009-04-19 18:48:51 +02:00
|
|
|
const char *name = "<IMPORTED MODULE ERROR!>";
|
|
|
|
|
|
|
|
if (tag__is_namespace(module))
|
2021-06-24 15:01:27 +02:00
|
|
|
name = namespace__name(tag__namespace(module));
|
2009-04-19 18:48:51 +02:00
|
|
|
|
|
|
|
return fprintf(fp, "using namespace %s", name);
|
|
|
|
}
|
|
|
|
|
2021-06-25 14:57:24 +02:00
|
|
|
static int enumeration__max_entry_name_len(struct type *type)
|
2020-11-19 15:20:39 +01:00
|
|
|
{
|
|
|
|
if (type->max_tag_name_len)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
struct enumerator *pos;
|
|
|
|
|
|
|
|
type__for_each_enumerator(type, pos) {
|
2021-06-25 14:49:34 +02:00
|
|
|
int len = strlen(enumerator__name(pos));
|
2020-11-19 15:20:39 +01:00
|
|
|
|
|
|
|
if (type->max_tag_name_len < len)
|
|
|
|
type->max_tag_name_len = len;
|
|
|
|
}
|
|
|
|
out:
|
|
|
|
return type->max_tag_name_len;
|
|
|
|
}
|
|
|
|
|
2021-06-25 14:56:00 +02:00
|
|
|
size_t enumeration__fprintf(const struct tag *tag, const struct conf_fprintf *conf, FILE *fp)
|
2009-04-19 18:48:51 +02:00
|
|
|
{
|
2012-08-17 23:47:15 +02:00
|
|
|
struct type *type = tag__type(tag);
|
2009-04-19 18:48:51 +02:00
|
|
|
struct enumerator *pos;
|
2021-06-25 14:56:00 +02:00
|
|
|
int max_entry_name_len = enumeration__max_entry_name_len(type);
|
2021-06-25 01:41:37 +02:00
|
|
|
size_t printed = fprintf(fp, "enum%s%s {\n", type__name(type) ? " " : "", type__name(type) ?: "");
|
2009-04-19 18:48:51 +02:00
|
|
|
int indent = conf->indent;
|
|
|
|
|
|
|
|
if (indent >= (int)sizeof(tabs))
|
|
|
|
indent = sizeof(tabs) - 1;
|
|
|
|
|
fprintf: Honour conf_fprintf.hex when printing enumerations
Now this works:
$ pahole --hex perf_event_type
enum perf_event_type {
PERF_RECORD_MMAP = 0x1,
PERF_RECORD_LOST = 0x2,
PERF_RECORD_COMM = 0x3,
PERF_RECORD_EXIT = 0x4,
PERF_RECORD_THROTTLE = 0x5,
PERF_RECORD_UNTHROTTLE = 0x6,
PERF_RECORD_FORK = 0x7,
PERF_RECORD_READ = 0x8,
PERF_RECORD_SAMPLE = 0x9,
PERF_RECORD_MMAP2 = 0xa,
PERF_RECORD_AUX = 0xb,
PERF_RECORD_ITRACE_START = 0xc,
PERF_RECORD_LOST_SAMPLES = 0xd,
PERF_RECORD_SWITCH = 0xe,
PERF_RECORD_SWITCH_CPU_WIDE = 0xf,
PERF_RECORD_NAMESPACES = 0x10,
PERF_RECORD_KSYMBOL = 0x11,
PERF_RECORD_BPF_EVENT = 0x12,
PERF_RECORD_CGROUP = 0x13,
PERF_RECORD_TEXT_POKE = 0x14,
PERF_RECORD_MAX = 0x15,
};
$
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2021-02-20 12:41:11 +01:00
|
|
|
type__for_each_enumerator(type, pos) {
|
|
|
|
printed += fprintf(fp, "%.*s\t%-*s = ", indent, tabs,
|
2021-06-25 14:49:34 +02:00
|
|
|
max_entry_name_len, enumerator__name(pos));
|
fprintf: Honour conf_fprintf.hex when printing enumerations
Now this works:
$ pahole --hex perf_event_type
enum perf_event_type {
PERF_RECORD_MMAP = 0x1,
PERF_RECORD_LOST = 0x2,
PERF_RECORD_COMM = 0x3,
PERF_RECORD_EXIT = 0x4,
PERF_RECORD_THROTTLE = 0x5,
PERF_RECORD_UNTHROTTLE = 0x6,
PERF_RECORD_FORK = 0x7,
PERF_RECORD_READ = 0x8,
PERF_RECORD_SAMPLE = 0x9,
PERF_RECORD_MMAP2 = 0xa,
PERF_RECORD_AUX = 0xb,
PERF_RECORD_ITRACE_START = 0xc,
PERF_RECORD_LOST_SAMPLES = 0xd,
PERF_RECORD_SWITCH = 0xe,
PERF_RECORD_SWITCH_CPU_WIDE = 0xf,
PERF_RECORD_NAMESPACES = 0x10,
PERF_RECORD_KSYMBOL = 0x11,
PERF_RECORD_BPF_EVENT = 0x12,
PERF_RECORD_CGROUP = 0x13,
PERF_RECORD_TEXT_POKE = 0x14,
PERF_RECORD_MAX = 0x15,
};
$
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2021-02-20 12:41:11 +01:00
|
|
|
printed += fprintf(fp, conf->hex_fmt ? "%#x" : "%u", pos->value);
|
|
|
|
printed += fprintf(fp, ",\n");
|
|
|
|
}
|
2009-04-19 18:48:51 +02:00
|
|
|
|
2019-04-11 15:49:56 +02:00
|
|
|
printed += fprintf(fp, "%.*s}", indent, tabs);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXX: find out how to precisely determine the max size for an
|
|
|
|
* enumeration, use sizeof(int) for now.
|
|
|
|
*/
|
|
|
|
if (type->size / 8 != sizeof(int))
|
2019-07-02 15:29:49 +02:00
|
|
|
printed += fprintf(fp, " %s", "__attribute__((__packed__))");
|
2019-04-11 15:49:56 +02:00
|
|
|
|
|
|
|
if (conf->suffix)
|
|
|
|
printed += fprintf(fp, " %s", conf->suffix);
|
|
|
|
|
|
|
|
return printed;
|
2009-04-19 18:48:51 +02:00
|
|
|
}
|
|
|
|
|
2009-08-24 22:22:43 +02:00
|
|
|
static const char *tag__prefix(const struct cu *cu, const uint32_t tag,
|
|
|
|
const struct conf_fprintf *conf)
|
2009-04-19 18:48:51 +02:00
|
|
|
{
|
|
|
|
switch (tag) {
|
|
|
|
case DW_TAG_enumeration_type: return "enum ";
|
|
|
|
case DW_TAG_structure_type:
|
2009-08-24 22:22:43 +02:00
|
|
|
return (!conf->classes_as_structs &&
|
|
|
|
cu->language == DW_LANG_C_plus_plus) ? "class " :
|
|
|
|
"struct ";
|
|
|
|
case DW_TAG_class_type:
|
|
|
|
return conf->classes_as_structs ? "struct " : "class ";
|
2009-04-19 18:48:51 +02:00
|
|
|
case DW_TAG_union_type: return "union ";
|
|
|
|
case DW_TAG_pointer_type: return " *";
|
|
|
|
case DW_TAG_reference_type: return " &";
|
|
|
|
}
|
|
|
|
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
static const char *__tag__name(const struct tag *tag, const struct cu *cu,
|
2009-09-13 00:42:46 +02:00
|
|
|
char *bf, size_t len,
|
2009-09-11 06:51:22 +02:00
|
|
|
const struct conf_fprintf *conf);
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
static const char *tag__ptr_name(const struct tag *tag, const struct cu *cu,
|
2009-09-13 00:42:46 +02:00
|
|
|
char *bf, size_t len, const char *ptr_suffix)
|
2009-04-19 18:48:51 +02:00
|
|
|
{
|
2012-08-17 23:47:15 +02:00
|
|
|
if (tag->type == 0) /* No type == void */
|
2009-04-19 18:48:51 +02:00
|
|
|
snprintf(bf, len, "void %s", ptr_suffix);
|
|
|
|
else {
|
2012-08-17 23:47:15 +02:00
|
|
|
const struct tag *type = cu__type(cu, tag->type);
|
2009-04-19 18:48:51 +02:00
|
|
|
|
|
|
|
if (type == NULL) {
|
2012-08-17 23:47:15 +02:00
|
|
|
size_t l = tag__id_not_found_snprintf(bf, len, tag->type);
|
2009-04-19 18:48:51 +02:00
|
|
|
snprintf(bf + l, len - l, " %s", ptr_suffix);
|
2012-08-17 23:47:15 +02:00
|
|
|
} else if (!tag__has_type_loop(tag, type, bf, len, NULL)) {
|
2009-04-19 18:48:51 +02:00
|
|
|
char tmpbf[1024];
|
2019-04-12 17:07:46 +02:00
|
|
|
const char *const_pointer = "";
|
2009-09-14 22:07:02 +02:00
|
|
|
|
2019-04-12 17:07:46 +02:00
|
|
|
if (tag__is_const(type)) {
|
|
|
|
struct tag *next_type = cu__type(cu, type->type);
|
|
|
|
|
|
|
|
if (next_type && tag__is_pointer(next_type)) {
|
|
|
|
const_pointer = "const ";
|
|
|
|
type = next_type;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
snprintf(bf, len, "%s %s%s",
|
2009-09-11 06:51:22 +02:00
|
|
|
__tag__name(type, cu,
|
2009-09-13 00:42:46 +02:00
|
|
|
tmpbf, sizeof(tmpbf), NULL),
|
2019-04-12 17:07:46 +02:00
|
|
|
const_pointer,
|
2009-09-11 06:51:22 +02:00
|
|
|
ptr_suffix);
|
2009-04-19 18:48:51 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return bf;
|
|
|
|
}
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
static const char *__tag__name(const struct tag *tag, const struct cu *cu,
|
2009-09-13 00:42:46 +02:00
|
|
|
char *bf, size_t len,
|
2009-09-10 21:43:00 +02:00
|
|
|
const struct conf_fprintf *conf)
|
2009-04-19 18:48:51 +02:00
|
|
|
{
|
|
|
|
struct tag *type;
|
2009-08-24 22:22:43 +02:00
|
|
|
const struct conf_fprintf *pconf = conf ?: &conf_fprintf__defaults;
|
2009-04-19 18:48:51 +02:00
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
if (tag == NULL)
|
2009-04-19 18:48:51 +02:00
|
|
|
strncpy(bf, "void", len);
|
2012-08-17 23:47:15 +02:00
|
|
|
else switch (tag->tag) {
|
2009-04-19 18:48:51 +02:00
|
|
|
case DW_TAG_base_type: {
|
2012-08-17 23:47:15 +02:00
|
|
|
const struct base_type *bt = tag__base_type(tag);
|
2009-04-19 18:48:51 +02:00
|
|
|
const char *name = "nameless base type!";
|
|
|
|
char bf2[64];
|
|
|
|
|
|
|
|
if (bt->name)
|
2021-06-30 15:07:14 +02:00
|
|
|
name = base_type__name(tag__base_type(tag), bf2, sizeof(bf2));
|
2009-04-19 18:48:51 +02:00
|
|
|
|
|
|
|
strncpy(bf, name, len);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case DW_TAG_subprogram:
|
2021-06-25 19:11:30 +02:00
|
|
|
strncpy(bf, function__name(tag__function(tag)), len);
|
2009-04-19 18:48:51 +02:00
|
|
|
break;
|
|
|
|
case DW_TAG_pointer_type:
|
2012-08-17 23:47:15 +02:00
|
|
|
return tag__ptr_name(tag, cu, bf, len, "*");
|
2009-04-19 18:48:51 +02:00
|
|
|
case DW_TAG_reference_type:
|
2012-08-17 23:47:15 +02:00
|
|
|
return tag__ptr_name(tag, cu, bf, len, "&");
|
2009-04-19 18:48:51 +02:00
|
|
|
case DW_TAG_ptr_to_member_type: {
|
|
|
|
char suffix[512];
|
2019-03-07 22:04:20 +01:00
|
|
|
type_id_t id = tag__ptr_to_member_type(tag)->containing_type;
|
2009-04-19 18:48:51 +02:00
|
|
|
|
|
|
|
type = cu__type(cu, id);
|
|
|
|
if (type != NULL)
|
2021-06-25 01:41:37 +02:00
|
|
|
snprintf(suffix, sizeof(suffix), "%s::*", class__name(tag__class(type)));
|
2009-04-19 18:48:51 +02:00
|
|
|
else {
|
|
|
|
size_t l = tag__id_not_found_snprintf(suffix,
|
|
|
|
sizeof(suffix),
|
|
|
|
id);
|
|
|
|
snprintf(suffix + l, sizeof(suffix) - l, "::*");
|
|
|
|
}
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
return tag__ptr_name(tag, cu, bf, len, suffix);
|
2009-04-19 18:48:51 +02:00
|
|
|
}
|
|
|
|
case DW_TAG_volatile_type:
|
|
|
|
case DW_TAG_const_type:
|
dwarf_loader: Add support for DW_TAG_restrict_type
I.e. supporting the 'restrict' keyword, emitted by recent compilers:
[acme@jouet pahole]$ pfunct -P ~/bin/perf |& grep -w restrict
inline int vprintf(const char * restrict __fmt, struct __va_list_tag * __ap);
inline size_t fread(void * restrict __ptr, size_t __size, size_t __n, FILE * restrict __stream);
inline int vfprintf(FILE * restrict __stream, const char * restrict __fmt, struct __va_list_tag * __ap);
inline int vasprintf(char * * restrict __ptr, const char * restrict __fmt, struct __va_list_tag * __ap);
inline char * realpath(const char * restrict __name, char * restrict __resolved);
inline ssize_t readlink(const char * restrict __path, char * restrict __buf, size_t __len);
inline char * strcat(char * restrict __dest, const char * restrict __src);
inline char * fgets(char * restrict __s, int __n, FILE * restrict __stream);
inline int snprintf(char * restrict __s, size_t __n, const char * restrict __fmt, ...);
inline int sprintf(char * restrict __s, const char * restrict __fmt, ...);
inline char * strcpy(char * restrict __dest, const char * restrict __src);
inline int asprintf(char * * restrict __ptr, const char * restrict __fmt, ...);
inline char * strncpy(char * restrict __dest, const char * restrict __src, size_t __len);
inline int fprintf(FILE * restrict __stream, const char * restrict __fmt, ...);
inline int vsnprintf(char * restrict __s, size_t __n, const char * restrict __fmt, struct __va_list_tag * __ap);
inline int printf(const char * restrict __fmt, ...);
[acme@jouet pahole]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-05-06 20:02:17 +02:00
|
|
|
case DW_TAG_restrict_type:
|
2016-05-17 15:05:34 +02:00
|
|
|
case DW_TAG_unspecified_type:
|
2012-08-17 23:47:15 +02:00
|
|
|
type = cu__type(cu, tag->type);
|
|
|
|
if (type == NULL && tag->type != 0)
|
|
|
|
tag__id_not_found_snprintf(bf, len, tag->type);
|
|
|
|
else if (!tag__has_type_loop(tag, type, bf, len, NULL)) {
|
2009-04-19 18:48:51 +02:00
|
|
|
char tmpbf[128];
|
dwarf_loader: Add support for DW_TAG_restrict_type
I.e. supporting the 'restrict' keyword, emitted by recent compilers:
[acme@jouet pahole]$ pfunct -P ~/bin/perf |& grep -w restrict
inline int vprintf(const char * restrict __fmt, struct __va_list_tag * __ap);
inline size_t fread(void * restrict __ptr, size_t __size, size_t __n, FILE * restrict __stream);
inline int vfprintf(FILE * restrict __stream, const char * restrict __fmt, struct __va_list_tag * __ap);
inline int vasprintf(char * * restrict __ptr, const char * restrict __fmt, struct __va_list_tag * __ap);
inline char * realpath(const char * restrict __name, char * restrict __resolved);
inline ssize_t readlink(const char * restrict __path, char * restrict __buf, size_t __len);
inline char * strcat(char * restrict __dest, const char * restrict __src);
inline char * fgets(char * restrict __s, int __n, FILE * restrict __stream);
inline int snprintf(char * restrict __s, size_t __n, const char * restrict __fmt, ...);
inline int sprintf(char * restrict __s, const char * restrict __fmt, ...);
inline char * strcpy(char * restrict __dest, const char * restrict __src);
inline int asprintf(char * * restrict __ptr, const char * restrict __fmt, ...);
inline char * strncpy(char * restrict __dest, const char * restrict __src, size_t __len);
inline int fprintf(FILE * restrict __stream, const char * restrict __fmt, ...);
inline int vsnprintf(char * restrict __s, size_t __n, const char * restrict __fmt, struct __va_list_tag * __ap);
inline int printf(const char * restrict __fmt, ...);
[acme@jouet pahole]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-05-06 20:02:17 +02:00
|
|
|
const char *prefix = "", *suffix = "",
|
2009-09-10 21:43:00 +02:00
|
|
|
*type_str = __tag__name(type, cu, tmpbf,
|
|
|
|
sizeof(tmpbf),
|
2009-09-13 00:42:46 +02:00
|
|
|
pconf);
|
dwarf_loader: Add support for DW_TAG_restrict_type
I.e. supporting the 'restrict' keyword, emitted by recent compilers:
[acme@jouet pahole]$ pfunct -P ~/bin/perf |& grep -w restrict
inline int vprintf(const char * restrict __fmt, struct __va_list_tag * __ap);
inline size_t fread(void * restrict __ptr, size_t __size, size_t __n, FILE * restrict __stream);
inline int vfprintf(FILE * restrict __stream, const char * restrict __fmt, struct __va_list_tag * __ap);
inline int vasprintf(char * * restrict __ptr, const char * restrict __fmt, struct __va_list_tag * __ap);
inline char * realpath(const char * restrict __name, char * restrict __resolved);
inline ssize_t readlink(const char * restrict __path, char * restrict __buf, size_t __len);
inline char * strcat(char * restrict __dest, const char * restrict __src);
inline char * fgets(char * restrict __s, int __n, FILE * restrict __stream);
inline int snprintf(char * restrict __s, size_t __n, const char * restrict __fmt, ...);
inline int sprintf(char * restrict __s, const char * restrict __fmt, ...);
inline char * strcpy(char * restrict __dest, const char * restrict __src);
inline int asprintf(char * * restrict __ptr, const char * restrict __fmt, ...);
inline char * strncpy(char * restrict __dest, const char * restrict __src, size_t __len);
inline int fprintf(FILE * restrict __stream, const char * restrict __fmt, ...);
inline int vsnprintf(char * restrict __s, size_t __n, const char * restrict __fmt, struct __va_list_tag * __ap);
inline int printf(const char * restrict __fmt, ...);
[acme@jouet pahole]$
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-05-06 20:02:17 +02:00
|
|
|
switch (tag->tag) {
|
|
|
|
case DW_TAG_volatile_type: prefix = "volatile "; break;
|
|
|
|
case DW_TAG_const_type: prefix = "const "; break;
|
|
|
|
case DW_TAG_restrict_type: suffix = " restrict"; break;
|
|
|
|
}
|
|
|
|
snprintf(bf, len, "%s%s%s ", prefix, type_str, suffix);
|
2009-04-19 18:48:51 +02:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case DW_TAG_array_type:
|
2012-08-17 23:47:15 +02:00
|
|
|
type = cu__type(cu, tag->type);
|
2009-04-19 18:48:51 +02:00
|
|
|
if (type == NULL)
|
2012-08-17 23:47:15 +02:00
|
|
|
tag__id_not_found_snprintf(bf, len, tag->type);
|
|
|
|
else if (!tag__has_type_loop(tag, type, bf, len, NULL))
|
2009-09-13 00:42:46 +02:00
|
|
|
return __tag__name(type, cu, bf, len, pconf);
|
2009-04-19 18:48:51 +02:00
|
|
|
break;
|
|
|
|
case DW_TAG_subroutine_type: {
|
|
|
|
FILE *bfp = fmemopen(bf, len, "w");
|
|
|
|
|
|
|
|
if (bfp != NULL) {
|
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>
2019-11-05 15:30:51 +01:00
|
|
|
ftype__fprintf(tag__ftype(tag), cu, NULL, 0, 0, 0, true, pconf, bfp);
|
2009-04-19 18:48:51 +02:00
|
|
|
fclose(bfp);
|
|
|
|
} else
|
|
|
|
snprintf(bf, len, "<ERROR(%s): fmemopen failed!>",
|
|
|
|
__func__);
|
|
|
|
}
|
|
|
|
break;
|
2009-08-20 23:33:52 +02:00
|
|
|
case DW_TAG_member:
|
2021-06-24 15:01:27 +02:00
|
|
|
snprintf(bf, len, "%s", class_member__name(tag__class_member(tag)));
|
2009-08-20 23:33:52 +02:00
|
|
|
break;
|
2009-08-21 03:41:48 +02:00
|
|
|
case DW_TAG_variable:
|
2021-06-30 15:07:14 +02:00
|
|
|
snprintf(bf, len, "%s", variable__name(tag__variable(tag)));
|
2009-08-21 03:41:48 +02:00
|
|
|
break;
|
2009-04-19 18:48:51 +02:00
|
|
|
default:
|
2012-08-17 23:47:15 +02:00
|
|
|
snprintf(bf, len, "%s%s", tag__prefix(cu, tag->tag, pconf),
|
2021-06-25 01:41:37 +02:00
|
|
|
type__name(tag__type(tag)) ?: "");
|
2009-04-19 18:48:51 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return bf;
|
|
|
|
}
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
const char *tag__name(const struct tag *tag, const struct cu *cu,
|
2009-09-10 21:43:00 +02:00
|
|
|
char *bf, size_t len, const struct conf_fprintf *conf)
|
|
|
|
{
|
2019-04-05 02:47:36 +02:00
|
|
|
int printed = 0;
|
2009-09-11 06:51:22 +02:00
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
if (tag == NULL) {
|
2009-09-11 06:51:22 +02:00
|
|
|
strncpy(bf, "void", len);
|
|
|
|
return bf;
|
|
|
|
}
|
|
|
|
|
2019-04-05 02:47:36 +02:00
|
|
|
__tag__name(tag, cu, bf + printed, len - printed, conf);
|
2009-09-11 06:51:22 +02:00
|
|
|
|
|
|
|
return bf;
|
2009-09-10 21:43:00 +02:00
|
|
|
}
|
|
|
|
|
2009-04-19 18:48:51 +02:00
|
|
|
static const char *variable__prefix(const struct variable *var)
|
|
|
|
{
|
2018-09-05 20:07:46 +02:00
|
|
|
switch (variable__scope(var)) {
|
|
|
|
case VSCOPE_REGISTER:
|
2009-04-19 18:48:51 +02:00
|
|
|
return "register ";
|
2018-09-05 20:07:46 +02:00
|
|
|
case VSCOPE_UNKNOWN:
|
2009-04-19 18:48:51 +02:00
|
|
|
if (var->external && var->declaration)
|
|
|
|
return "extern ";
|
|
|
|
break;
|
2018-09-05 20:07:46 +02:00
|
|
|
case VSCOPE_GLOBAL:
|
2009-04-19 18:48:51 +02:00
|
|
|
if (!var->external)
|
|
|
|
return "static ";
|
|
|
|
break;
|
2018-09-05 20:07:46 +02:00
|
|
|
case VSCOPE_LOCAL:
|
|
|
|
case VSCOPE_OPTIMIZED:
|
2009-04-19 18:48:51 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-01-10 14:15:42 +01:00
|
|
|
static size_t type__fprintf_stats(struct type *type, const struct cu *cu,
|
|
|
|
const struct conf_fprintf *conf, FILE *fp)
|
|
|
|
{
|
|
|
|
size_t printed = fprintf(fp, "\n%.*s/* size: %d, cachelines: %zd, members: %u",
|
|
|
|
conf->indent, tabs, type->size,
|
2021-10-28 14:27:08 +02:00
|
|
|
tag__nr_cachelines(conf, type__tag(type), cu), type->nr_members);
|
2019-01-10 14:15:42 +01:00
|
|
|
|
|
|
|
if (type->nr_static_members != 0)
|
|
|
|
printed += fprintf(fp, ", static members: %u */\n", type->nr_static_members);
|
|
|
|
else
|
|
|
|
printed += fprintf(fp, " */\n");
|
|
|
|
|
|
|
|
return printed;
|
|
|
|
}
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
static size_t union__fprintf(struct type *type, const struct cu *cu,
|
2009-04-19 18:48:51 +02:00
|
|
|
const struct conf_fprintf *conf, FILE *fp);
|
|
|
|
|
|
|
|
static size_t type__fprintf(struct tag *type, const struct cu *cu,
|
|
|
|
const char *name, const struct conf_fprintf *conf,
|
|
|
|
FILE *fp)
|
|
|
|
{
|
|
|
|
char tbf[128];
|
|
|
|
char namebf[256];
|
fprintf: Pretty print struct members that are pointers to nameless structs
I.e. to structs defined inside other structs, without a name:
Before:
$ pahole -C parsed_partitions /home/acme/git/build/v5.1-rc4+/block/partitions/check.o
struct parsed_partitions {
struct block_device * bdev; /* 0 8 */
char name[32]; /* 8 32 */
struct * parts; /* 40 8 */
int next; /* 48 4 */
int limit; /* 52 4 */
bool access_beyond_eod; /* 56 1 */
/* XXX 7 bytes hole, try to pack */
/* --- cacheline 1 boundary (64 bytes) --- */
char * pp_buf; /* 64 8 */
/* size: 72, cachelines: 2, members: 7 */
/* sum members: 65, holes: 1, sum holes: 7 */
/* last cacheline: 8 bytes */
};
$
After:
$ pahole -C parsed_partitions /home/acme/git/build/v5.1-rc4+/block/partitions/check.o
struct parsed_partitions {
struct block_device * bdev; /* 0 8 */
char name[32]; /* 8 32 */
struct {
sector_t from; /* 40 8 */
sector_t size; /* 48 8 */
int flags; /* 56 4 */
bool has_info; /* 60 1 */
struct partition_meta_info info; /* 61 101 */
} * parts; /* 40 8 */
int next; /* 48 4 */
int limit; /* 52 4 */
bool access_beyond_eod; /* 56 1 */
/* XXX 7 bytes hole, try to pack */
/* --- cacheline 1 boundary (64 bytes) --- */
char * pp_buf; /* 64 8 */
/* size: 72, cachelines: 2, members: 7 */
/* sum members: 65, holes: 1, sum holes: 7 */
/* last cacheline: 8 bytes */
};
$
Still need to align that offsets, leave this for later.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-16 19:26:11 +02:00
|
|
|
char namebfptr[258];
|
2009-04-19 18:48:51 +02:00
|
|
|
struct type *ctype;
|
2019-07-02 21:17:44 +02:00
|
|
|
struct tag *type_expanded = NULL;
|
2019-07-02 21:28:50 +02:00
|
|
|
struct conf_fprintf tconf = {
|
|
|
|
.type_spacing = conf->type_spacing,
|
|
|
|
};
|
2009-04-19 18:48:51 +02:00
|
|
|
size_t printed = 0;
|
|
|
|
int expand_types = conf->expand_types;
|
|
|
|
int suppress_offset_comment = conf->suppress_offset_comment;
|
|
|
|
|
|
|
|
if (type == NULL)
|
|
|
|
goto out_type_not_found;
|
|
|
|
|
|
|
|
if (conf->expand_pointers) {
|
|
|
|
int nr_indirections = 0;
|
|
|
|
|
2019-04-05 20:21:55 +02:00
|
|
|
while (tag__is_pointer(type) && type->type != 0) {
|
2009-09-14 22:07:02 +02:00
|
|
|
struct tag *ttype = cu__type(cu, type->type);
|
|
|
|
if (ttype == NULL)
|
2009-04-19 18:48:51 +02:00
|
|
|
goto out_type_not_found;
|
2009-09-14 22:07:02 +02:00
|
|
|
else {
|
|
|
|
printed = tag__has_type_loop(type, ttype,
|
|
|
|
NULL, 0, fp);
|
|
|
|
if (printed)
|
|
|
|
return printed;
|
|
|
|
}
|
|
|
|
type = ttype;
|
2009-04-19 18:48:51 +02:00
|
|
|
++nr_indirections;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nr_indirections > 0) {
|
|
|
|
const size_t len = strlen(name);
|
|
|
|
if (len + nr_indirections >= sizeof(namebf))
|
|
|
|
goto out_type_not_found;
|
|
|
|
memset(namebf, '*', nr_indirections);
|
|
|
|
memcpy(namebf + nr_indirections, name, len);
|
|
|
|
namebf[len + nr_indirections] = '\0';
|
|
|
|
name = namebf;
|
|
|
|
}
|
|
|
|
|
|
|
|
expand_types = nr_indirections;
|
|
|
|
if (!suppress_offset_comment)
|
|
|
|
suppress_offset_comment = !!nr_indirections;
|
|
|
|
|
|
|
|
/* Avoid loops */
|
|
|
|
if (type->recursivity_level != 0)
|
|
|
|
expand_types = 0;
|
|
|
|
++type->recursivity_level;
|
2019-07-02 21:17:44 +02:00
|
|
|
type_expanded = type;
|
2009-04-19 18:48:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (expand_types) {
|
|
|
|
int typedef_expanded = 0;
|
|
|
|
|
|
|
|
while (tag__is_typedef(type)) {
|
2009-09-14 22:07:02 +02:00
|
|
|
struct tag *type_type;
|
|
|
|
int n;
|
|
|
|
|
2009-04-19 18:48:51 +02:00
|
|
|
ctype = tag__type(type);
|
|
|
|
if (typedef_expanded)
|
2021-06-25 01:41:37 +02:00
|
|
|
printed += fprintf(fp, " -> %s", type__name(ctype));
|
2009-04-19 18:48:51 +02:00
|
|
|
else {
|
2021-06-25 01:41:37 +02:00
|
|
|
printed += fprintf(fp, "/* typedef %s", type__name(ctype));
|
2009-04-19 18:48:51 +02:00
|
|
|
typedef_expanded = 1;
|
|
|
|
}
|
2009-09-14 22:07:02 +02:00
|
|
|
type_type = cu__type(cu, type->type);
|
|
|
|
if (type_type == NULL)
|
2009-04-19 18:48:51 +02:00
|
|
|
goto out_type_not_found;
|
2009-09-14 22:07:02 +02:00
|
|
|
n = tag__has_type_loop(type, type_type, NULL, 0, fp);
|
|
|
|
if (n)
|
|
|
|
return printed + n;
|
|
|
|
type = type_type;
|
2009-04-19 18:48:51 +02:00
|
|
|
}
|
|
|
|
if (typedef_expanded)
|
|
|
|
printed += fprintf(fp, " */ ");
|
|
|
|
}
|
|
|
|
|
fprintf: Print "const" for class members more early, in type__fprintf()
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>
2019-04-09 23:07:24 +02:00
|
|
|
tconf = *conf;
|
|
|
|
|
2009-04-19 18:48:51 +02:00
|
|
|
if (tag__is_struct(type) || tag__is_union(type) ||
|
|
|
|
tag__is_enumeration(type)) {
|
fprintf: Pretty print struct members that are pointers to nameless structs
I.e. to structs defined inside other structs, without a name:
Before:
$ pahole -C parsed_partitions /home/acme/git/build/v5.1-rc4+/block/partitions/check.o
struct parsed_partitions {
struct block_device * bdev; /* 0 8 */
char name[32]; /* 8 32 */
struct * parts; /* 40 8 */
int next; /* 48 4 */
int limit; /* 52 4 */
bool access_beyond_eod; /* 56 1 */
/* XXX 7 bytes hole, try to pack */
/* --- cacheline 1 boundary (64 bytes) --- */
char * pp_buf; /* 64 8 */
/* size: 72, cachelines: 2, members: 7 */
/* sum members: 65, holes: 1, sum holes: 7 */
/* last cacheline: 8 bytes */
};
$
After:
$ pahole -C parsed_partitions /home/acme/git/build/v5.1-rc4+/block/partitions/check.o
struct parsed_partitions {
struct block_device * bdev; /* 0 8 */
char name[32]; /* 8 32 */
struct {
sector_t from; /* 40 8 */
sector_t size; /* 48 8 */
int flags; /* 56 4 */
bool has_info; /* 60 1 */
struct partition_meta_info info; /* 61 101 */
} * parts; /* 40 8 */
int next; /* 48 4 */
int limit; /* 52 4 */
bool access_beyond_eod; /* 56 1 */
/* XXX 7 bytes hole, try to pack */
/* --- cacheline 1 boundary (64 bytes) --- */
char * pp_buf; /* 64 8 */
/* size: 72, cachelines: 2, members: 7 */
/* sum members: 65, holes: 1, sum holes: 7 */
/* last cacheline: 8 bytes */
};
$
Still need to align that offsets, leave this for later.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-16 19:26:11 +02:00
|
|
|
inner_struct:
|
2009-04-19 18:48:51 +02:00
|
|
|
tconf.prefix = NULL;
|
|
|
|
tconf.suffix = name;
|
|
|
|
tconf.emit_stats = 0;
|
|
|
|
tconf.suppress_offset_comment = suppress_offset_comment;
|
|
|
|
}
|
|
|
|
|
fprintf: Print "const" for class members more early, in type__fprintf()
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>
2019-04-09 23:07:24 +02:00
|
|
|
next_type:
|
2009-04-19 18:48:51 +02:00
|
|
|
switch (type->tag) {
|
|
|
|
case DW_TAG_pointer_type:
|
|
|
|
if (type->type != 0) {
|
2009-09-14 22:07:02 +02:00
|
|
|
int n;
|
2009-04-19 18:48:51 +02:00
|
|
|
struct tag *ptype = cu__type(cu, type->type);
|
|
|
|
if (ptype == NULL)
|
|
|
|
goto out_type_not_found;
|
2009-09-14 22:07:02 +02:00
|
|
|
n = tag__has_type_loop(type, ptype, NULL, 0, fp);
|
|
|
|
if (n)
|
|
|
|
return printed + n;
|
2009-04-19 18:48:51 +02:00
|
|
|
if (ptype->tag == DW_TAG_subroutine_type) {
|
|
|
|
printed += ftype__fprintf(tag__ftype(ptype),
|
|
|
|
cu, name, 0, 1,
|
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>
2019-11-05 15:30:51 +01:00
|
|
|
tconf.type_spacing, true,
|
fprintf: Print "const" for class members more early, in type__fprintf()
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>
2019-04-09 23:07:24 +02:00
|
|
|
&tconf, fp);
|
2009-04-19 18:48:51 +02:00
|
|
|
break;
|
|
|
|
}
|
2019-05-01 18:04:53 +02:00
|
|
|
if ((tag__is_struct(ptype) || tag__is_union(ptype) ||
|
2021-06-25 01:41:37 +02:00
|
|
|
tag__is_enumeration(ptype)) && type__name(tag__type(ptype)) == NULL) {
|
2019-07-02 16:46:31 +02:00
|
|
|
if (name == namebfptr)
|
|
|
|
goto out_type_not_found;
|
2019-12-16 14:37:51 +01:00
|
|
|
snprintf(namebfptr, sizeof(namebfptr), "* %.*s", (int)sizeof(namebfptr) - 3, name);
|
fprintf: Print relative offsets for inner pointer structs
I.e. before:
$ pahole -C parsed_partitions /home/acme/git/build/v5.1-rc4+/block/partitions/check.o > a.c ; head -29 a.c
struct parsed_partitions {
struct block_device * bdev; /* 0 8 */
char name[32]; /* 8 32 */
struct {
sector_t from; /* 40 8 */
sector_t size; /* 48 8 */
int flags; /* 56 4 */
bool has_info; /* 60 1 */
struct partition_meta_info info; /* 61 101 */
} * parts; /* 40 8 */
int next; /* 48 4 */
int limit; /* 52 4 */
bool access_beyond_eod; /* 56 1 */
/* XXX 7 bytes hole, try to pack */
/* --- cacheline 1 boundary (64 bytes) --- */
char * pp_buf; /* 64 8 */
/* size: 72, cachelines: 2, members: 7 */
/* sum members: 65, holes: 1, sum holes: 7 */
/* last cacheline: 8 bytes */
};
$
Now:
$ pahole -C parsed_partitions /home/acme/git/build/v5.1-rc4+/block/partitions/check.o
struct parsed_partitions {
struct block_device * bdev; /* 0 8 */
char name[32]; /* 8 32 */
struct {
sector_t from; /* 0 8 */
sector_t size; /* 8 8 */
int flags; /* 16 4 */
bool has_info; /* 20 1 */
struct partition_meta_info info; /* 21 101 */
} * parts; /* 40 8 */
int next; /* 48 4 */
int limit; /* 52 4 */
bool access_beyond_eod; /* 56 1 */
/* XXX 7 bytes hole, try to pack */
/* --- cacheline 1 boundary (64 bytes) --- */
char * pp_buf; /* 64 8 */
/* size: 72, cachelines: 2, members: 7 */
/* sum members: 65, holes: 1, sum holes: 7 */
/* last cacheline: 8 bytes */
};
$
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-16 19:39:51 +02:00
|
|
|
tconf.rel_offset = 1;
|
fprintf: Pretty print struct members that are pointers to nameless structs
I.e. to structs defined inside other structs, without a name:
Before:
$ pahole -C parsed_partitions /home/acme/git/build/v5.1-rc4+/block/partitions/check.o
struct parsed_partitions {
struct block_device * bdev; /* 0 8 */
char name[32]; /* 8 32 */
struct * parts; /* 40 8 */
int next; /* 48 4 */
int limit; /* 52 4 */
bool access_beyond_eod; /* 56 1 */
/* XXX 7 bytes hole, try to pack */
/* --- cacheline 1 boundary (64 bytes) --- */
char * pp_buf; /* 64 8 */
/* size: 72, cachelines: 2, members: 7 */
/* sum members: 65, holes: 1, sum holes: 7 */
/* last cacheline: 8 bytes */
};
$
After:
$ pahole -C parsed_partitions /home/acme/git/build/v5.1-rc4+/block/partitions/check.o
struct parsed_partitions {
struct block_device * bdev; /* 0 8 */
char name[32]; /* 8 32 */
struct {
sector_t from; /* 40 8 */
sector_t size; /* 48 8 */
int flags; /* 56 4 */
bool has_info; /* 60 1 */
struct partition_meta_info info; /* 61 101 */
} * parts; /* 40 8 */
int next; /* 48 4 */
int limit; /* 52 4 */
bool access_beyond_eod; /* 56 1 */
/* XXX 7 bytes hole, try to pack */
/* --- cacheline 1 boundary (64 bytes) --- */
char * pp_buf; /* 64 8 */
/* size: 72, cachelines: 2, members: 7 */
/* sum members: 65, holes: 1, sum holes: 7 */
/* last cacheline: 8 bytes */
};
$
Still need to align that offsets, leave this for later.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-16 19:26:11 +02:00
|
|
|
name = namebfptr;
|
|
|
|
type = ptype;
|
2019-11-19 15:48:36 +01:00
|
|
|
tconf.type_spacing -= 8;
|
fprintf: Pretty print struct members that are pointers to nameless structs
I.e. to structs defined inside other structs, without a name:
Before:
$ pahole -C parsed_partitions /home/acme/git/build/v5.1-rc4+/block/partitions/check.o
struct parsed_partitions {
struct block_device * bdev; /* 0 8 */
char name[32]; /* 8 32 */
struct * parts; /* 40 8 */
int next; /* 48 4 */
int limit; /* 52 4 */
bool access_beyond_eod; /* 56 1 */
/* XXX 7 bytes hole, try to pack */
/* --- cacheline 1 boundary (64 bytes) --- */
char * pp_buf; /* 64 8 */
/* size: 72, cachelines: 2, members: 7 */
/* sum members: 65, holes: 1, sum holes: 7 */
/* last cacheline: 8 bytes */
};
$
After:
$ pahole -C parsed_partitions /home/acme/git/build/v5.1-rc4+/block/partitions/check.o
struct parsed_partitions {
struct block_device * bdev; /* 0 8 */
char name[32]; /* 8 32 */
struct {
sector_t from; /* 40 8 */
sector_t size; /* 48 8 */
int flags; /* 56 4 */
bool has_info; /* 60 1 */
struct partition_meta_info info; /* 61 101 */
} * parts; /* 40 8 */
int next; /* 48 4 */
int limit; /* 52 4 */
bool access_beyond_eod; /* 56 1 */
/* XXX 7 bytes hole, try to pack */
/* --- cacheline 1 boundary (64 bytes) --- */
char * pp_buf; /* 64 8 */
/* size: 72, cachelines: 2, members: 7 */
/* sum members: 65, holes: 1, sum holes: 7 */
/* last cacheline: 8 bytes */
};
$
Still need to align that offsets, leave this for later.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-16 19:26:11 +02:00
|
|
|
goto inner_struct;
|
|
|
|
}
|
2009-04-19 18:48:51 +02:00
|
|
|
}
|
|
|
|
/* Fall Thru */
|
|
|
|
default:
|
2019-05-01 20:56:33 +02:00
|
|
|
print_default:
|
fprintf: Print "const" for class members more early, in type__fprintf()
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>
2019-04-09 23:07:24 +02:00
|
|
|
printed += fprintf(fp, "%-*s %s", tconf.type_spacing,
|
|
|
|
tag__name(type, cu, tbf, sizeof(tbf), &tconf),
|
2009-08-24 22:22:43 +02:00
|
|
|
name);
|
2009-04-19 18:48:51 +02:00
|
|
|
break;
|
|
|
|
case DW_TAG_subroutine_type:
|
|
|
|
printed += ftype__fprintf(tag__ftype(type), cu, name, 0, 0,
|
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>
2019-11-05 15:30:51 +01:00
|
|
|
tconf.type_spacing, true, &tconf, fp);
|
2009-04-19 18:48:51 +02:00
|
|
|
break;
|
fprintf: Print "const" for class members more early, in type__fprintf()
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>
2019-04-09 23:07:24 +02:00
|
|
|
case DW_TAG_const_type: {
|
|
|
|
size_t const_printed = fprintf(fp, "%s ", "const");
|
|
|
|
tconf.type_spacing -= const_printed;
|
|
|
|
printed += const_printed;
|
2019-07-02 15:42:52 +02:00
|
|
|
|
|
|
|
struct tag *ttype = cu__type(cu, type->type);
|
|
|
|
if (ttype) {
|
|
|
|
type = ttype;
|
2019-05-01 20:56:33 +02:00
|
|
|
goto next_type;
|
2019-07-02 15:42:52 +02:00
|
|
|
}
|
|
|
|
}
|
2019-05-01 20:56:33 +02:00
|
|
|
goto print_default;
|
fprintf: Print "const" for class members more early, in type__fprintf()
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>
2019-04-09 23:07:24 +02:00
|
|
|
|
2009-04-19 18:48:51 +02:00
|
|
|
case DW_TAG_array_type:
|
fprintf: Print "const" for class members more early, in type__fprintf()
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>
2019-04-09 23:07:24 +02:00
|
|
|
printed += array_type__fprintf(type, cu, name, &tconf, fp);
|
2009-04-19 18:48:51 +02:00
|
|
|
break;
|
2020-09-18 23:19:22 +02:00
|
|
|
case DW_TAG_string_type:
|
2021-06-30 15:07:14 +02:00
|
|
|
printed += string_type__fprintf(type, name, &tconf, fp);
|
2020-09-18 23:19:22 +02:00
|
|
|
break;
|
2009-04-19 18:48:51 +02:00
|
|
|
case DW_TAG_class_type:
|
|
|
|
case DW_TAG_structure_type:
|
|
|
|
ctype = tag__type(type);
|
|
|
|
|
2021-06-25 01:41:37 +02:00
|
|
|
if (type__name(ctype) != NULL && !expand_types) {
|
2009-04-19 18:48:51 +02:00
|
|
|
printed += fprintf(fp, "%s %-*s %s",
|
2009-08-24 22:22:43 +02:00
|
|
|
(type->tag == DW_TAG_class_type &&
|
fprintf: Print "const" for class members more early, in type__fprintf()
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>
2019-04-09 23:07:24 +02:00
|
|
|
!tconf.classes_as_structs) ? "class" : "struct",
|
|
|
|
tconf.type_spacing - 7,
|
2021-10-14 13:48:49 +02:00
|
|
|
type__name(ctype), name ?: "");
|
dwarves_fprintf: Find holes when expanding types
When --expand_types/-E is used we go on expanding internal types, and
when doing that for structs we were not looking for holes in them, only
on the main struct, fix it.
With that we can see these extra holes in a expanded Linux kernel's
'struct task_struct':
@@ -46,6 +46,9 @@
struct list_head * prev; /* 176 8 */
} group_node; /* 168 16 */
unsigned int on_rq; /* 184 4 */
+
+ /* XXX 4 bytes hole, try to pack */
+
/* --- cacheline 3 boundary (192 bytes) --- */
/* typedef u64 */ long long unsigned int exec_start; /* 192 8 */
/* typedef u64 */ long long unsigned int sum_exec_runtime; /* 200 8 */
@@ -86,9 +89,15 @@
} statistics; /* 232 216 */
/* --- cacheline 7 boundary (448 bytes) --- */
int depth; /* 448 4 */
+
+ /* XXX 4 bytes hole, try to pack */
+
struct sched_entity * parent; /* 456 8 */
struct cfs_rq * cfs_rq; /* 464 8 */
struct cfs_rq * my_q; /* 472 8 */
+
+ /* XXX 32 bytes hole, try to pack */
+
/* --- cacheline 8 boundary (512 bytes) --- */
struct sched_avg {
/* typedef u64 */ long long unsigned int last_update_time; /* 512 8 */
@@ -153,6 +162,9 @@
struct hrtimer_clock_base * base; /* 768 8 */
/* typedef u8 */ unsigned char state; /* 776 1 */
/* typedef u8 */ unsigned char is_rel; /* 777 1 */
+
+ /* XXX 2 bytes hole, try to pack */
+
int start_pid; /* 780 4 */
void * start_site; /* 784 8 */
char start_comm[16]; /* 792 16 */
@@ -197,6 +209,9 @@
} tasks; /* 912 16 */
struct plist_node {
int prio; /* 928 4 */
+
+ /* XXX 4 bytes hole, try to pack */
+
struct list_head {
struct list_head * next; /* 936 8 */
struct list_head * prev; /* 944 8 */
@@ -258,12 +273,18 @@
/* typedef u32 */ unsigned int val; /* 1136 4 */
/* typedef u32 */ unsigned int flags; /* 1140 4 */
/* typedef u32 */ unsigned int bitset; /* 1144 4 */
+
+ /* XXX 4 bytes hole, try to pack */
+
/* --- cacheline 18 boundary (1152 bytes) --- */
/* typedef u64 */ long long unsigned int time; /* 1152 8 */
u32 * uaddr2; /* 1160 8 */
} futex; /* 40 */
struct {
/* typedef clockid_t -> __kernel_clockid_t */ int clockid; /* 1128 4 */
+
+ /* XXX 4 bytes hole, try to pack */
+
struct timespec * rmtp; /* 1136 8 */
struct compat_timespec * compat_rmtp; /* 1144 8 */
/* typedef u64 */ long long unsigned int expires; /* 1152 8 */
@@ -426,6 +447,9 @@
unsigned int sessionid; /* 1804 4 */
struct seccomp {
int mode; /* 1808 4 */
+
+ /* XXX 4 bytes hole, try to pack */
+
struct seccomp_filter * filter; /* 1816 8 */
} seccomp; /* 1808 16 */
/* typedef u32 */ unsigned int parent_exec_id; /* 1824 4 */
@@ -602,6 +626,9 @@
long unsigned int backtrace[12]; /* 2472 96 */
/* --- cacheline 40 boundary (2560 bytes) was 8 bytes ago --- */
unsigned int count; /* 2568 4 */
+
+ /* XXX 4 bytes hole, try to pack */
+
long unsigned int time; /* 2576 8 */
long unsigned int max; /* 2584 8 */
} latency_record[32]; /* 2472 3840 */
@@ -686,12 +713,18 @@
long unsigned int * io_bitmap_ptr; /* 6600 8 */
long unsigned int iopl; /* 6608 8 */
unsigned int io_bitmap_max; /* 6616 4 */
+
+ /* XXX 36 bytes hole, try to pack */
+
/* --- cacheline 104 boundary (6656 bytes) --- */
struct fpu {
unsigned int last_cpu; /* 6656 4 */
unsigned char fpstate_active; /* 6660 1 */
unsigned char fpregs_active; /* 6661 1 */
unsigned char counter; /* 6662 1 */
+
+ /* XXX 57 bytes hole, try to pack */
+
/* --- cacheline 105 boundary (6720 bytes) --- */
union fpregs_state {
struct fregs_state {
@@ -751,6 +784,9 @@
/* typedef u8 */ unsigned char no_update; /* 6831 1 */
/* typedef u8 */ unsigned char rm; /* 6832 1 */
/* typedef u8 */ unsigned char alimit; /* 6833 1 */
+
+ /* XXX 6 bytes hole, try to pack */
+
struct math_emu_info * info; /* 6840 8 */
/* typedef u32 */ unsigned int entry_eip; /* 6848 4 */
} soft; /* 136 */
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-06-30 21:30:28 +02:00
|
|
|
} else {
|
|
|
|
struct class *cclass = tag__class(type);
|
|
|
|
|
fprintf: Print "const" for class members more early, in type__fprintf()
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>
2019-04-09 23:07:24 +02:00
|
|
|
if (!tconf.suppress_comments)
|
dwarves_fprintf: Find holes when expanding types
When --expand_types/-E is used we go on expanding internal types, and
when doing that for structs we were not looking for holes in them, only
on the main struct, fix it.
With that we can see these extra holes in a expanded Linux kernel's
'struct task_struct':
@@ -46,6 +46,9 @@
struct list_head * prev; /* 176 8 */
} group_node; /* 168 16 */
unsigned int on_rq; /* 184 4 */
+
+ /* XXX 4 bytes hole, try to pack */
+
/* --- cacheline 3 boundary (192 bytes) --- */
/* typedef u64 */ long long unsigned int exec_start; /* 192 8 */
/* typedef u64 */ long long unsigned int sum_exec_runtime; /* 200 8 */
@@ -86,9 +89,15 @@
} statistics; /* 232 216 */
/* --- cacheline 7 boundary (448 bytes) --- */
int depth; /* 448 4 */
+
+ /* XXX 4 bytes hole, try to pack */
+
struct sched_entity * parent; /* 456 8 */
struct cfs_rq * cfs_rq; /* 464 8 */
struct cfs_rq * my_q; /* 472 8 */
+
+ /* XXX 32 bytes hole, try to pack */
+
/* --- cacheline 8 boundary (512 bytes) --- */
struct sched_avg {
/* typedef u64 */ long long unsigned int last_update_time; /* 512 8 */
@@ -153,6 +162,9 @@
struct hrtimer_clock_base * base; /* 768 8 */
/* typedef u8 */ unsigned char state; /* 776 1 */
/* typedef u8 */ unsigned char is_rel; /* 777 1 */
+
+ /* XXX 2 bytes hole, try to pack */
+
int start_pid; /* 780 4 */
void * start_site; /* 784 8 */
char start_comm[16]; /* 792 16 */
@@ -197,6 +209,9 @@
} tasks; /* 912 16 */
struct plist_node {
int prio; /* 928 4 */
+
+ /* XXX 4 bytes hole, try to pack */
+
struct list_head {
struct list_head * next; /* 936 8 */
struct list_head * prev; /* 944 8 */
@@ -258,12 +273,18 @@
/* typedef u32 */ unsigned int val; /* 1136 4 */
/* typedef u32 */ unsigned int flags; /* 1140 4 */
/* typedef u32 */ unsigned int bitset; /* 1144 4 */
+
+ /* XXX 4 bytes hole, try to pack */
+
/* --- cacheline 18 boundary (1152 bytes) --- */
/* typedef u64 */ long long unsigned int time; /* 1152 8 */
u32 * uaddr2; /* 1160 8 */
} futex; /* 40 */
struct {
/* typedef clockid_t -> __kernel_clockid_t */ int clockid; /* 1128 4 */
+
+ /* XXX 4 bytes hole, try to pack */
+
struct timespec * rmtp; /* 1136 8 */
struct compat_timespec * compat_rmtp; /* 1144 8 */
/* typedef u64 */ long long unsigned int expires; /* 1152 8 */
@@ -426,6 +447,9 @@
unsigned int sessionid; /* 1804 4 */
struct seccomp {
int mode; /* 1808 4 */
+
+ /* XXX 4 bytes hole, try to pack */
+
struct seccomp_filter * filter; /* 1816 8 */
} seccomp; /* 1808 16 */
/* typedef u32 */ unsigned int parent_exec_id; /* 1824 4 */
@@ -602,6 +626,9 @@
long unsigned int backtrace[12]; /* 2472 96 */
/* --- cacheline 40 boundary (2560 bytes) was 8 bytes ago --- */
unsigned int count; /* 2568 4 */
+
+ /* XXX 4 bytes hole, try to pack */
+
long unsigned int time; /* 2576 8 */
long unsigned int max; /* 2584 8 */
} latency_record[32]; /* 2472 3840 */
@@ -686,12 +713,18 @@
long unsigned int * io_bitmap_ptr; /* 6600 8 */
long unsigned int iopl; /* 6608 8 */
unsigned int io_bitmap_max; /* 6616 4 */
+
+ /* XXX 36 bytes hole, try to pack */
+
/* --- cacheline 104 boundary (6656 bytes) --- */
struct fpu {
unsigned int last_cpu; /* 6656 4 */
unsigned char fpstate_active; /* 6660 1 */
unsigned char fpregs_active; /* 6661 1 */
unsigned char counter; /* 6662 1 */
+
+ /* XXX 57 bytes hole, try to pack */
+
/* --- cacheline 105 boundary (6720 bytes) --- */
union fpregs_state {
struct fregs_state {
@@ -751,6 +784,9 @@
/* typedef u8 */ unsigned char no_update; /* 6831 1 */
/* typedef u8 */ unsigned char rm; /* 6832 1 */
/* typedef u8 */ unsigned char alimit; /* 6833 1 */
+
+ /* XXX 6 bytes hole, try to pack */
+
struct math_emu_info * info; /* 6840 8 */
/* typedef u32 */ unsigned int entry_eip; /* 6848 4 */
} soft; /* 136 */
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-06-30 21:30:28 +02:00
|
|
|
class__find_holes(cclass);
|
|
|
|
|
2019-11-19 15:48:36 +01:00
|
|
|
tconf.type_spacing -= 8;
|
dwarves_fprintf: Find holes when expanding types
When --expand_types/-E is used we go on expanding internal types, and
when doing that for structs we were not looking for holes in them, only
on the main struct, fix it.
With that we can see these extra holes in a expanded Linux kernel's
'struct task_struct':
@@ -46,6 +46,9 @@
struct list_head * prev; /* 176 8 */
} group_node; /* 168 16 */
unsigned int on_rq; /* 184 4 */
+
+ /* XXX 4 bytes hole, try to pack */
+
/* --- cacheline 3 boundary (192 bytes) --- */
/* typedef u64 */ long long unsigned int exec_start; /* 192 8 */
/* typedef u64 */ long long unsigned int sum_exec_runtime; /* 200 8 */
@@ -86,9 +89,15 @@
} statistics; /* 232 216 */
/* --- cacheline 7 boundary (448 bytes) --- */
int depth; /* 448 4 */
+
+ /* XXX 4 bytes hole, try to pack */
+
struct sched_entity * parent; /* 456 8 */
struct cfs_rq * cfs_rq; /* 464 8 */
struct cfs_rq * my_q; /* 472 8 */
+
+ /* XXX 32 bytes hole, try to pack */
+
/* --- cacheline 8 boundary (512 bytes) --- */
struct sched_avg {
/* typedef u64 */ long long unsigned int last_update_time; /* 512 8 */
@@ -153,6 +162,9 @@
struct hrtimer_clock_base * base; /* 768 8 */
/* typedef u8 */ unsigned char state; /* 776 1 */
/* typedef u8 */ unsigned char is_rel; /* 777 1 */
+
+ /* XXX 2 bytes hole, try to pack */
+
int start_pid; /* 780 4 */
void * start_site; /* 784 8 */
char start_comm[16]; /* 792 16 */
@@ -197,6 +209,9 @@
} tasks; /* 912 16 */
struct plist_node {
int prio; /* 928 4 */
+
+ /* XXX 4 bytes hole, try to pack */
+
struct list_head {
struct list_head * next; /* 936 8 */
struct list_head * prev; /* 944 8 */
@@ -258,12 +273,18 @@
/* typedef u32 */ unsigned int val; /* 1136 4 */
/* typedef u32 */ unsigned int flags; /* 1140 4 */
/* typedef u32 */ unsigned int bitset; /* 1144 4 */
+
+ /* XXX 4 bytes hole, try to pack */
+
/* --- cacheline 18 boundary (1152 bytes) --- */
/* typedef u64 */ long long unsigned int time; /* 1152 8 */
u32 * uaddr2; /* 1160 8 */
} futex; /* 40 */
struct {
/* typedef clockid_t -> __kernel_clockid_t */ int clockid; /* 1128 4 */
+
+ /* XXX 4 bytes hole, try to pack */
+
struct timespec * rmtp; /* 1136 8 */
struct compat_timespec * compat_rmtp; /* 1144 8 */
/* typedef u64 */ long long unsigned int expires; /* 1152 8 */
@@ -426,6 +447,9 @@
unsigned int sessionid; /* 1804 4 */
struct seccomp {
int mode; /* 1808 4 */
+
+ /* XXX 4 bytes hole, try to pack */
+
struct seccomp_filter * filter; /* 1816 8 */
} seccomp; /* 1808 16 */
/* typedef u32 */ unsigned int parent_exec_id; /* 1824 4 */
@@ -602,6 +626,9 @@
long unsigned int backtrace[12]; /* 2472 96 */
/* --- cacheline 40 boundary (2560 bytes) was 8 bytes ago --- */
unsigned int count; /* 2568 4 */
+
+ /* XXX 4 bytes hole, try to pack */
+
long unsigned int time; /* 2576 8 */
long unsigned int max; /* 2584 8 */
} latency_record[32]; /* 2472 3840 */
@@ -686,12 +713,18 @@
long unsigned int * io_bitmap_ptr; /* 6600 8 */
long unsigned int iopl; /* 6608 8 */
unsigned int io_bitmap_max; /* 6616 4 */
+
+ /* XXX 36 bytes hole, try to pack */
+
/* --- cacheline 104 boundary (6656 bytes) --- */
struct fpu {
unsigned int last_cpu; /* 6656 4 */
unsigned char fpstate_active; /* 6660 1 */
unsigned char fpregs_active; /* 6661 1 */
unsigned char counter; /* 6662 1 */
+
+ /* XXX 57 bytes hole, try to pack */
+
/* --- cacheline 105 boundary (6720 bytes) --- */
union fpregs_state {
struct fregs_state {
@@ -751,6 +784,9 @@
/* typedef u8 */ unsigned char no_update; /* 6831 1 */
/* typedef u8 */ unsigned char rm; /* 6832 1 */
/* typedef u8 */ unsigned char alimit; /* 6833 1 */
+
+ /* XXX 6 bytes hole, try to pack */
+
struct math_emu_info * info; /* 6840 8 */
/* typedef u32 */ unsigned int entry_eip; /* 6848 4 */
} soft; /* 136 */
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-06-30 21:30:28 +02:00
|
|
|
printed += __class__fprintf(cclass, cu, &tconf, fp);
|
|
|
|
}
|
2009-04-19 18:48:51 +02:00
|
|
|
break;
|
|
|
|
case DW_TAG_union_type:
|
|
|
|
ctype = tag__type(type);
|
|
|
|
|
2021-06-25 01:41:37 +02:00
|
|
|
if (type__name(ctype) != NULL && !expand_types) {
|
2021-10-14 13:48:49 +02:00
|
|
|
printed += fprintf(fp, "union %-*s %s", tconf.type_spacing - 6, type__name(ctype), name ?: "");
|
2019-11-19 15:48:36 +01:00
|
|
|
} else {
|
|
|
|
tconf.type_spacing -= 8;
|
2009-04-19 18:48:51 +02:00
|
|
|
printed += union__fprintf(ctype, cu, &tconf, fp);
|
2019-11-19 15:48:36 +01:00
|
|
|
}
|
2009-04-19 18:48:51 +02:00
|
|
|
break;
|
|
|
|
case DW_TAG_enumeration_type:
|
|
|
|
ctype = tag__type(type);
|
|
|
|
|
2021-06-25 01:41:37 +02:00
|
|
|
if (type__name(ctype) != NULL)
|
2021-10-14 13:48:49 +02:00
|
|
|
printed += fprintf(fp, "enum %-*s %s", tconf.type_spacing - 5, type__name(ctype), name ?: "");
|
2009-04-19 18:48:51 +02:00
|
|
|
else
|
2021-06-25 14:56:00 +02:00
|
|
|
printed += enumeration__fprintf(type, &tconf, fp);
|
2009-04-19 18:48:51 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
out:
|
2019-07-02 21:17:44 +02:00
|
|
|
if (type_expanded)
|
|
|
|
--type_expanded->recursivity_level;
|
2009-04-19 18:48:51 +02:00
|
|
|
|
|
|
|
return printed;
|
|
|
|
out_type_not_found:
|
2019-07-02 16:46:31 +02:00
|
|
|
printed = fprintf(fp, "%-*s%s> %s", tconf.type_spacing, "<ERROR",
|
|
|
|
name == namebfptr ? ": pointer to pointer to inner struct/union/enum?" : "", name);
|
2009-04-19 18:48:51 +02:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2016-06-29 22:27:51 +02:00
|
|
|
static size_t class__fprintf_cacheline_boundary(struct conf_fprintf *conf,
|
|
|
|
uint32_t offset,
|
|
|
|
FILE *fp);
|
|
|
|
|
2017-12-15 17:33:10 +01:00
|
|
|
static size_t class_member__fprintf(struct class_member *member, bool union_member,
|
2009-04-19 18:48:51 +02:00
|
|
|
struct tag *type, const struct cu *cu,
|
2016-06-29 22:27:51 +02:00
|
|
|
struct conf_fprintf *conf, FILE *fp)
|
2009-04-19 18:48:51 +02:00
|
|
|
{
|
2012-08-17 23:47:15 +02:00
|
|
|
const int size = member->byte_size;
|
2021-10-27 20:42:04 +02:00
|
|
|
int member_alignment_printed = 0;
|
2009-04-19 18:48:51 +02:00
|
|
|
struct conf_fprintf sconf = *conf;
|
2012-08-17 23:47:15 +02:00
|
|
|
uint32_t offset = member->byte_offset;
|
2016-06-29 22:27:51 +02:00
|
|
|
size_t printed = 0, printed_cacheline = 0;
|
2021-06-24 15:01:27 +02:00
|
|
|
const char *cm_name = class_member__name(member),
|
2009-09-12 23:11:19 +02:00
|
|
|
*name = cm_name;
|
2009-04-19 18:48:51 +02:00
|
|
|
|
|
|
|
if (!sconf.rel_offset) {
|
2017-12-15 17:33:10 +01:00
|
|
|
offset += sconf.base_offset;
|
|
|
|
if (!union_member)
|
|
|
|
sconf.base_offset = offset;
|
2009-04-19 18:48:51 +02:00
|
|
|
}
|
|
|
|
|
dwarves_fprintf: Handle negative bit_offsets in packed structs with bitfields
Andrii reported that structs with bitfields and with __attribute__((packed)) were
not producing correct output, that is because pahole and friends didn't
knew about something Yonghong discovered: DW_AT_bit_offset may be
negative when a bitfield straddles a 4 byte boundary, fix it so that we
produce a saner output:
$ cat examples/yonghong/packed_bitfield.c
struct packed {
char x1: 1;
char x2: 3;
char x3: 3;
int y1: 7;
int y2: 20;
} __attribute__((packed));
struct packed g;
$
cc -g -c examples/yonghong/packed_bitfield.c
$ readelf -wi packed_bitfield.o | grep bit_offset
<37> DW_AT_bit_offset : 7
<46> DW_AT_bit_offset : 4
<55> DW_AT_bit_offset : 1
<64> DW_AT_bit_offset : 18
<73> DW_AT_bit_offset : -2
$
Before:
$ pahole packed_bitfield.o
struct packed {
char x1:1; /* 0: 7 1 */
char x2:3; /* 0: 4 1 */
char x3:3; /* 0: 1 1 */
int y1:7; /* 0:18 4 */
int y2:20; /* 0:4294967294 4 */
/* size: 5, cachelines: 1, members: 5 */
/* padding: 1 */
/* bit_padding: 254 bits */
/* last cacheline: 5 bytes */
/* BRAIN FART ALERT! 5 != 1 + 0(holes), diff = 4 */
};
$
Now:
$ pahole packed_bitfield.o
struct packed {
char x1:1; /* 0: 7 1 */
char x2:3; /* 0: 4 1 */
char x3:3; /* 0: 1 1 */
int y1:7; /* 0:18 4 */
int y2:20; /* 4:30 4 */
/* size: 5, cachelines: 1, members: 5 */
/* padding: 1 */
/* bit_padding: 254 bits */
/* last cacheline: 5 bytes */
/* BRAIN FART ALERT! 5 != 1 + 0(holes), diff = 4 */
};
$
And for two big endian archs, one 32-bit and the other 64-bit:
$ file ~acme/git/tmp/packed_bitfield.powerpc.o
/home/acme/git/tmp/packed_bitfield.powerpc.o: ELF 32-bit MSB relocatable, PowerPC or cisco 4500, version 1 (SYSV), with debug_info, not stripped
$ pahole ~acme/git/tmp/packed_bitfield.powerpc.o
struct packed {
char x1:1; /* 0: 0 1 */
char x2:3; /* 0: 1 1 */
char x3:3; /* 0: 4 1 */
int y1:7; /* 0: 7 4 */
int y2:20; /* 0:14 4 */
/* size: 5, cachelines: 1, members: 5 */
/* padding: 1 */
/* bit_padding: 254 bits */
/* last cacheline: 5 bytes */
/* BRAIN FART ALERT! 5 != 1 + 0(holes), diff = 4 */
};
$
$ file ~acme/git/tmp/packed_bitfield.sparc64.o
/home/acme/git/tmp/packed_bitfield.sparc64.o: ELF 64-bit MSB relocatable, SPARC V9, relaxed memory ordering, version 1 (SYSV), with debug_info, not stripped
$
$ pahole ~acme/git/tmp/packed_bitfield.sparc64.o
struct packed {
char x1:1; /* 0: 0 1 */
char x2:3; /* 0: 1 1 */
char x3:3; /* 0: 4 1 */
int y1:7; /* 0: 7 4 */
int y2:20; /* 0:14 4 */
/* size: 5, cachelines: 1, members: 5 */
/* padding: 1 */
/* bit_padding: 254 bits */
/* last cacheline: 5 bytes */
/* BRAIN FART ALERT! 5 != 1 + 0(holes), diff = 4 */
};
Now to fix the holes calculations.
Reported-by: Andrii Nakryiko <andrii.nakryiko@gmail.com>
Acked-by: Yonghong Song <yhs@fb.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Martin Lau <kafai@fb.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-01-15 17:52:29 +01:00
|
|
|
if (member->bitfield_offset < 0)
|
|
|
|
offset += member->byte_size;
|
|
|
|
|
2016-06-29 22:27:51 +02:00
|
|
|
if (!conf->suppress_comments)
|
|
|
|
printed_cacheline = class__fprintf_cacheline_boundary(conf, offset, fp);
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
if (member->tag.tag == DW_TAG_inheritance) {
|
2009-04-19 18:48:51 +02:00
|
|
|
name = "<ancestor>";
|
|
|
|
printed += fprintf(fp, "/* ");
|
|
|
|
}
|
|
|
|
|
2012-08-20 19:42:17 +02:00
|
|
|
if (member->is_static)
|
|
|
|
printed += fprintf(fp, "static ");
|
|
|
|
|
2021-10-14 13:48:49 +02:00
|
|
|
/* For struct-like constructs, the name of the member cannot be
|
|
|
|
* conflated with the name of its type, otherwise __attribute__ are
|
|
|
|
* printed in the wrong order.
|
|
|
|
*/
|
|
|
|
if (tag__is_union(type) || tag__is_struct(type) ||
|
|
|
|
tag__is_enumeration(type)) {
|
|
|
|
printed += type__fprintf(type, cu, NULL, &sconf, fp);
|
|
|
|
if (name) {
|
|
|
|
if (!type__name(tag__type(type)))
|
|
|
|
printed += fprintf(fp, " ");
|
|
|
|
printed += fprintf(fp, "%s", name);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
printed += type__fprintf(type, cu, name, &sconf, fp);
|
|
|
|
}
|
2009-04-19 18:48:51 +02:00
|
|
|
|
2012-08-20 19:42:17 +02:00
|
|
|
if (member->is_static) {
|
|
|
|
if (member->const_value != 0)
|
2019-04-03 17:31:54 +02:00
|
|
|
printed += fprintf(fp, " = %" PRIu64, member->const_value);
|
2012-08-20 19:42:17 +02:00
|
|
|
} else if (member->bitfield_size != 0) {
|
2019-04-03 17:31:54 +02:00
|
|
|
printed += fprintf(fp, ":%u", member->bitfield_size);
|
2009-04-19 18:48:51 +02:00
|
|
|
}
|
|
|
|
|
2021-10-27 20:42:04 +02:00
|
|
|
if (!sconf.suppress_aligned_attribute && member->alignment != 0) {
|
|
|
|
member_alignment_printed = fprintf(fp, " __attribute__((__aligned__(%u)))", member->alignment);
|
|
|
|
printed += member_alignment_printed;
|
|
|
|
}
|
dwarf_loader: Store the DW_AT_alignment if available
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>
2019-04-03 17:35:30 +02:00
|
|
|
|
2019-04-03 17:31:54 +02:00
|
|
|
fputc(';', fp);
|
|
|
|
++printed;
|
|
|
|
|
2009-04-19 18:48:51 +02:00
|
|
|
if ((tag__is_union(type) || tag__is_struct(type) ||
|
|
|
|
tag__is_enumeration(type)) &&
|
|
|
|
/* Look if is a type defined inline */
|
2021-06-25 01:41:37 +02:00
|
|
|
type__name(tag__type(type)) == NULL) {
|
2009-04-19 18:48:51 +02:00
|
|
|
if (!sconf.suppress_offset_comment) {
|
|
|
|
/* Check if this is a anonymous union */
|
2021-10-27 20:42:04 +02:00
|
|
|
int slen = member_alignment_printed + (cm_name ? (int)strlen(cm_name) : -1);
|
2019-03-12 20:43:37 +01:00
|
|
|
int size_spacing = 5;
|
|
|
|
|
2019-04-15 20:01:53 +02:00
|
|
|
if (tag__is_struct(type) && tag__class(type)->is_packed && !conf->suppress_packed) {
|
core: Infer if a struct is packed by the offsets/natural alignments
As DWARF (nor BTF) provides explicit attributes, we need to look at the
natural alignments, a byte is always alignted, etc.
This probably fails with things like __attribute__(__aligned(power-of-two)),
but with it most of the kernel data structures are full circled, i.e.
'pfunct --compile' regenerates source code from debug info that when
compiled generats debug info that end up matching the original sources.
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
const char * uptr;
int refcnt;
};
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
const char * uptr; /* 8 8 */
int refcnt; /* 16 4 */
/* size: 24, cachelines: 1, members: 3 */
/* padding: 4 */
/* last cacheline: 24 bytes */
};
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
const char * uptr;
int refcnt;
} __packed;
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
const char * uptr; /* 8 8 */
int refcnt; /* 16 4 */
/* size: 20, cachelines: 1, members: 3 */
/* last cacheline: 20 bytes */
} __attribute__((__packed__));
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
int refcnt;
const char * uptr;
};
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
int refcnt; /* 8 4 */
/* XXX 4 bytes hole, try to pack */
const char * uptr; /* 16 8 */
/* size: 24, cachelines: 1, members: 3 */
/* sum members: 20, holes: 1, sum holes: 4 */
/* last cacheline: 24 bytes */
};
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
int refcnt;
const char * uptr;
} __packed;
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
int refcnt; /* 8 4 */
const char * uptr; /* 12 8 */
/* size: 20, cachelines: 1, members: 3 */
/* last cacheline: 20 bytes */
} __attribute__((__packed__));
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
const char * uptr;
unsigned char refcnt;
};
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
const char * uptr; /* 8 8 */
unsigned char refcnt; /* 16 1 */
/* size: 24, cachelines: 1, members: 3 */
/* padding: 7 */
/* last cacheline: 24 bytes */
};
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
const char * uptr;
unsigned char refcnt;
} __packed;
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
const char * uptr; /* 8 8 */
unsigned char refcnt; /* 16 1 */
/* size: 17, cachelines: 1, members: 3 */
/* last cacheline: 17 bytes */
} __attribute__((__packed__));
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
unsigned char refcnt;
const char * uptr;
};
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
unsigned char refcnt; /* 8 1 */
/* XXX 7 bytes hole, try to pack */
const char * uptr; /* 16 8 */
/* size: 24, cachelines: 1, members: 3 */
/* sum members: 17, holes: 1, sum holes: 7 */
/* last cacheline: 24 bytes */
};
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
unsigned char refcnt;
const char * uptr;
} __packed;
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
unsigned char refcnt; /* 8 1 */
const char * uptr; /* 9 8 */
/* size: 17, cachelines: 1, members: 3 */
/* last cacheline: 17 bytes */
} __attribute__((__packed__));
$
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-10 22:41:59 +02:00
|
|
|
int packed_len = sizeof("__attribute__((__packed__))");
|
|
|
|
slen += packed_len;
|
|
|
|
}
|
|
|
|
|
2009-10-20 17:59:12 +02:00
|
|
|
printed += fprintf(fp, sconf.hex_fmt ?
|
2019-03-12 20:43:37 +01:00
|
|
|
"%*s/* %#5x" :
|
|
|
|
"%*s/* %5u",
|
2009-04-19 18:48:51 +02:00
|
|
|
(sconf.type_spacing +
|
|
|
|
sconf.name_spacing - slen - 3),
|
2019-03-12 20:43:37 +01:00
|
|
|
" ", offset);
|
|
|
|
|
|
|
|
if (member->bitfield_size != 0) {
|
|
|
|
unsigned int bitfield_offset = member->bitfield_offset;
|
|
|
|
|
|
|
|
if (member->bitfield_offset < 0)
|
|
|
|
bitfield_offset = member->byte_size * 8 + member->bitfield_offset;
|
|
|
|
|
|
|
|
printed += fprintf(fp, sconf.hex_fmt ? ":%#2x" : ":%2u", bitfield_offset);
|
|
|
|
size_spacing -= 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
printed += fprintf(fp, sconf.hex_fmt ? " %#*x */" : " %*u */", size_spacing, size);
|
2009-04-19 18:48:51 +02:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
int spacing = sconf.type_spacing + sconf.name_spacing - printed;
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
if (member->tag.tag == DW_TAG_inheritance) {
|
2009-04-19 18:48:51 +02:00
|
|
|
const size_t p = fprintf(fp, " */");
|
|
|
|
printed += p;
|
|
|
|
spacing -= p;
|
|
|
|
}
|
|
|
|
if (!sconf.suppress_offset_comment) {
|
|
|
|
int size_spacing = 5;
|
|
|
|
|
2009-10-20 17:59:12 +02:00
|
|
|
printed += fprintf(fp, sconf.hex_fmt ?
|
|
|
|
"%*s/* %#5x" : "%*s/* %5u",
|
2009-04-19 18:48:51 +02:00
|
|
|
spacing > 0 ? spacing : 0, " ",
|
|
|
|
offset);
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
if (member->bitfield_size != 0) {
|
dwarves_fprintf: Handle negative bit_offsets in packed structs with bitfields
Andrii reported that structs with bitfields and with __attribute__((packed)) were
not producing correct output, that is because pahole and friends didn't
knew about something Yonghong discovered: DW_AT_bit_offset may be
negative when a bitfield straddles a 4 byte boundary, fix it so that we
produce a saner output:
$ cat examples/yonghong/packed_bitfield.c
struct packed {
char x1: 1;
char x2: 3;
char x3: 3;
int y1: 7;
int y2: 20;
} __attribute__((packed));
struct packed g;
$
cc -g -c examples/yonghong/packed_bitfield.c
$ readelf -wi packed_bitfield.o | grep bit_offset
<37> DW_AT_bit_offset : 7
<46> DW_AT_bit_offset : 4
<55> DW_AT_bit_offset : 1
<64> DW_AT_bit_offset : 18
<73> DW_AT_bit_offset : -2
$
Before:
$ pahole packed_bitfield.o
struct packed {
char x1:1; /* 0: 7 1 */
char x2:3; /* 0: 4 1 */
char x3:3; /* 0: 1 1 */
int y1:7; /* 0:18 4 */
int y2:20; /* 0:4294967294 4 */
/* size: 5, cachelines: 1, members: 5 */
/* padding: 1 */
/* bit_padding: 254 bits */
/* last cacheline: 5 bytes */
/* BRAIN FART ALERT! 5 != 1 + 0(holes), diff = 4 */
};
$
Now:
$ pahole packed_bitfield.o
struct packed {
char x1:1; /* 0: 7 1 */
char x2:3; /* 0: 4 1 */
char x3:3; /* 0: 1 1 */
int y1:7; /* 0:18 4 */
int y2:20; /* 4:30 4 */
/* size: 5, cachelines: 1, members: 5 */
/* padding: 1 */
/* bit_padding: 254 bits */
/* last cacheline: 5 bytes */
/* BRAIN FART ALERT! 5 != 1 + 0(holes), diff = 4 */
};
$
And for two big endian archs, one 32-bit and the other 64-bit:
$ file ~acme/git/tmp/packed_bitfield.powerpc.o
/home/acme/git/tmp/packed_bitfield.powerpc.o: ELF 32-bit MSB relocatable, PowerPC or cisco 4500, version 1 (SYSV), with debug_info, not stripped
$ pahole ~acme/git/tmp/packed_bitfield.powerpc.o
struct packed {
char x1:1; /* 0: 0 1 */
char x2:3; /* 0: 1 1 */
char x3:3; /* 0: 4 1 */
int y1:7; /* 0: 7 4 */
int y2:20; /* 0:14 4 */
/* size: 5, cachelines: 1, members: 5 */
/* padding: 1 */
/* bit_padding: 254 bits */
/* last cacheline: 5 bytes */
/* BRAIN FART ALERT! 5 != 1 + 0(holes), diff = 4 */
};
$
$ file ~acme/git/tmp/packed_bitfield.sparc64.o
/home/acme/git/tmp/packed_bitfield.sparc64.o: ELF 64-bit MSB relocatable, SPARC V9, relaxed memory ordering, version 1 (SYSV), with debug_info, not stripped
$
$ pahole ~acme/git/tmp/packed_bitfield.sparc64.o
struct packed {
char x1:1; /* 0: 0 1 */
char x2:3; /* 0: 1 1 */
char x3:3; /* 0: 4 1 */
int y1:7; /* 0: 7 4 */
int y2:20; /* 0:14 4 */
/* size: 5, cachelines: 1, members: 5 */
/* padding: 1 */
/* bit_padding: 254 bits */
/* last cacheline: 5 bytes */
/* BRAIN FART ALERT! 5 != 1 + 0(holes), diff = 4 */
};
Now to fix the holes calculations.
Reported-by: Andrii Nakryiko <andrii.nakryiko@gmail.com>
Acked-by: Yonghong Song <yhs@fb.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Martin Lau <kafai@fb.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-01-15 17:52:29 +01:00
|
|
|
unsigned int bitfield_offset = member->bitfield_offset;
|
|
|
|
|
|
|
|
if (member->bitfield_offset < 0)
|
|
|
|
bitfield_offset = member->byte_size * 8 + member->bitfield_offset;
|
|
|
|
|
2009-10-20 17:59:12 +02:00
|
|
|
printed += fprintf(fp, sconf.hex_fmt ?
|
|
|
|
":%#2x" : ":%2u",
|
dwarves_fprintf: Handle negative bit_offsets in packed structs with bitfields
Andrii reported that structs with bitfields and with __attribute__((packed)) were
not producing correct output, that is because pahole and friends didn't
knew about something Yonghong discovered: DW_AT_bit_offset may be
negative when a bitfield straddles a 4 byte boundary, fix it so that we
produce a saner output:
$ cat examples/yonghong/packed_bitfield.c
struct packed {
char x1: 1;
char x2: 3;
char x3: 3;
int y1: 7;
int y2: 20;
} __attribute__((packed));
struct packed g;
$
cc -g -c examples/yonghong/packed_bitfield.c
$ readelf -wi packed_bitfield.o | grep bit_offset
<37> DW_AT_bit_offset : 7
<46> DW_AT_bit_offset : 4
<55> DW_AT_bit_offset : 1
<64> DW_AT_bit_offset : 18
<73> DW_AT_bit_offset : -2
$
Before:
$ pahole packed_bitfield.o
struct packed {
char x1:1; /* 0: 7 1 */
char x2:3; /* 0: 4 1 */
char x3:3; /* 0: 1 1 */
int y1:7; /* 0:18 4 */
int y2:20; /* 0:4294967294 4 */
/* size: 5, cachelines: 1, members: 5 */
/* padding: 1 */
/* bit_padding: 254 bits */
/* last cacheline: 5 bytes */
/* BRAIN FART ALERT! 5 != 1 + 0(holes), diff = 4 */
};
$
Now:
$ pahole packed_bitfield.o
struct packed {
char x1:1; /* 0: 7 1 */
char x2:3; /* 0: 4 1 */
char x3:3; /* 0: 1 1 */
int y1:7; /* 0:18 4 */
int y2:20; /* 4:30 4 */
/* size: 5, cachelines: 1, members: 5 */
/* padding: 1 */
/* bit_padding: 254 bits */
/* last cacheline: 5 bytes */
/* BRAIN FART ALERT! 5 != 1 + 0(holes), diff = 4 */
};
$
And for two big endian archs, one 32-bit and the other 64-bit:
$ file ~acme/git/tmp/packed_bitfield.powerpc.o
/home/acme/git/tmp/packed_bitfield.powerpc.o: ELF 32-bit MSB relocatable, PowerPC or cisco 4500, version 1 (SYSV), with debug_info, not stripped
$ pahole ~acme/git/tmp/packed_bitfield.powerpc.o
struct packed {
char x1:1; /* 0: 0 1 */
char x2:3; /* 0: 1 1 */
char x3:3; /* 0: 4 1 */
int y1:7; /* 0: 7 4 */
int y2:20; /* 0:14 4 */
/* size: 5, cachelines: 1, members: 5 */
/* padding: 1 */
/* bit_padding: 254 bits */
/* last cacheline: 5 bytes */
/* BRAIN FART ALERT! 5 != 1 + 0(holes), diff = 4 */
};
$
$ file ~acme/git/tmp/packed_bitfield.sparc64.o
/home/acme/git/tmp/packed_bitfield.sparc64.o: ELF 64-bit MSB relocatable, SPARC V9, relaxed memory ordering, version 1 (SYSV), with debug_info, not stripped
$
$ pahole ~acme/git/tmp/packed_bitfield.sparc64.o
struct packed {
char x1:1; /* 0: 0 1 */
char x2:3; /* 0: 1 1 */
char x3:3; /* 0: 4 1 */
int y1:7; /* 0: 7 4 */
int y2:20; /* 0:14 4 */
/* size: 5, cachelines: 1, members: 5 */
/* padding: 1 */
/* bit_padding: 254 bits */
/* last cacheline: 5 bytes */
/* BRAIN FART ALERT! 5 != 1 + 0(holes), diff = 4 */
};
Now to fix the holes calculations.
Reported-by: Andrii Nakryiko <andrii.nakryiko@gmail.com>
Acked-by: Yonghong Song <yhs@fb.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Martin Lau <kafai@fb.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-01-15 17:52:29 +01:00
|
|
|
bitfield_offset);
|
2009-04-19 18:48:51 +02:00
|
|
|
size_spacing -= 3;
|
|
|
|
}
|
|
|
|
|
2009-10-20 17:59:12 +02:00
|
|
|
printed += fprintf(fp, sconf.hex_fmt ?
|
|
|
|
" %#*x */" : " %*u */",
|
|
|
|
size_spacing, size);
|
2009-04-19 18:48:51 +02:00
|
|
|
}
|
|
|
|
}
|
2016-06-29 22:27:51 +02:00
|
|
|
return printed + printed_cacheline;
|
2009-04-19 18:48:51 +02:00
|
|
|
}
|
|
|
|
|
2017-12-15 17:33:10 +01:00
|
|
|
static size_t struct_member__fprintf(struct class_member *member,
|
|
|
|
struct tag *type, const struct cu *cu,
|
|
|
|
struct conf_fprintf *conf, FILE *fp)
|
|
|
|
{
|
|
|
|
return class_member__fprintf(member, false, type, cu, conf, fp);
|
|
|
|
}
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
static size_t union_member__fprintf(struct class_member *member,
|
2009-04-19 18:48:51 +02:00
|
|
|
struct tag *type, const struct cu *cu,
|
2017-12-15 17:33:10 +01:00
|
|
|
struct conf_fprintf *conf, FILE *fp)
|
2009-04-19 18:48:51 +02:00
|
|
|
{
|
2017-12-15 17:33:10 +01:00
|
|
|
return class_member__fprintf(member, true, type, cu, conf, fp);
|
2009-04-19 18:48:51 +02:00
|
|
|
}
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
static size_t union__fprintf(struct type *type, const struct cu *cu,
|
2009-04-19 18:48:51 +02:00
|
|
|
const struct conf_fprintf *conf, FILE *fp)
|
|
|
|
{
|
|
|
|
struct class_member *pos;
|
|
|
|
size_t printed = 0;
|
|
|
|
int indent = conf->indent;
|
|
|
|
struct conf_fprintf uconf;
|
dwarves_fprintf: Print cacheline boundaries in multiple union members
In 'struct audit_context' we have an union that have member structs that
straddles cacheline boundaries, the existing logic was showing those
cacheline boundaries only for the first struct in the union where that
straddling took place, all the subsequent structs where straddling also
takes place were not showing it, the struct:
struct audit_context {
<SNIP>
union {
struct {
int nargs; /* 824 4 */
/* XXX 4 bytes hole, try to pack */
/* --- cacheline 13 boundary (832 bytes) --- */
long int args[6]; /* 832 48 */
} socketcall; /* 824 56 */
struct {
kuid_t uid; /* 824 4 */
kgid_t gid; /* 828 4 */
umode_t mode; /* 832 2 */
/* XXX 2 bytes hole, try to pack */
u32 osid; /* 836 4 */
int has_perm; /* 840 4 */
uid_t perm_uid; /* 844 4 */
gid_t perm_gid; /* 848 4 */
umode_t perm_mode; /* 852 2 */
/* XXX 2 bytes hole, try to pack */
long unsigned int qbytes; /* 856 8 */
} ipc; /* 824 40 */
struct {
mqd_t mqdes; /* 824 4 */
/* XXX 4 bytes hole, try to pack */
struct mq_attr mqstat; /* 832 64 */
} mq_getsetattr; /* 824 72 */
struct {
mqd_t mqdes; /* 824 4 */
int sigev_signo; /* 828 4 */
} mq_notify; /* 824 8 */
struct {
mqd_t mqdes; /* 824 4 */
/* XXX 4 bytes hole, try to pack */
size_t msg_len; /* 832 8 */
unsigned int msg_prio; /* 840 4 */
/* XXX 4 bytes hole, try to pack */
struct timespec64 abs_timeout; /* 848 16 */
} mq_sendrecv; /* 824 40 */
struct {
int oflag; /* 824 4 */
umode_t mode; /* 828 2 */
/* XXX 2 bytes hole, try to pack */
struct mq_attr attr; /* 832 64 */
} mq_open; /* 824 72 */
struct {
pid_t pid; /* 824 4 */
struct audit_cap_data cap; /* 828 32 */
} capset; /* 824 36 */
struct {
int fd; /* 824 4 */
int flags; /* 828 4 */
} mmap; /* 824 8 */
struct {
int argc; /* 824 4 */
} execve; /* 824 4 */
struct {
char * name; /* 824 8 */
} module; /* 824 8 */
}; /* 824 72 */
/* --- cacheline 14 boundary (896 bytes) --- */
int fds[2]; /* 896 8 */
struct audit_proctitle proctitle; /* 904 16 */
/* size: 920, cachelines: 15, members: 46 */
/* sum members: 912, holes: 2, sum holes: 8 */
/* last cacheline: 24 bytes */
};
With this fix:
struct audit_context {
<SNIP>
union {
struct {
int nargs; /* 824 4 */
/* XXX 4 bytes hole, try to pack */
/* --- cacheline 13 boundary (832 bytes) --- */
long int args[6]; /* 832 48 */
} socketcall; /* 824 56 */
struct {
kuid_t uid; /* 824 4 */
kgid_t gid; /* 828 4 */
/* --- cacheline 13 boundary (832 bytes) --- */
umode_t mode; /* 832 2 */
/* XXX 2 bytes hole, try to pack */
u32 osid; /* 836 4 */
int has_perm; /* 840 4 */
uid_t perm_uid; /* 844 4 */
gid_t perm_gid; /* 848 4 */
umode_t perm_mode; /* 852 2 */
/* XXX 2 bytes hole, try to pack */
long unsigned int qbytes; /* 856 8 */
} ipc; /* 824 40 */
struct {
mqd_t mqdes; /* 824 4 */
/* XXX 4 bytes hole, try to pack */
/* --- cacheline 13 boundary (832 bytes) --- */
struct mq_attr mqstat; /* 832 64 */
} mq_getsetattr; /* 824 72 */
struct {
mqd_t mqdes; /* 824 4 */
int sigev_signo; /* 828 4 */
} mq_notify; /* 824 8 */
struct {
mqd_t mqdes; /* 824 4 */
/* XXX 4 bytes hole, try to pack */
/* --- cacheline 13 boundary (832 bytes) --- */
size_t msg_len; /* 832 8 */
unsigned int msg_prio; /* 840 4 */
/* XXX 4 bytes hole, try to pack */
struct timespec64 abs_timeout; /* 848 16 */
} mq_sendrecv; /* 824 40 */
struct {
int oflag; /* 824 4 */
umode_t mode; /* 828 2 */
/* XXX 2 bytes hole, try to pack */
/* --- cacheline 13 boundary (832 bytes) --- */
struct mq_attr attr; /* 832 64 */
} mq_open; /* 824 72 */
struct {
pid_t pid; /* 824 4 */
struct audit_cap_data cap; /* 828 32 */
} capset; /* 824 36 */
struct {
int fd; /* 824 4 */
int flags; /* 828 4 */
} mmap; /* 824 8 */
struct {
int argc; /* 824 4 */
} execve; /* 824 4 */
struct {
char * name; /* 824 8 */
} module; /* 824 8 */
}; /* 824 72 */
/* --- cacheline 14 boundary (896 bytes) --- */
int fds[2]; /* 896 8 */
struct audit_proctitle proctitle; /* 904 16 */
/* size: 920, cachelines: 15, members: 46 */
/* sum members: 912, holes: 2, sum holes: 8 */
/* last cacheline: 24 bytes */
};
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2018-07-28 19:25:30 +02:00
|
|
|
uint32_t initial_union_cacheline;
|
2019-07-02 17:11:27 +02:00
|
|
|
uint32_t cacheline = 0; /* This will only be used if this is the outermost union */
|
2009-04-19 18:48:51 +02:00
|
|
|
|
|
|
|
if (indent >= (int)sizeof(tabs))
|
|
|
|
indent = sizeof(tabs) - 1;
|
|
|
|
|
|
|
|
if (conf->prefix != NULL)
|
|
|
|
printed += fprintf(fp, "%s ", conf->prefix);
|
2021-06-25 01:41:37 +02:00
|
|
|
printed += fprintf(fp, "union%s%s {\n", type__name(type) ? " " : "",
|
|
|
|
type__name(type) ?: "");
|
2009-04-19 18:48:51 +02:00
|
|
|
|
|
|
|
uconf = *conf;
|
|
|
|
uconf.indent = indent + 1;
|
2018-09-26 22:06:56 +02:00
|
|
|
|
2019-04-12 22:08:41 +02:00
|
|
|
/*
|
|
|
|
* If structs embedded in unions, nameless or not, have a size which isn't
|
|
|
|
* isn't a multiple of the union size, then it must be packed, even if
|
|
|
|
* it has no holes nor padding, as an array of such unions would have the
|
|
|
|
* natural alignments of non-multiple structs inside it broken.
|
|
|
|
*/
|
|
|
|
union__infer_packed_attributes(type, cu);
|
|
|
|
|
2018-09-26 22:06:56 +02:00
|
|
|
/*
|
|
|
|
* We may be called directly or from tag__fprintf, so keep sure
|
|
|
|
* we keep track of the cacheline we're in.
|
|
|
|
*
|
|
|
|
* If we're being called from an outer structure, i.e. union within
|
|
|
|
* struct, class or another union, then this will already have a
|
|
|
|
* value and we'll continue to use it.
|
|
|
|
*/
|
|
|
|
if (uconf.cachelinep == NULL)
|
|
|
|
uconf.cachelinep = &cacheline;
|
dwarves_fprintf: Print cacheline boundaries in multiple union members
In 'struct audit_context' we have an union that have member structs that
straddles cacheline boundaries, the existing logic was showing those
cacheline boundaries only for the first struct in the union where that
straddling took place, all the subsequent structs where straddling also
takes place were not showing it, the struct:
struct audit_context {
<SNIP>
union {
struct {
int nargs; /* 824 4 */
/* XXX 4 bytes hole, try to pack */
/* --- cacheline 13 boundary (832 bytes) --- */
long int args[6]; /* 832 48 */
} socketcall; /* 824 56 */
struct {
kuid_t uid; /* 824 4 */
kgid_t gid; /* 828 4 */
umode_t mode; /* 832 2 */
/* XXX 2 bytes hole, try to pack */
u32 osid; /* 836 4 */
int has_perm; /* 840 4 */
uid_t perm_uid; /* 844 4 */
gid_t perm_gid; /* 848 4 */
umode_t perm_mode; /* 852 2 */
/* XXX 2 bytes hole, try to pack */
long unsigned int qbytes; /* 856 8 */
} ipc; /* 824 40 */
struct {
mqd_t mqdes; /* 824 4 */
/* XXX 4 bytes hole, try to pack */
struct mq_attr mqstat; /* 832 64 */
} mq_getsetattr; /* 824 72 */
struct {
mqd_t mqdes; /* 824 4 */
int sigev_signo; /* 828 4 */
} mq_notify; /* 824 8 */
struct {
mqd_t mqdes; /* 824 4 */
/* XXX 4 bytes hole, try to pack */
size_t msg_len; /* 832 8 */
unsigned int msg_prio; /* 840 4 */
/* XXX 4 bytes hole, try to pack */
struct timespec64 abs_timeout; /* 848 16 */
} mq_sendrecv; /* 824 40 */
struct {
int oflag; /* 824 4 */
umode_t mode; /* 828 2 */
/* XXX 2 bytes hole, try to pack */
struct mq_attr attr; /* 832 64 */
} mq_open; /* 824 72 */
struct {
pid_t pid; /* 824 4 */
struct audit_cap_data cap; /* 828 32 */
} capset; /* 824 36 */
struct {
int fd; /* 824 4 */
int flags; /* 828 4 */
} mmap; /* 824 8 */
struct {
int argc; /* 824 4 */
} execve; /* 824 4 */
struct {
char * name; /* 824 8 */
} module; /* 824 8 */
}; /* 824 72 */
/* --- cacheline 14 boundary (896 bytes) --- */
int fds[2]; /* 896 8 */
struct audit_proctitle proctitle; /* 904 16 */
/* size: 920, cachelines: 15, members: 46 */
/* sum members: 912, holes: 2, sum holes: 8 */
/* last cacheline: 24 bytes */
};
With this fix:
struct audit_context {
<SNIP>
union {
struct {
int nargs; /* 824 4 */
/* XXX 4 bytes hole, try to pack */
/* --- cacheline 13 boundary (832 bytes) --- */
long int args[6]; /* 832 48 */
} socketcall; /* 824 56 */
struct {
kuid_t uid; /* 824 4 */
kgid_t gid; /* 828 4 */
/* --- cacheline 13 boundary (832 bytes) --- */
umode_t mode; /* 832 2 */
/* XXX 2 bytes hole, try to pack */
u32 osid; /* 836 4 */
int has_perm; /* 840 4 */
uid_t perm_uid; /* 844 4 */
gid_t perm_gid; /* 848 4 */
umode_t perm_mode; /* 852 2 */
/* XXX 2 bytes hole, try to pack */
long unsigned int qbytes; /* 856 8 */
} ipc; /* 824 40 */
struct {
mqd_t mqdes; /* 824 4 */
/* XXX 4 bytes hole, try to pack */
/* --- cacheline 13 boundary (832 bytes) --- */
struct mq_attr mqstat; /* 832 64 */
} mq_getsetattr; /* 824 72 */
struct {
mqd_t mqdes; /* 824 4 */
int sigev_signo; /* 828 4 */
} mq_notify; /* 824 8 */
struct {
mqd_t mqdes; /* 824 4 */
/* XXX 4 bytes hole, try to pack */
/* --- cacheline 13 boundary (832 bytes) --- */
size_t msg_len; /* 832 8 */
unsigned int msg_prio; /* 840 4 */
/* XXX 4 bytes hole, try to pack */
struct timespec64 abs_timeout; /* 848 16 */
} mq_sendrecv; /* 824 40 */
struct {
int oflag; /* 824 4 */
umode_t mode; /* 828 2 */
/* XXX 2 bytes hole, try to pack */
/* --- cacheline 13 boundary (832 bytes) --- */
struct mq_attr attr; /* 832 64 */
} mq_open; /* 824 72 */
struct {
pid_t pid; /* 824 4 */
struct audit_cap_data cap; /* 828 32 */
} capset; /* 824 36 */
struct {
int fd; /* 824 4 */
int flags; /* 828 4 */
} mmap; /* 824 8 */
struct {
int argc; /* 824 4 */
} execve; /* 824 4 */
struct {
char * name; /* 824 8 */
} module; /* 824 8 */
}; /* 824 72 */
/* --- cacheline 14 boundary (896 bytes) --- */
int fds[2]; /* 896 8 */
struct audit_proctitle proctitle; /* 904 16 */
/* size: 920, cachelines: 15, members: 46 */
/* sum members: 912, holes: 2, sum holes: 8 */
/* last cacheline: 24 bytes */
};
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2018-07-28 19:25:30 +02:00
|
|
|
/*
|
|
|
|
* Save the cacheline we're in, then, after each union member, get
|
|
|
|
* back to it. Else we'll end up showing cacheline boundaries in
|
|
|
|
* just the first of a multi struct union, for instance.
|
|
|
|
*/
|
|
|
|
initial_union_cacheline = *uconf.cachelinep;
|
2012-08-17 23:47:15 +02:00
|
|
|
type__for_each_member(type, pos) {
|
2019-04-15 21:30:15 +02:00
|
|
|
struct tag *pos_type = cu__type(cu, pos->tag.type);
|
2009-04-19 18:48:51 +02:00
|
|
|
|
2019-04-15 21:30:15 +02:00
|
|
|
if (pos_type == NULL) {
|
2009-04-19 18:48:51 +02:00
|
|
|
printed += fprintf(fp, "%.*s", uconf.indent, tabs);
|
|
|
|
printed += tag__id_not_found_fprintf(fp, pos->tag.type);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2019-04-15 22:09:03 +02:00
|
|
|
uconf.union_member = 1;
|
2009-04-19 18:48:51 +02:00
|
|
|
printed += fprintf(fp, "%.*s", uconf.indent, tabs);
|
2019-04-15 21:30:15 +02:00
|
|
|
printed += union_member__fprintf(pos, pos_type, cu, &uconf, fp);
|
2009-04-19 18:48:51 +02:00
|
|
|
fputc('\n', fp);
|
|
|
|
++printed;
|
dwarves_fprintf: Print cacheline boundaries in multiple union members
In 'struct audit_context' we have an union that have member structs that
straddles cacheline boundaries, the existing logic was showing those
cacheline boundaries only for the first struct in the union where that
straddling took place, all the subsequent structs where straddling also
takes place were not showing it, the struct:
struct audit_context {
<SNIP>
union {
struct {
int nargs; /* 824 4 */
/* XXX 4 bytes hole, try to pack */
/* --- cacheline 13 boundary (832 bytes) --- */
long int args[6]; /* 832 48 */
} socketcall; /* 824 56 */
struct {
kuid_t uid; /* 824 4 */
kgid_t gid; /* 828 4 */
umode_t mode; /* 832 2 */
/* XXX 2 bytes hole, try to pack */
u32 osid; /* 836 4 */
int has_perm; /* 840 4 */
uid_t perm_uid; /* 844 4 */
gid_t perm_gid; /* 848 4 */
umode_t perm_mode; /* 852 2 */
/* XXX 2 bytes hole, try to pack */
long unsigned int qbytes; /* 856 8 */
} ipc; /* 824 40 */
struct {
mqd_t mqdes; /* 824 4 */
/* XXX 4 bytes hole, try to pack */
struct mq_attr mqstat; /* 832 64 */
} mq_getsetattr; /* 824 72 */
struct {
mqd_t mqdes; /* 824 4 */
int sigev_signo; /* 828 4 */
} mq_notify; /* 824 8 */
struct {
mqd_t mqdes; /* 824 4 */
/* XXX 4 bytes hole, try to pack */
size_t msg_len; /* 832 8 */
unsigned int msg_prio; /* 840 4 */
/* XXX 4 bytes hole, try to pack */
struct timespec64 abs_timeout; /* 848 16 */
} mq_sendrecv; /* 824 40 */
struct {
int oflag; /* 824 4 */
umode_t mode; /* 828 2 */
/* XXX 2 bytes hole, try to pack */
struct mq_attr attr; /* 832 64 */
} mq_open; /* 824 72 */
struct {
pid_t pid; /* 824 4 */
struct audit_cap_data cap; /* 828 32 */
} capset; /* 824 36 */
struct {
int fd; /* 824 4 */
int flags; /* 828 4 */
} mmap; /* 824 8 */
struct {
int argc; /* 824 4 */
} execve; /* 824 4 */
struct {
char * name; /* 824 8 */
} module; /* 824 8 */
}; /* 824 72 */
/* --- cacheline 14 boundary (896 bytes) --- */
int fds[2]; /* 896 8 */
struct audit_proctitle proctitle; /* 904 16 */
/* size: 920, cachelines: 15, members: 46 */
/* sum members: 912, holes: 2, sum holes: 8 */
/* last cacheline: 24 bytes */
};
With this fix:
struct audit_context {
<SNIP>
union {
struct {
int nargs; /* 824 4 */
/* XXX 4 bytes hole, try to pack */
/* --- cacheline 13 boundary (832 bytes) --- */
long int args[6]; /* 832 48 */
} socketcall; /* 824 56 */
struct {
kuid_t uid; /* 824 4 */
kgid_t gid; /* 828 4 */
/* --- cacheline 13 boundary (832 bytes) --- */
umode_t mode; /* 832 2 */
/* XXX 2 bytes hole, try to pack */
u32 osid; /* 836 4 */
int has_perm; /* 840 4 */
uid_t perm_uid; /* 844 4 */
gid_t perm_gid; /* 848 4 */
umode_t perm_mode; /* 852 2 */
/* XXX 2 bytes hole, try to pack */
long unsigned int qbytes; /* 856 8 */
} ipc; /* 824 40 */
struct {
mqd_t mqdes; /* 824 4 */
/* XXX 4 bytes hole, try to pack */
/* --- cacheline 13 boundary (832 bytes) --- */
struct mq_attr mqstat; /* 832 64 */
} mq_getsetattr; /* 824 72 */
struct {
mqd_t mqdes; /* 824 4 */
int sigev_signo; /* 828 4 */
} mq_notify; /* 824 8 */
struct {
mqd_t mqdes; /* 824 4 */
/* XXX 4 bytes hole, try to pack */
/* --- cacheline 13 boundary (832 bytes) --- */
size_t msg_len; /* 832 8 */
unsigned int msg_prio; /* 840 4 */
/* XXX 4 bytes hole, try to pack */
struct timespec64 abs_timeout; /* 848 16 */
} mq_sendrecv; /* 824 40 */
struct {
int oflag; /* 824 4 */
umode_t mode; /* 828 2 */
/* XXX 2 bytes hole, try to pack */
/* --- cacheline 13 boundary (832 bytes) --- */
struct mq_attr attr; /* 832 64 */
} mq_open; /* 824 72 */
struct {
pid_t pid; /* 824 4 */
struct audit_cap_data cap; /* 828 32 */
} capset; /* 824 36 */
struct {
int fd; /* 824 4 */
int flags; /* 828 4 */
} mmap; /* 824 8 */
struct {
int argc; /* 824 4 */
} execve; /* 824 4 */
struct {
char * name; /* 824 8 */
} module; /* 824 8 */
}; /* 824 72 */
/* --- cacheline 14 boundary (896 bytes) --- */
int fds[2]; /* 896 8 */
struct audit_proctitle proctitle; /* 904 16 */
/* size: 920, cachelines: 15, members: 46 */
/* sum members: 912, holes: 2, sum holes: 8 */
/* last cacheline: 24 bytes */
};
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2018-07-28 19:25:30 +02:00
|
|
|
*uconf.cachelinep = initial_union_cacheline;
|
2009-04-19 18:48:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return printed + fprintf(fp, "%.*s}%s%s", indent, tabs,
|
|
|
|
conf->suffix ? " " : "", conf->suffix ?: "");
|
|
|
|
}
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
const char *function__prototype(const struct function *func,
|
2009-04-19 18:48:51 +02:00
|
|
|
const struct cu *cu, char *bf, size_t len)
|
|
|
|
{
|
|
|
|
FILE *bfp = fmemopen(bf, len, "w");
|
|
|
|
|
|
|
|
if (bfp != NULL) {
|
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>
2019-11-05 15:30:51 +01:00
|
|
|
ftype__fprintf(&func->proto, cu, NULL, 0, 0, 0, true,
|
2009-04-19 18:48:51 +02:00
|
|
|
&conf_fprintf__defaults, bfp);
|
|
|
|
fclose(bfp);
|
|
|
|
} else
|
|
|
|
snprintf(bf, len, "<ERROR(%s): fmemopen failed!>", __func__);
|
|
|
|
|
|
|
|
return bf;
|
|
|
|
}
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
size_t ftype__fprintf_parms(const struct ftype *ftype,
|
2009-06-08 19:23:46 +02:00
|
|
|
const struct cu *cu, int indent,
|
|
|
|
const struct conf_fprintf *conf, FILE *fp)
|
2009-04-19 18:48:51 +02:00
|
|
|
{
|
|
|
|
struct parameter *pos;
|
|
|
|
int first_parm = 1;
|
|
|
|
char sbf[128];
|
|
|
|
struct tag *type;
|
|
|
|
const char *name, *stype;
|
|
|
|
size_t printed = fprintf(fp, "(");
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
ftype__for_each_parameter(ftype, pos) {
|
2009-04-19 18:48:51 +02:00
|
|
|
if (!first_parm) {
|
|
|
|
if (indent == 0)
|
|
|
|
printed += fprintf(fp, ", ");
|
|
|
|
else
|
|
|
|
printed += fprintf(fp, ",\n%.*s",
|
|
|
|
indent, tabs);
|
|
|
|
} else
|
|
|
|
first_parm = 0;
|
2021-06-24 15:01:27 +02:00
|
|
|
name = conf->no_parm_names ? NULL : parameter__name(pos);
|
2009-04-19 18:48:51 +02:00
|
|
|
type = cu__type(cu, pos->tag.type);
|
|
|
|
if (type == NULL) {
|
|
|
|
snprintf(sbf, sizeof(sbf),
|
|
|
|
"<ERROR: type %d not found>", pos->tag.type);
|
|
|
|
stype = sbf;
|
|
|
|
goto print_it;
|
|
|
|
}
|
2019-04-05 20:21:55 +02:00
|
|
|
if (tag__is_pointer(type)) {
|
2009-04-19 18:48:51 +02:00
|
|
|
if (type->type != 0) {
|
2009-09-14 22:07:02 +02:00
|
|
|
int n;
|
2009-04-19 18:48:51 +02:00
|
|
|
struct tag *ptype = cu__type(cu, type->type);
|
|
|
|
if (ptype == NULL) {
|
|
|
|
printed +=
|
|
|
|
tag__id_not_found_fprintf(fp, type->type);
|
|
|
|
continue;
|
|
|
|
}
|
2009-09-14 22:07:02 +02:00
|
|
|
n = tag__has_type_loop(type, ptype, NULL, 0, fp);
|
|
|
|
if (n)
|
|
|
|
return printed + n;
|
2009-04-19 18:48:51 +02:00
|
|
|
if (ptype->tag == DW_TAG_subroutine_type) {
|
|
|
|
printed +=
|
|
|
|
ftype__fprintf(tag__ftype(ptype),
|
|
|
|
cu, name, 0, 1, 0,
|
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>
2019-11-05 15:30:51 +01:00
|
|
|
true, conf, fp);
|
2009-04-19 18:48:51 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (type->tag == DW_TAG_subroutine_type) {
|
|
|
|
printed += ftype__fprintf(tag__ftype(type), cu, name,
|
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>
2019-11-05 15:30:51 +01:00
|
|
|
true, 0, 0, 0, conf, fp);
|
2009-04-19 18:48:51 +02:00
|
|
|
continue;
|
|
|
|
}
|
2009-08-24 22:22:43 +02:00
|
|
|
stype = tag__name(type, cu, sbf, sizeof(sbf), conf);
|
2009-04-19 18:48:51 +02:00
|
|
|
print_it:
|
|
|
|
printed += fprintf(fp, "%s%s%s", stype, name ? " " : "",
|
|
|
|
name ?: "");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* No parameters? */
|
|
|
|
if (first_parm)
|
|
|
|
printed += fprintf(fp, "void)");
|
2012-08-17 23:47:15 +02:00
|
|
|
else if (ftype->unspec_parms)
|
2009-04-19 18:48:51 +02:00
|
|
|
printed += fprintf(fp, ", ...)");
|
|
|
|
else
|
|
|
|
printed += fprintf(fp, ")");
|
|
|
|
return printed;
|
|
|
|
}
|
|
|
|
|
|
|
|
static size_t function__tag_fprintf(const struct tag *tag, const struct cu *cu,
|
|
|
|
struct function *function, uint16_t indent,
|
|
|
|
const struct conf_fprintf *conf, FILE *fp)
|
|
|
|
{
|
|
|
|
char bf[512];
|
|
|
|
size_t printed = 0, n;
|
|
|
|
const void *vtag = tag;
|
|
|
|
int c;
|
|
|
|
|
|
|
|
if (indent >= sizeof(tabs))
|
|
|
|
indent = sizeof(tabs) - 1;
|
|
|
|
c = indent * 8;
|
|
|
|
|
|
|
|
switch (tag->tag) {
|
|
|
|
case DW_TAG_inlined_subroutine: {
|
|
|
|
const struct inline_expansion *exp = vtag;
|
2009-06-04 22:30:06 +02:00
|
|
|
const struct tag *talias = cu__function(cu, exp->ip.tag.type);
|
2009-04-19 18:48:51 +02:00
|
|
|
struct function *alias = tag__function(talias);
|
|
|
|
const char *name;
|
|
|
|
|
|
|
|
if (alias == NULL) {
|
2009-06-04 22:30:06 +02:00
|
|
|
printed += tag__id_not_found_fprintf(fp, exp->ip.tag.type);
|
2009-04-19 18:48:51 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
printed = fprintf(fp, "%.*s", indent, tabs);
|
2021-06-25 19:11:30 +02:00
|
|
|
name = function__name(alias);
|
2009-04-19 18:48:51 +02:00
|
|
|
n = fprintf(fp, "%s", name);
|
|
|
|
size_t namelen = 0;
|
|
|
|
if (name != NULL)
|
|
|
|
namelen = strlen(name);
|
|
|
|
n += ftype__fprintf_parms(&alias->proto, cu,
|
|
|
|
indent + (namelen + 7) / 8,
|
|
|
|
conf, fp);
|
|
|
|
n += fprintf(fp, "; /* size=%zd, low_pc=%#llx */",
|
2009-06-04 22:30:06 +02:00
|
|
|
exp->size, (unsigned long long)exp->ip.addr);
|
2009-04-19 18:48:51 +02:00
|
|
|
#if 0
|
|
|
|
n = fprintf(fp, "%s(); /* size=%zd, low_pc=%#llx */",
|
2021-06-25 19:11:30 +02:00
|
|
|
function__name(alias), exp->size,
|
2009-06-04 22:30:06 +02:00
|
|
|
(unsigned long long)exp->ip.addr);
|
2009-04-19 18:48:51 +02:00
|
|
|
#endif
|
|
|
|
c = 69;
|
|
|
|
printed += n;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case DW_TAG_variable:
|
|
|
|
printed = fprintf(fp, "%.*s", indent, tabs);
|
2018-09-05 20:54:04 +02:00
|
|
|
n = fprintf(fp, "%s %s; /* scope: %s */",
|
2009-04-19 18:48:51 +02:00
|
|
|
variable__type_name(vtag, cu, bf, sizeof(bf)),
|
2021-06-30 15:07:14 +02:00
|
|
|
variable__name(vtag),
|
2018-09-05 20:54:04 +02:00
|
|
|
variable__scope_str(vtag));
|
2009-04-19 18:48:51 +02:00
|
|
|
c += n;
|
|
|
|
printed += n;
|
|
|
|
break;
|
|
|
|
case DW_TAG_label: {
|
|
|
|
const struct label *label = vtag;
|
|
|
|
printed = fprintf(fp, "%.*s", indent, tabs);
|
|
|
|
fputc('\n', fp);
|
|
|
|
++printed;
|
2021-06-24 15:01:27 +02:00
|
|
|
c = fprintf(fp, "%s:", label__name(label));
|
2009-04-19 18:48:51 +02:00
|
|
|
printed += c;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case DW_TAG_lexical_block:
|
|
|
|
printed = lexblock__fprintf(vtag, cu, function, indent,
|
|
|
|
conf, fp);
|
|
|
|
fputc('\n', fp);
|
|
|
|
return printed + 1;
|
|
|
|
default:
|
|
|
|
printed = fprintf(fp, "%.*s", indent, tabs);
|
|
|
|
n = fprintf(fp, "%s <%llx>", dwarf_tag_name(tag->tag),
|
|
|
|
tag__orig_id(tag, cu));
|
|
|
|
c += n;
|
|
|
|
printed += n;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return printed + fprintf(fp, "%-*.*s// %5u\n", 70 - c, 70 - c, " ",
|
|
|
|
tag__decl_line(tag, cu));
|
|
|
|
}
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
size_t lexblock__fprintf(const struct lexblock *block, const struct cu *cu,
|
2009-04-19 18:48:51 +02:00
|
|
|
struct function *function, uint16_t indent,
|
|
|
|
const struct conf_fprintf *conf, FILE *fp)
|
|
|
|
{
|
|
|
|
struct tag *pos;
|
|
|
|
size_t printed;
|
|
|
|
|
|
|
|
if (indent >= sizeof(tabs))
|
|
|
|
indent = sizeof(tabs) - 1;
|
|
|
|
printed = fprintf(fp, "%.*s{", indent, tabs);
|
2012-08-17 23:47:15 +02:00
|
|
|
if (block->ip.addr != 0) {
|
|
|
|
uint64_t offset = block->ip.addr - function->lexblock.ip.addr;
|
2009-04-19 18:48:51 +02:00
|
|
|
|
|
|
|
if (offset == 0)
|
|
|
|
printed += fprintf(fp, " /* low_pc=%#llx */",
|
2012-08-17 23:47:15 +02:00
|
|
|
(unsigned long long)block->ip.addr);
|
2009-04-19 18:48:51 +02:00
|
|
|
else
|
|
|
|
printed += fprintf(fp, " /* %s+%#llx */",
|
2021-06-25 19:11:30 +02:00
|
|
|
function__name(function),
|
2009-04-19 18:48:51 +02:00
|
|
|
(unsigned long long)offset);
|
|
|
|
}
|
|
|
|
printed += fprintf(fp, "\n");
|
2012-08-17 23:47:15 +02:00
|
|
|
list_for_each_entry(pos, &block->tags, node)
|
2009-04-19 18:48:51 +02:00
|
|
|
printed += function__tag_fprintf(pos, cu, function, indent + 1,
|
|
|
|
conf, fp);
|
|
|
|
printed += fprintf(fp, "%.*s}", indent, tabs);
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
if (function->lexblock.ip.addr != block->ip.addr)
|
|
|
|
printed += fprintf(fp, " /* lexblock size=%d */", block->size);
|
2009-04-19 18:48:51 +02:00
|
|
|
|
|
|
|
return printed;
|
|
|
|
}
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
size_t ftype__fprintf(const struct ftype *ftype, const struct cu *cu,
|
2009-04-19 18:48:51 +02:00
|
|
|
const char *name, const int inlined,
|
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>
2019-11-05 15:30:51 +01:00
|
|
|
const int is_pointer, int type_spacing, bool is_prototype,
|
2009-04-19 18:48:51 +02:00
|
|
|
const struct conf_fprintf *conf, FILE *fp)
|
|
|
|
{
|
2012-08-17 23:47:15 +02:00
|
|
|
struct tag *type = cu__type(cu, ftype->tag.type);
|
2009-04-19 18:48:51 +02:00
|
|
|
char sbf[128];
|
2009-08-24 22:22:43 +02:00
|
|
|
const char *stype = tag__name(type, cu, sbf, sizeof(sbf), conf);
|
2009-04-19 18:48:51 +02:00
|
|
|
size_t printed = fprintf(fp, "%s%-*s %s%s%s%s",
|
|
|
|
inlined ? "inline " : "",
|
|
|
|
type_spacing, stype,
|
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>
2019-11-05 15:30:51 +01:00
|
|
|
is_prototype ? "(" : "",
|
2009-04-19 18:48:51 +02:00
|
|
|
is_pointer ? "*" : "", name ?: "",
|
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>
2019-11-05 15:30:51 +01:00
|
|
|
is_prototype ? ")" : "");
|
2009-04-19 18:48:51 +02:00
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
return printed + ftype__fprintf_parms(ftype, cu, 0, conf, fp);
|
2009-04-19 18:48:51 +02:00
|
|
|
}
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
static size_t function__fprintf(const struct tag *tag, const struct cu *cu,
|
|
|
|
const struct conf_fprintf *conf, FILE *fp)
|
2009-04-19 18:48:51 +02:00
|
|
|
{
|
2012-08-17 23:47:15 +02:00
|
|
|
struct function *func = tag__function(tag);
|
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>
2019-11-05 15:30:51 +01:00
|
|
|
struct ftype *ftype = func->btf ? tag__ftype(cu__type(cu, func->proto.tag.type)) : &func->proto;
|
2009-04-19 18:48:51 +02:00
|
|
|
size_t printed = 0;
|
2019-04-16 20:00:44 +02:00
|
|
|
bool inlined = !conf->strip_inline && function__declared_inline(func);
|
2009-04-19 18:48:51 +02:00
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
if (func->virtuality == DW_VIRTUALITY_virtual ||
|
|
|
|
func->virtuality == DW_VIRTUALITY_pure_virtual)
|
2009-04-19 18:48:51 +02:00
|
|
|
printed += fprintf(fp, "virtual ");
|
|
|
|
|
2021-06-25 19:11:30 +02:00
|
|
|
printed += ftype__fprintf(ftype, cu, function__name(func),
|
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>
2019-11-05 15:30:51 +01:00
|
|
|
inlined, 0, 0, false, conf, fp);
|
2009-04-19 18:48:51 +02:00
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
if (func->virtuality == DW_VIRTUALITY_pure_virtual)
|
2009-04-19 18:48:51 +02:00
|
|
|
printed += fprintf(fp, " = 0");
|
|
|
|
|
|
|
|
return printed;
|
|
|
|
}
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
size_t function__fprintf_stats(const struct tag *tag, const struct cu *cu,
|
|
|
|
const struct conf_fprintf *conf, FILE *fp)
|
2009-04-19 18:48:51 +02:00
|
|
|
{
|
2012-08-17 23:47:15 +02:00
|
|
|
struct function *func = tag__function(tag);
|
|
|
|
size_t printed = lexblock__fprintf(&func->lexblock, cu, func, 0, conf, fp);
|
2009-04-19 18:48:51 +02:00
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
printed += fprintf(fp, "/* size: %d", function__size(func));
|
|
|
|
if (func->lexblock.nr_variables > 0)
|
2009-04-19 18:48:51 +02:00
|
|
|
printed += fprintf(fp, ", variables: %u",
|
2012-08-17 23:47:15 +02:00
|
|
|
func->lexblock.nr_variables);
|
|
|
|
if (func->lexblock.nr_labels > 0)
|
2009-04-19 18:48:51 +02:00
|
|
|
printed += fprintf(fp, ", goto labels: %u",
|
2012-08-17 23:47:15 +02:00
|
|
|
func->lexblock.nr_labels);
|
|
|
|
if (func->lexblock.nr_inline_expansions > 0)
|
2009-04-19 18:48:51 +02:00
|
|
|
printed += fprintf(fp, ", inline expansions: %u (%d bytes)",
|
2012-08-17 23:47:15 +02:00
|
|
|
func->lexblock.nr_inline_expansions,
|
|
|
|
func->lexblock.size_inline_expansions);
|
2009-04-19 18:48:51 +02:00
|
|
|
return printed + fprintf(fp, " */\n");
|
|
|
|
}
|
|
|
|
|
2016-06-29 22:27:51 +02:00
|
|
|
static size_t class__fprintf_cacheline_boundary(struct conf_fprintf *conf,
|
|
|
|
uint32_t offset,
|
|
|
|
FILE *fp)
|
2009-04-19 18:48:51 +02:00
|
|
|
{
|
2016-06-29 22:27:51 +02:00
|
|
|
int indent = conf->indent;
|
2021-10-28 14:27:08 +02:00
|
|
|
uint32_t cacheline = offset / conf->cacheline_size;
|
2009-04-19 18:48:51 +02:00
|
|
|
size_t printed = 0;
|
|
|
|
|
2016-06-29 22:27:51 +02:00
|
|
|
if (cacheline > *conf->cachelinep) {
|
2021-10-28 14:27:08 +02:00
|
|
|
const uint32_t cacheline_pos = offset % conf->cacheline_size;
|
2016-06-29 22:27:51 +02:00
|
|
|
const uint32_t cacheline_in_bytes = offset - cacheline_pos;
|
2009-04-19 18:48:51 +02:00
|
|
|
|
|
|
|
if (cacheline_pos == 0)
|
|
|
|
printed += fprintf(fp, "/* --- cacheline %u boundary "
|
2016-06-29 22:27:51 +02:00
|
|
|
"(%u bytes) --- */\n", cacheline,
|
2009-04-19 18:48:51 +02:00
|
|
|
cacheline_in_bytes);
|
|
|
|
else
|
|
|
|
printed += fprintf(fp, "/* --- cacheline %u boundary "
|
|
|
|
"(%u bytes) was %u bytes ago --- "
|
2016-06-29 22:27:51 +02:00
|
|
|
"*/\n", cacheline,
|
2009-04-19 18:48:51 +02:00
|
|
|
cacheline_in_bytes, cacheline_pos);
|
2016-06-29 22:27:51 +02:00
|
|
|
|
|
|
|
printed += fprintf(fp, "%.*s", indent, tabs);
|
|
|
|
|
|
|
|
*conf->cachelinep = cacheline;
|
2009-04-19 18:48:51 +02:00
|
|
|
}
|
|
|
|
return printed;
|
|
|
|
}
|
|
|
|
|
2021-06-30 15:07:14 +02:00
|
|
|
static size_t class__vtable_fprintf(struct class *class, const struct conf_fprintf *conf, FILE *fp)
|
2009-04-19 18:48:51 +02:00
|
|
|
{
|
|
|
|
struct function *pos;
|
|
|
|
size_t printed = 0;
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
if (class->nr_vtable_entries == 0)
|
2009-04-19 18:48:51 +02:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
printed += fprintf(fp, "%.*s/* vtable has %u entries: {\n",
|
2012-08-17 23:47:15 +02:00
|
|
|
conf->indent, tabs, class->nr_vtable_entries);
|
2009-04-19 18:48:51 +02:00
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
list_for_each_entry(pos, &class->vtable, vtable_node) {
|
2009-04-19 18:48:51 +02:00
|
|
|
printed += fprintf(fp, "%.*s [%d] = %s(%s), \n",
|
|
|
|
conf->indent, tabs, pos->vtable_entry,
|
2021-06-25 19:11:30 +02:00
|
|
|
function__name(pos),
|
2021-06-24 15:01:27 +02:00
|
|
|
function__linkage_name(pos));
|
2009-04-19 18:48:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
printed += fprintf(fp, "%.*s} */", conf->indent, tabs);
|
|
|
|
out:
|
|
|
|
return printed;
|
|
|
|
}
|
|
|
|
|
2016-06-29 21:19:20 +02:00
|
|
|
static size_t __class__fprintf(struct class *class, const struct cu *cu,
|
|
|
|
const struct conf_fprintf *conf, FILE *fp)
|
2009-04-19 18:48:51 +02:00
|
|
|
{
|
2012-08-17 23:47:15 +02:00
|
|
|
struct type *type = &class->type;
|
2009-04-19 18:48:51 +02:00
|
|
|
size_t last_size = 0, size;
|
|
|
|
uint8_t newline = 0;
|
|
|
|
uint16_t nr_paddings = 0;
|
fprintf: Show statistics about holes due to forced alignments
$ pahole -C task_struct | 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 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 */
/* forced alignments: 1, forced holes: 1, sum forced holes: 60 */
};
$
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>
2019-04-03 18:33:37 +02:00
|
|
|
uint16_t nr_forced_alignments = 0, nr_forced_alignment_holes = 0;
|
|
|
|
uint32_t sum_forced_alignment_holes = 0;
|
dwarves_fprintf: Count bitfield member sizes separately
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>
2019-03-26 20:53:30 +01:00
|
|
|
uint32_t sum_bytes = 0, sum_bits = 0;
|
2009-04-19 18:48:51 +02:00
|
|
|
uint32_t sum_holes = 0;
|
|
|
|
uint32_t sum_paddings = 0;
|
|
|
|
uint32_t sum_bit_holes = 0;
|
2016-06-29 22:27:51 +02:00
|
|
|
uint32_t cacheline = 0;
|
2019-03-18 05:23:40 +01:00
|
|
|
int size_diff = 0;
|
2009-04-19 18:48:51 +02:00
|
|
|
int first = 1;
|
|
|
|
struct class_member *pos, *last = NULL;
|
|
|
|
struct tag *tag_pos;
|
|
|
|
const char *current_accessibility = NULL;
|
|
|
|
struct conf_fprintf cconf = conf ? *conf : conf_fprintf__defaults;
|
2012-08-17 23:47:15 +02:00
|
|
|
const uint16_t t = type->namespace.tag.tag;
|
2009-04-19 18:48:51 +02:00
|
|
|
size_t printed = fprintf(fp, "%s%s%s%s%s",
|
|
|
|
cconf.prefix ?: "", cconf.prefix ? " " : "",
|
2012-02-26 15:33:05 +01:00
|
|
|
((cconf.classes_as_structs ||
|
2009-08-24 22:22:43 +02:00
|
|
|
t == DW_TAG_structure_type) ? "struct" :
|
|
|
|
t == DW_TAG_class_type ? "class" :
|
|
|
|
"interface"),
|
2021-06-25 01:41:37 +02:00
|
|
|
type__name(type) ? " " : "",
|
|
|
|
type__name(type) ?: "");
|
2009-04-19 18:48:51 +02:00
|
|
|
int indent = cconf.indent;
|
|
|
|
|
|
|
|
if (indent >= (int)sizeof(tabs))
|
|
|
|
indent = sizeof(tabs) - 1;
|
|
|
|
|
2016-06-29 22:27:51 +02:00
|
|
|
if (cconf.cachelinep == NULL)
|
|
|
|
cconf.cachelinep = &cacheline;
|
|
|
|
|
2009-04-19 18:48:51 +02:00
|
|
|
cconf.indent = indent + 1;
|
|
|
|
cconf.no_semicolon = 0;
|
|
|
|
|
core: Infer if a struct is packed by the offsets/natural alignments
As DWARF (nor BTF) provides explicit attributes, we need to look at the
natural alignments, a byte is always alignted, etc.
This probably fails with things like __attribute__(__aligned(power-of-two)),
but with it most of the kernel data structures are full circled, i.e.
'pfunct --compile' regenerates source code from debug info that when
compiled generats debug info that end up matching the original sources.
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
const char * uptr;
int refcnt;
};
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
const char * uptr; /* 8 8 */
int refcnt; /* 16 4 */
/* size: 24, cachelines: 1, members: 3 */
/* padding: 4 */
/* last cacheline: 24 bytes */
};
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
const char * uptr;
int refcnt;
} __packed;
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
const char * uptr; /* 8 8 */
int refcnt; /* 16 4 */
/* size: 20, cachelines: 1, members: 3 */
/* last cacheline: 20 bytes */
} __attribute__((__packed__));
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
int refcnt;
const char * uptr;
};
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
int refcnt; /* 8 4 */
/* XXX 4 bytes hole, try to pack */
const char * uptr; /* 16 8 */
/* size: 24, cachelines: 1, members: 3 */
/* sum members: 20, holes: 1, sum holes: 4 */
/* last cacheline: 24 bytes */
};
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
int refcnt;
const char * uptr;
} __packed;
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
int refcnt; /* 8 4 */
const char * uptr; /* 12 8 */
/* size: 20, cachelines: 1, members: 3 */
/* last cacheline: 20 bytes */
} __attribute__((__packed__));
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
const char * uptr;
unsigned char refcnt;
};
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
const char * uptr; /* 8 8 */
unsigned char refcnt; /* 16 1 */
/* size: 24, cachelines: 1, members: 3 */
/* padding: 7 */
/* last cacheline: 24 bytes */
};
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
const char * uptr;
unsigned char refcnt;
} __packed;
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
const char * uptr; /* 8 8 */
unsigned char refcnt; /* 16 1 */
/* size: 17, cachelines: 1, members: 3 */
/* last cacheline: 17 bytes */
} __attribute__((__packed__));
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
unsigned char refcnt;
const char * uptr;
};
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
unsigned char refcnt; /* 8 1 */
/* XXX 7 bytes hole, try to pack */
const char * uptr; /* 16 8 */
/* size: 24, cachelines: 1, members: 3 */
/* sum members: 17, holes: 1, sum holes: 7 */
/* last cacheline: 24 bytes */
};
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
unsigned char refcnt;
const char * uptr;
} __packed;
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
unsigned char refcnt; /* 8 1 */
const char * uptr; /* 9 8 */
/* size: 17, cachelines: 1, members: 3 */
/* last cacheline: 17 bytes */
} __attribute__((__packed__));
$
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-10 22:41:59 +02:00
|
|
|
class__infer_packed_attributes(class, cu);
|
|
|
|
|
2009-04-19 18:48:51 +02:00
|
|
|
/* First look if we have DW_TAG_inheritance */
|
2012-08-17 23:47:15 +02:00
|
|
|
type__for_each_tag(type, tag_pos) {
|
2009-04-19 18:48:51 +02:00
|
|
|
const char *accessibility;
|
|
|
|
|
|
|
|
if (tag_pos->tag != DW_TAG_inheritance)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (first) {
|
|
|
|
printed += fprintf(fp, " :");
|
|
|
|
first = 0;
|
|
|
|
} else
|
|
|
|
printed += fprintf(fp, ",");
|
|
|
|
|
|
|
|
pos = tag__class_member(tag_pos);
|
|
|
|
|
|
|
|
if (pos->virtuality == DW_VIRTUALITY_virtual)
|
|
|
|
printed += fprintf(fp, " virtual");
|
|
|
|
|
|
|
|
accessibility = tag__accessibility(tag_pos);
|
|
|
|
if (accessibility != NULL)
|
|
|
|
printed += fprintf(fp, " %s", accessibility);
|
|
|
|
|
2019-04-15 21:30:15 +02:00
|
|
|
struct tag *pos_type = cu__type(cu, tag_pos->type);
|
|
|
|
if (pos_type != NULL)
|
2009-04-19 18:48:51 +02:00
|
|
|
printed += fprintf(fp, " %s",
|
2021-06-25 01:41:37 +02:00
|
|
|
type__name(tag__type(pos_type)));
|
2009-04-19 18:48:51 +02:00
|
|
|
else
|
|
|
|
printed += tag__id_not_found_fprintf(fp, tag_pos->type);
|
|
|
|
}
|
|
|
|
|
|
|
|
printed += fprintf(fp, " {\n");
|
|
|
|
|
2019-04-03 09:01:45 +02:00
|
|
|
if (class->pre_bit_hole > 0 && !cconf.suppress_comments) {
|
|
|
|
if (!newline++) {
|
|
|
|
fputc('\n', fp);
|
|
|
|
++printed;
|
|
|
|
}
|
|
|
|
printed += fprintf(fp, "%.*s/* XXX %d bit%s hole, "
|
|
|
|
"try to pack */\n", cconf.indent, tabs,
|
|
|
|
class->pre_bit_hole,
|
|
|
|
class->pre_bit_hole != 1 ? "s" : "");
|
|
|
|
sum_bit_holes += class->pre_bit_hole;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (class->pre_hole > 0 && !cconf.suppress_comments) {
|
|
|
|
if (!newline++) {
|
|
|
|
fputc('\n', fp);
|
|
|
|
++printed;
|
|
|
|
}
|
|
|
|
printed += fprintf(fp, "%.*s/* XXX %d byte%s hole, "
|
|
|
|
"try to pack */\n",
|
|
|
|
cconf.indent, tabs, class->pre_hole,
|
|
|
|
class->pre_hole != 1 ? "s" : "");
|
|
|
|
sum_holes += class->pre_hole;
|
|
|
|
}
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
type__for_each_tag(type, tag_pos) {
|
2009-04-19 18:48:51 +02:00
|
|
|
const char *accessibility = tag__accessibility(tag_pos);
|
|
|
|
|
|
|
|
if (accessibility != NULL &&
|
|
|
|
accessibility != current_accessibility) {
|
|
|
|
current_accessibility = accessibility;
|
|
|
|
printed += fprintf(fp, "%.*s%s:\n\n",
|
|
|
|
cconf.indent - 1, tabs,
|
|
|
|
accessibility);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tag_pos->tag != DW_TAG_member &&
|
|
|
|
tag_pos->tag != DW_TAG_inheritance) {
|
|
|
|
if (!cconf.show_only_data_members) {
|
|
|
|
printed += tag__fprintf(tag_pos, cu, &cconf, fp);
|
|
|
|
printed += fprintf(fp, "\n\n");
|
|
|
|
}
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
pos = tag__class_member(tag_pos);
|
2019-04-03 18:22:37 +02:00
|
|
|
|
fprintf: Show statistics about holes due to forced alignments
$ pahole -C task_struct | 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 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 */
/* forced alignments: 1, forced holes: 1, sum forced holes: 60 */
};
$
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>
2019-04-03 18:33:37 +02:00
|
|
|
if (!cconf.suppress_aligned_attribute && pos->alignment != 0) {
|
|
|
|
uint32_t forced_alignment_hole = last ? last->hole : class->pre_hole;
|
|
|
|
|
|
|
|
if (forced_alignment_hole != 0) {
|
|
|
|
++nr_forced_alignment_holes;
|
|
|
|
sum_forced_alignment_holes += forced_alignment_hole;
|
|
|
|
}
|
2019-04-03 18:22:37 +02:00
|
|
|
++nr_forced_alignments;
|
fprintf: Show statistics about holes due to forced alignments
$ pahole -C task_struct | 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 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 */
/* forced alignments: 1, forced holes: 1, sum forced holes: 60 */
};
$
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>
2019-04-03 18:33:37 +02:00
|
|
|
}
|
2009-04-19 18:48:51 +02:00
|
|
|
/*
|
|
|
|
* These paranoid checks doesn't make much sense on
|
|
|
|
* DW_TAG_inheritance, have to understand why virtual public
|
|
|
|
* ancestors make the offset go backwards...
|
|
|
|
*/
|
2009-08-10 17:31:47 +02:00
|
|
|
if (last != NULL && tag_pos->tag == DW_TAG_member &&
|
|
|
|
/*
|
|
|
|
* kmemcheck bitfield tricks use zero sized arrays as markers
|
|
|
|
* all over the place.
|
|
|
|
*/
|
|
|
|
last_size != 0) {
|
fprintf: Notice explicit bitfield alignment modifications
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>
2019-04-09 20:44:42 +02:00
|
|
|
if (last->bit_hole != 0 && pos->bitfield_size) {
|
2019-04-10 23:11:55 +02:00
|
|
|
uint8_t bitfield_size = last->bit_hole;
|
2019-04-15 21:30:15 +02:00
|
|
|
struct tag *pos_type = cu__type(cu, pos->tag.type);
|
2019-04-10 23:11:55 +02:00
|
|
|
|
2019-04-15 21:30:15 +02:00
|
|
|
if (pos_type == NULL) {
|
fprintf: Notice explicit bitfield alignment modifications
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>
2019-04-09 20:44:42 +02:00
|
|
|
printed += fprintf(fp, "%.*s", cconf.indent, tabs);
|
|
|
|
printed += tag__id_not_found_fprintf(fp, pos->tag.type);
|
|
|
|
continue;
|
|
|
|
}
|
2019-04-10 23:11:55 +02:00
|
|
|
/*
|
|
|
|
* Now check if this isn't something like 'unsigned :N' with N > 0,
|
|
|
|
* i.e. _explicitely_ adding a bit hole.
|
|
|
|
*/
|
|
|
|
if (last->byte_offset != pos->byte_offset) {
|
|
|
|
printed += fprintf(fp, "\n%.*s/* Force alignment to the next boundary: */\n", cconf.indent, tabs);
|
|
|
|
bitfield_size = 0;
|
|
|
|
}
|
|
|
|
|
fprintf: Notice explicit bitfield alignment modifications
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>
2019-04-09 20:44:42 +02:00
|
|
|
printed += fprintf(fp, "%.*s", cconf.indent, tabs);
|
2019-04-15 21:30:15 +02:00
|
|
|
printed += type__fprintf(pos_type, cu, "", &cconf, fp);
|
2019-04-10 23:11:55 +02:00
|
|
|
printed += fprintf(fp, ":%u;\n", bitfield_size);
|
fprintf: Notice explicit bitfield alignment modifications
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>
2019-04-09 20:44:42 +02:00
|
|
|
}
|
|
|
|
|
2009-04-19 18:48:51 +02:00
|
|
|
if (pos->byte_offset < last->byte_offset ||
|
|
|
|
(pos->byte_offset == last->byte_offset &&
|
|
|
|
last->bitfield_size == 0 &&
|
|
|
|
/*
|
|
|
|
* This is just when transitioning from a non-bitfield to
|
|
|
|
* a bitfield, think about zero sized arrays in the middle
|
|
|
|
* of a struct.
|
|
|
|
*/
|
|
|
|
pos->bitfield_size != 0)) {
|
|
|
|
if (!cconf.suppress_comments) {
|
|
|
|
if (!newline++) {
|
|
|
|
fputc('\n', fp);
|
|
|
|
++printed;
|
|
|
|
}
|
|
|
|
printed += fprintf(fp, "%.*s/* Bitfield combined"
|
|
|
|
" with previous fields */\n",
|
|
|
|
cconf.indent, tabs);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
const ssize_t cc_last_size = ((ssize_t)pos->byte_offset -
|
|
|
|
(ssize_t)last->byte_offset);
|
|
|
|
|
|
|
|
if (cc_last_size > 0 &&
|
|
|
|
(size_t)cc_last_size < last_size) {
|
|
|
|
if (!cconf.suppress_comments) {
|
|
|
|
if (!newline++) {
|
|
|
|
fputc('\n', fp);
|
|
|
|
++printed;
|
|
|
|
}
|
|
|
|
printed += fprintf(fp, "%.*s/* Bitfield combined"
|
|
|
|
" with next fields */\n",
|
|
|
|
cconf.indent, tabs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (newline) {
|
|
|
|
fputc('\n', fp);
|
|
|
|
newline = 0;
|
|
|
|
++printed;
|
|
|
|
}
|
|
|
|
|
2019-04-15 21:30:15 +02:00
|
|
|
struct tag *pos_type = cu__type(cu, pos->tag.type);
|
|
|
|
if (pos_type == NULL) {
|
2009-04-19 18:48:51 +02:00
|
|
|
printed += fprintf(fp, "%.*s", cconf.indent, tabs);
|
|
|
|
printed += tag__id_not_found_fprintf(fp, pos->tag.type);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
fprintf: Deal with zero sized arrays in the middle of a struct
Consider:
struct ipc64_perm {
__kernel_key_t key;
__kernel_uid32_t uid;
__kernel_gid32_t gid;
__kernel_uid32_t cuid;
__kernel_gid32_t cgid;
__kernel_mode_t mode;
/* pad if mode_t is u16: */
unsigned char __pad1[4 - sizeof(__kernel_mode_t)];
unsigned short seq;
unsigned short __pad2;
__kernel_ulong_t __unused1;
__kernel_ulong_t __unused2;
};
That is a roundabout way of using __attribute__(__aligned__(4)), but
should work nonetheless.
We were not putting the [0] in that zero sized array which ended up
making gcc complain with:
$ gcc -g -c shm.c
shm.c:199:29: error: flexible array member not at end of struct
unsigned char __pad1[]; /* 24 0 */
^~~~~~
$
Now this works, i.e. generates compilable source code out of the
type tags, be it from BTF or from DWARF, i.e. this is all from the
internal representation of such types, agnostic wrt the original type
format.
So, the full circle:
$ pahole -C ipc64_perm /home/acme/git/build/v5.1-rc4+/ipc/shm.o
struct ipc64_perm {
__kernel_key_t key; /* 0 4 */
__kernel_uid32_t uid; /* 4 4 */
__kernel_gid32_t gid; /* 8 4 */
__kernel_uid32_t cuid; /* 12 4 */
__kernel_gid32_t cgid; /* 16 4 */
__kernel_mode_t mode; /* 20 4 */
unsigned char __pad1[0]; /* 24 0 */
short unsigned int seq; /* 24 2 */
short unsigned int __pad2; /* 26 2 */
/* XXX 4 bytes hole, try to pack */
__kernel_ulong_t __unused1; /* 32 8 */
__kernel_ulong_t __unused2; /* 40 8 */
/* size: 48, cachelines: 1, members: 11 */
/* sum members: 44, holes: 1, sum holes: 4 */
/* last cacheline: 48 bytes */
};
$ pfunct --compile /home/acme/git/build/v5.1-rc4+/ipc/shm.o > shm.c
$ gcc -g -c shm.c
$ pahole -C ipc64_perm shm.o
struct ipc64_perm {
__kernel_key_t key; /* 0 4 */
__kernel_uid32_t uid; /* 4 4 */
__kernel_gid32_t gid; /* 8 4 */
__kernel_uid32_t cuid; /* 12 4 */
__kernel_gid32_t cgid; /* 16 4 */
__kernel_mode_t mode; /* 20 4 */
unsigned char __pad1[0]; /* 24 0 */
short unsigned int seq; /* 24 2 */
short unsigned int __pad2; /* 26 2 */
/* XXX 4 bytes hole, try to pack */
__kernel_ulong_t __unused1; /* 32 8 */
__kernel_ulong_t __unused2; /* 40 8 */
/* size: 48, cachelines: 1, members: 11 */
/* sum members: 44, holes: 1, sum holes: 4 */
/* last cacheline: 48 bytes */
};
$
And for a chuckle, the original source code with a bit of history about
struct layout worries:
include/uapi/asm-generic/ipcbuf.h:
/*
* The generic ipc64_perm structure:
* Note extra padding because this structure is passed back and forth
* between kernel and user space.
*
* ipc64_perm was originally meant to be architecture specific, but
* everyone just ended up making identical copies without specific
* optimizations, so we may just as well all use the same one.
*
* Pad space is left for:
* - 32-bit mode_t on architectures that only had 16 bit
* - 32-bit seq
* - 2 miscellaneous 32-bit values
*/
struct ipc64_perm {
__kernel_key_t key;
__kernel_uid32_t uid;
__kernel_gid32_t gid;
__kernel_uid32_t cuid;
__kernel_gid32_t cgid;
__kernel_mode_t mode;
/* pad if mode_t is u16: */
unsigned char __pad1[4 - sizeof(__kernel_mode_t)];
unsigned short seq;
unsigned short __pad2;
__kernel_ulong_t __unused1;
__kernel_ulong_t __unused2;
};
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-15 21:33:51 +02:00
|
|
|
cconf.last_member = list_is_last(&tag_pos->node, &type->namespace.tags);
|
2019-04-15 22:20:11 +02:00
|
|
|
cconf.first_member = last == NULL;
|
fprintf: Deal with zero sized arrays in the middle of a struct
Consider:
struct ipc64_perm {
__kernel_key_t key;
__kernel_uid32_t uid;
__kernel_gid32_t gid;
__kernel_uid32_t cuid;
__kernel_gid32_t cgid;
__kernel_mode_t mode;
/* pad if mode_t is u16: */
unsigned char __pad1[4 - sizeof(__kernel_mode_t)];
unsigned short seq;
unsigned short __pad2;
__kernel_ulong_t __unused1;
__kernel_ulong_t __unused2;
};
That is a roundabout way of using __attribute__(__aligned__(4)), but
should work nonetheless.
We were not putting the [0] in that zero sized array which ended up
making gcc complain with:
$ gcc -g -c shm.c
shm.c:199:29: error: flexible array member not at end of struct
unsigned char __pad1[]; /* 24 0 */
^~~~~~
$
Now this works, i.e. generates compilable source code out of the
type tags, be it from BTF or from DWARF, i.e. this is all from the
internal representation of such types, agnostic wrt the original type
format.
So, the full circle:
$ pahole -C ipc64_perm /home/acme/git/build/v5.1-rc4+/ipc/shm.o
struct ipc64_perm {
__kernel_key_t key; /* 0 4 */
__kernel_uid32_t uid; /* 4 4 */
__kernel_gid32_t gid; /* 8 4 */
__kernel_uid32_t cuid; /* 12 4 */
__kernel_gid32_t cgid; /* 16 4 */
__kernel_mode_t mode; /* 20 4 */
unsigned char __pad1[0]; /* 24 0 */
short unsigned int seq; /* 24 2 */
short unsigned int __pad2; /* 26 2 */
/* XXX 4 bytes hole, try to pack */
__kernel_ulong_t __unused1; /* 32 8 */
__kernel_ulong_t __unused2; /* 40 8 */
/* size: 48, cachelines: 1, members: 11 */
/* sum members: 44, holes: 1, sum holes: 4 */
/* last cacheline: 48 bytes */
};
$ pfunct --compile /home/acme/git/build/v5.1-rc4+/ipc/shm.o > shm.c
$ gcc -g -c shm.c
$ pahole -C ipc64_perm shm.o
struct ipc64_perm {
__kernel_key_t key; /* 0 4 */
__kernel_uid32_t uid; /* 4 4 */
__kernel_gid32_t gid; /* 8 4 */
__kernel_uid32_t cuid; /* 12 4 */
__kernel_gid32_t cgid; /* 16 4 */
__kernel_mode_t mode; /* 20 4 */
unsigned char __pad1[0]; /* 24 0 */
short unsigned int seq; /* 24 2 */
short unsigned int __pad2; /* 26 2 */
/* XXX 4 bytes hole, try to pack */
__kernel_ulong_t __unused1; /* 32 8 */
__kernel_ulong_t __unused2; /* 40 8 */
/* size: 48, cachelines: 1, members: 11 */
/* sum members: 44, holes: 1, sum holes: 4 */
/* last cacheline: 48 bytes */
};
$
And for a chuckle, the original source code with a bit of history about
struct layout worries:
include/uapi/asm-generic/ipcbuf.h:
/*
* The generic ipc64_perm structure:
* Note extra padding because this structure is passed back and forth
* between kernel and user space.
*
* ipc64_perm was originally meant to be architecture specific, but
* everyone just ended up making identical copies without specific
* optimizations, so we may just as well all use the same one.
*
* Pad space is left for:
* - 32-bit mode_t on architectures that only had 16 bit
* - 32-bit seq
* - 2 miscellaneous 32-bit values
*/
struct ipc64_perm {
__kernel_key_t key;
__kernel_uid32_t uid;
__kernel_gid32_t gid;
__kernel_uid32_t cuid;
__kernel_gid32_t cgid;
__kernel_mode_t mode;
/* pad if mode_t is u16: */
unsigned char __pad1[4 - sizeof(__kernel_mode_t)];
unsigned short seq;
unsigned short __pad2;
__kernel_ulong_t __unused1;
__kernel_ulong_t __unused2;
};
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-15 21:33:51 +02:00
|
|
|
|
2009-04-19 18:48:51 +02:00
|
|
|
size = pos->byte_size;
|
|
|
|
printed += fprintf(fp, "%.*s", cconf.indent, tabs);
|
2019-04-15 21:30:15 +02:00
|
|
|
printed += struct_member__fprintf(pos, pos_type, cu, &cconf, fp);
|
2009-04-19 18:48:51 +02:00
|
|
|
|
2019-04-15 21:30:15 +02:00
|
|
|
if (tag__is_struct(pos_type) && !cconf.suppress_comments) {
|
|
|
|
struct class *tclass = tag__class(pos_type);
|
2016-06-30 21:13:46 +02:00
|
|
|
uint16_t padding;
|
|
|
|
/*
|
|
|
|
* We may not yet have looked for holes and paddings
|
|
|
|
* in this member's struct type.
|
|
|
|
*/
|
|
|
|
class__find_holes(tclass);
|
core: Infer if a struct is packed by the offsets/natural alignments
As DWARF (nor BTF) provides explicit attributes, we need to look at the
natural alignments, a byte is always alignted, etc.
This probably fails with things like __attribute__(__aligned(power-of-two)),
but with it most of the kernel data structures are full circled, i.e.
'pfunct --compile' regenerates source code from debug info that when
compiled generats debug info that end up matching the original sources.
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
const char * uptr;
int refcnt;
};
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
const char * uptr; /* 8 8 */
int refcnt; /* 16 4 */
/* size: 24, cachelines: 1, members: 3 */
/* padding: 4 */
/* last cacheline: 24 bytes */
};
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
const char * uptr;
int refcnt;
} __packed;
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
const char * uptr; /* 8 8 */
int refcnt; /* 16 4 */
/* size: 20, cachelines: 1, members: 3 */
/* last cacheline: 20 bytes */
} __attribute__((__packed__));
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
int refcnt;
const char * uptr;
};
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
int refcnt; /* 8 4 */
/* XXX 4 bytes hole, try to pack */
const char * uptr; /* 16 8 */
/* size: 24, cachelines: 1, members: 3 */
/* sum members: 20, holes: 1, sum holes: 4 */
/* last cacheline: 24 bytes */
};
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
int refcnt;
const char * uptr;
} __packed;
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
int refcnt; /* 8 4 */
const char * uptr; /* 12 8 */
/* size: 20, cachelines: 1, members: 3 */
/* last cacheline: 20 bytes */
} __attribute__((__packed__));
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
const char * uptr;
unsigned char refcnt;
};
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
const char * uptr; /* 8 8 */
unsigned char refcnt; /* 16 1 */
/* size: 24, cachelines: 1, members: 3 */
/* padding: 7 */
/* last cacheline: 24 bytes */
};
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
const char * uptr;
unsigned char refcnt;
} __packed;
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
const char * uptr; /* 8 8 */
unsigned char refcnt; /* 16 1 */
/* size: 17, cachelines: 1, members: 3 */
/* last cacheline: 17 bytes */
} __attribute__((__packed__));
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
unsigned char refcnt;
const char * uptr;
};
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
unsigned char refcnt; /* 8 1 */
/* XXX 7 bytes hole, try to pack */
const char * uptr; /* 16 8 */
/* size: 24, cachelines: 1, members: 3 */
/* sum members: 17, holes: 1, sum holes: 7 */
/* last cacheline: 24 bytes */
};
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
unsigned char refcnt;
const char * uptr;
} __packed;
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
unsigned char refcnt; /* 8 1 */
const char * uptr; /* 9 8 */
/* size: 17, cachelines: 1, members: 3 */
/* last cacheline: 17 bytes */
} __attribute__((__packed__));
$
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-10 22:41:59 +02:00
|
|
|
class__infer_packed_attributes(tclass, cu);
|
2016-06-30 21:13:46 +02:00
|
|
|
|
|
|
|
padding = tclass->padding;
|
2009-04-19 18:48:51 +02:00
|
|
|
if (padding > 0) {
|
|
|
|
++nr_paddings;
|
|
|
|
sum_paddings += padding;
|
|
|
|
if (!newline++) {
|
|
|
|
fputc('\n', fp);
|
|
|
|
++printed;
|
|
|
|
}
|
|
|
|
|
|
|
|
printed += fprintf(fp, "\n%.*s/* XXX last "
|
|
|
|
"struct has %d byte%s of "
|
|
|
|
"padding */", cconf.indent,
|
|
|
|
tabs, padding,
|
|
|
|
padding != 1 ? "s" : "");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-03 09:01:45 +02:00
|
|
|
if (pos->bit_hole != 0 && !cconf.suppress_comments) {
|
|
|
|
if (!newline++) {
|
|
|
|
fputc('\n', fp);
|
|
|
|
++printed;
|
|
|
|
}
|
|
|
|
printed += fprintf(fp, "\n%.*s/* XXX %d bit%s hole, "
|
|
|
|
"try to pack */", cconf.indent, tabs,
|
|
|
|
pos->bit_hole,
|
|
|
|
pos->bit_hole != 1 ? "s" : "");
|
|
|
|
sum_bit_holes += pos->bit_hole;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pos->hole > 0 && !cconf.suppress_comments) {
|
|
|
|
if (!newline++) {
|
|
|
|
fputc('\n', fp);
|
|
|
|
++printed;
|
|
|
|
}
|
|
|
|
printed += fprintf(fp, "\n%.*s/* XXX %d byte%s hole, "
|
|
|
|
"try to pack */",
|
|
|
|
cconf.indent, tabs, pos->hole,
|
|
|
|
pos->hole != 1 ? "s" : "");
|
|
|
|
sum_holes += pos->hole;
|
|
|
|
}
|
|
|
|
|
2009-04-19 18:48:51 +02:00
|
|
|
fputc('\n', fp);
|
|
|
|
++printed;
|
|
|
|
|
|
|
|
/* XXX for now just skip these */
|
2013-03-20 14:38:03 +01:00
|
|
|
if (tag_pos->tag == DW_TAG_inheritance)
|
2009-04-19 18:48:51 +02:00
|
|
|
continue;
|
2013-03-20 14:38:03 +01:00
|
|
|
#if 0
|
|
|
|
/*
|
|
|
|
* This one was being skipped but caused problems with:
|
|
|
|
* http://article.gmane.org/gmane.comp.debugging.dwarves/185
|
|
|
|
* http://www.spinics.net/lists/dwarves/msg00119.html
|
|
|
|
*/
|
|
|
|
if (pos->virtuality == DW_VIRTUALITY_virtual)
|
|
|
|
continue;
|
|
|
|
#endif
|
2019-03-18 05:23:40 +01:00
|
|
|
|
dwarves_fprintf: Count bitfield member sizes separately
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>
2019-03-26 20:53:30 +01:00
|
|
|
if (pos->bitfield_size) {
|
|
|
|
sum_bits += pos->bitfield_size;
|
|
|
|
} else {
|
|
|
|
sum_bytes += pos->byte_size;
|
|
|
|
}
|
2009-04-19 18:48:51 +02:00
|
|
|
|
|
|
|
if (last == NULL || /* First member */
|
|
|
|
/*
|
|
|
|
* Last member was a zero sized array, typedef, struct, etc
|
|
|
|
*/
|
|
|
|
last_size == 0 ||
|
|
|
|
/*
|
|
|
|
* We moved to a new offset
|
|
|
|
*/
|
|
|
|
last->byte_offset != pos->byte_offset) {
|
|
|
|
last_size = size;
|
|
|
|
} else if (last->bitfield_size == 0 && pos->bitfield_size != 0) {
|
|
|
|
/*
|
|
|
|
* Transitioned from from a non-bitfield to a
|
|
|
|
* bitfield sharing the same offset
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* Compensate by removing the size of the
|
|
|
|
* last member that is "inside" this new
|
|
|
|
* member at the same offset.
|
|
|
|
*
|
|
|
|
* E.g.:
|
|
|
|
* struct foo {
|
|
|
|
* u8 a; / 0 1 /
|
|
|
|
* int b:1; / 0:23 4 /
|
|
|
|
* }
|
|
|
|
*/
|
|
|
|
last_size = size;
|
|
|
|
}
|
|
|
|
|
|
|
|
last = pos;
|
|
|
|
}
|
|
|
|
|
2019-04-15 18:55:34 +02:00
|
|
|
/*
|
|
|
|
* BTF doesn't have alignment info, for now use this infor from the loader
|
|
|
|
* to avoid adding the forced bitfield paddings and have btfdiff happy.
|
|
|
|
*/
|
2019-05-04 14:04:46 +02:00
|
|
|
if (class->padding != 0 && type->alignment == 0 && cconf.has_alignment_info &&
|
fprintf: Fixup handling classes with no members
Will Cohen reported this NULL pointer dereference while processing some
object linking with cuda:
#0 0x00007ffff7f91453 in __class__fprintf (class=0x522560, cu=0x40ff80, conf=0x7fffffffa930, fp=0x7ffff7ece780 <_IO_2_1_stdout_>)
at /home/acme/git/pahole/dwarves_fprintf.c:1624
#1 0x00007ffff7f92195 in tag__fprintf (tag=0x522560, cu=0x40ff80, conf=0x7fffffffa930, fp=0x7ffff7ece780 <_IO_2_1_stdout_>)
at /home/acme/git/pahole/dwarves_fprintf.c:1835
#2 0x00007ffff7f90b57 in __class__fprintf (class=0x5224c0, cu=0x40ff80, conf=0x7fffffffaaa0, fp=0x7ffff7ece780 <_IO_2_1_stdout_>)
at /home/acme/git/pahole/dwarves_fprintf.c:1406
#3 0x00007ffff7f92195 in tag__fprintf (tag=0x5224c0, cu=0x40ff80, conf=0x40a200 <conf>, fp=0x7ffff7ece780 <_IO_2_1_stdout_>)
at /home/acme/git/pahole/dwarves_fprintf.c:1835
#4 0x0000000000402d03 in class_formatter (class=0x5224c0, cu=0x40ff80, id=1257) at /home/acme/git/pahole/pahole.c:224
#5 0x0000000000403074 in print_classes (cu=0x40ff80) at /home/acme/git/pahole/pahole.c:319
#6 0x0000000000404bb2 in pahole_stealer (cu=0x40ff80, conf_load=0x40a240 <conf_load>) at /home/acme/git/pahole/pahole.c:1174
#7 0x00007ffff7f9ff73 in finalize_cu (cus=0x40b2b0, cu=0x40ff80, dcu=0x7fffffffacf0, conf=0x40a240 <conf_load>)
at /home/acme/git/pahole/dwarf_loader.c:2227
#8 0x00007ffff7f9ffac in finalize_cu_immediately (cus=0x40b2b0, cu=0x40ff80, dcu=0x7fffffffacf0, conf=0x40a240 <conf_load>)
at /home/acme/git/pahole/dwarf_loader.c:2236
#9 0x00007ffff7fa064c in cus__load_module (cus=0x40b2b0, conf=0x40a240 <conf_load>, mod=0x40d760, dw=0x40e980, elf=0x40b360,
filename=0x7fffffffd5e3 "examples/wcohen/02_Exercise.cuda") at /home/acme/git/pahole/dwarf_loader.c:2389
#10 0x00007ffff7fa0760 in cus__process_dwflmod (dwflmod=0x40d760, userdata=0x40d770, name=0x40d910 "examples/wcohen/02_Exercise.cuda",
base=4194304, arg=0x7fffffffcf10) at /home/acme/git/pahole/dwarf_loader.c:2434
#11 0x00007ffff7f32be1 in dwfl_getmodules () from /lib64/libdw.so.1
#12 0x00007ffff7fa0820 in cus__process_file (cus=0x40b2b0, conf=0x40a240 <conf_load>, fd=3,
filename=0x7fffffffd5e3 "examples/wcohen/02_Exercise.cuda") at /home/acme/git/pahole/dwarf_loader.c:2487
#13 0x00007ffff7fa089c in dwarf__load_file (cus=0x40b2b0, conf=0x40a240 <conf_load>, filename=0x7fffffffd5e3 "examples/wcohen/02_Exercise.cuda")
at /home/acme/git/pahole/dwarf_loader.c:2504
#14 0x00007ffff7f8b0dd in cus__load_file (cus=0x40b2b0, conf=0x40a240 <conf_load>, filename=0x7fffffffd5e3 "examples/wcohen/02_Exercise.cuda")
at /home/acme/git/pahole/dwarves.c:1745
#15 0x00007ffff7f8bc2a in cus__load_files (cus=0x40b2b0, conf=0x40a240 <conf_load>, filenames=0x7fffffffd150)
at /home/acme/git/pahole/dwarves.c:2109
#16 0x0000000000404ff0 in main (argc=2, argv=0x7fffffffd148) at /home/acme/git/pahole/pahole.c:1294
(gdb)
(gdb) p class__name(class, cu)
$6 = 0x5cbb85 "__nv_hdl_helper_trait<__nv_dl_tag<int (*)(int, char**), main, 1u>, void (main(int, char**)::__lambda0::*)(int, double&)const>"
(gdb) p class->type.nr_members
$7 = 0
(gdb) p last
$8 = (struct class_member *) 0x0
(gdb)
So, before checking for bitfield details, first check if there were
members.
Now, if we show all structs/classes in that object file and look for the
above data structure, we find it inside another:
$ pahole examples/wcohen/02_Exercise.cuda
<SNIP>
struct __nv_hdl_helper_trait_outer<false, false, int, Kokkos::View<double**>, Kokkos::View<double*>, Kokkos::View<double*> > {
struct __nv_hdl_helper_trait<__nv_dl_tag<int (*)(int, char**), main, 1u>, void (main(int, char**)::__lambda0::*)(int, double&)const> {
class __nv_hdl_wrapper_t<false, false, __nv_dl_tag<int (*)(int, char**), main, 1u>, void(int, double&), int, Kokkos::View<doubl get<main(int, char**)::__lambda0>(class __lambda0, int, class View<double**>, class View<double*>, class View<double*>);
/* size: 1, cachelines: 0, members: 0 */
/* padding: 1 */
/* last cacheline: 1 bytes */
};
/* size: 1, cachelines: 0, members: 0 */
/* padding: 1 */
/* last cacheline: 1 bytes */
};
<SNIP>
$
Reported-by: William Cohen <wcohen@redhat.com>
Fixes: 13e5b9fc00ee ("fprintf: Add unnamed bitfield padding at the end to rebuild original type")
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-07-01 16:37:40 +02:00
|
|
|
!cconf.suppress_force_paddings && last != NULL) {
|
2019-04-10 02:38:21 +02:00
|
|
|
tag_pos = cu__type(cu, last->tag.type);
|
|
|
|
size = tag__size(tag_pos, cu);
|
|
|
|
|
|
|
|
if (is_power_of_2(size) && class->padding > cu->addr_size) {
|
|
|
|
int added_padding;
|
|
|
|
int bit_size = size * 8;
|
|
|
|
|
|
|
|
printed += fprintf(fp, "\n%.*s/* Force padding: */\n", cconf.indent, tabs);
|
|
|
|
|
|
|
|
for (added_padding = 0; added_padding < class->padding; added_padding += size) {
|
|
|
|
printed += fprintf(fp, "%.*s", cconf.indent, tabs);
|
|
|
|
printed += type__fprintf(tag_pos, cu, "", &cconf, fp);
|
|
|
|
printed += fprintf(fp, ":%u;\n", bit_size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-06-17 20:30:09 +02:00
|
|
|
if (!cconf.show_only_data_members)
|
2021-06-30 15:07:14 +02:00
|
|
|
class__vtable_fprintf(class, &cconf, fp);
|
2009-06-17 20:30:09 +02:00
|
|
|
|
2009-04-19 18:48:51 +02:00
|
|
|
if (!cconf.emit_stats)
|
|
|
|
goto out;
|
|
|
|
|
2019-01-10 14:15:42 +01:00
|
|
|
printed += type__fprintf_stats(type, cu, &cconf, fp);
|
2012-08-20 19:42:17 +02:00
|
|
|
|
dwarves_fprintf: Count bitfield member sizes separately
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>
2019-03-26 20:53:30 +01:00
|
|
|
if (sum_holes > 0 || sum_bit_holes > 0) {
|
|
|
|
if (sum_bytes > 0) {
|
|
|
|
printed += fprintf(fp, "%.*s/* sum members: %u",
|
|
|
|
cconf.indent, tabs, sum_bytes);
|
|
|
|
if (sum_holes > 0)
|
|
|
|
printed += fprintf(fp, ", holes: %d, sum holes: %u",
|
|
|
|
class->nr_holes, sum_holes);
|
|
|
|
printed += fprintf(fp, " */\n");
|
|
|
|
}
|
|
|
|
if (sum_bits > 0) {
|
|
|
|
printed += fprintf(fp, "%.*s/* sum bitfield members: %u bits",
|
|
|
|
cconf.indent, tabs, sum_bits);
|
|
|
|
if (sum_bit_holes > 0)
|
|
|
|
printed += fprintf(fp, ", bit holes: %d, sum bit holes: %u bits",
|
|
|
|
class->nr_bit_holes, sum_bit_holes);
|
|
|
|
else
|
|
|
|
printed += fprintf(fp, " (%u bytes)", sum_bits / 8);
|
|
|
|
printed += fprintf(fp, " */\n");
|
|
|
|
}
|
|
|
|
}
|
2012-08-17 23:47:15 +02:00
|
|
|
if (class->padding > 0)
|
2019-01-10 14:15:42 +01:00
|
|
|
printed += fprintf(fp, "%.*s/* padding: %u */\n",
|
2009-04-19 18:48:51 +02:00
|
|
|
cconf.indent,
|
2012-08-17 23:47:15 +02:00
|
|
|
tabs, class->padding);
|
2009-04-19 18:48:51 +02:00
|
|
|
if (nr_paddings > 0)
|
2019-01-10 14:15:42 +01:00
|
|
|
printed += fprintf(fp, "%.*s/* paddings: %u, sum paddings: "
|
|
|
|
"%u */\n",
|
2009-04-19 18:48:51 +02:00
|
|
|
cconf.indent, tabs,
|
|
|
|
nr_paddings, sum_paddings);
|
2012-08-17 23:47:15 +02:00
|
|
|
if (class->bit_padding > 0)
|
2019-01-10 14:15:42 +01:00
|
|
|
printed += fprintf(fp, "%.*s/* bit_padding: %u bits */\n",
|
2009-04-19 18:48:51 +02:00
|
|
|
cconf.indent, tabs,
|
2012-08-17 23:47:15 +02:00
|
|
|
class->bit_padding);
|
fprintf: Show statistics about holes due to forced alignments
$ pahole -C task_struct | 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 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 */
/* forced alignments: 1, forced holes: 1, sum forced holes: 60 */
};
$
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>
2019-04-03 18:33:37 +02:00
|
|
|
if (!cconf.suppress_aligned_attribute && nr_forced_alignments != 0) {
|
|
|
|
printed += fprintf(fp, "%.*s/* forced alignments: %u",
|
2019-04-03 18:22:37 +02:00
|
|
|
cconf.indent, tabs,
|
|
|
|
nr_forced_alignments);
|
fprintf: Show statistics about holes due to forced alignments
$ pahole -C task_struct | 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 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 */
/* forced alignments: 1, forced holes: 1, sum forced holes: 60 */
};
$
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>
2019-04-03 18:33:37 +02:00
|
|
|
if (nr_forced_alignment_holes != 0) {
|
|
|
|
printed += fprintf(fp, ", forced holes: %u, sum forced holes: %u",
|
|
|
|
nr_forced_alignment_holes,
|
|
|
|
sum_forced_alignment_holes);
|
|
|
|
}
|
|
|
|
printed += fprintf(fp, " */\n");
|
|
|
|
}
|
2021-10-28 14:27:08 +02:00
|
|
|
cacheline = (cconf.base_offset + type->size) % conf->cacheline_size;
|
2016-06-29 22:27:51 +02:00
|
|
|
if (cacheline != 0)
|
2019-01-10 14:15:42 +01:00
|
|
|
printed += fprintf(fp, "%.*s/* last cacheline: %u bytes */\n",
|
2009-04-19 18:48:51 +02:00
|
|
|
cconf.indent, tabs,
|
2016-06-29 22:27:51 +02:00
|
|
|
cacheline);
|
2009-04-19 18:48:51 +02:00
|
|
|
if (cconf.show_first_biggest_size_base_type_member &&
|
2012-08-17 23:47:15 +02:00
|
|
|
type->nr_members != 0) {
|
|
|
|
struct class_member *m = type__find_first_biggest_size_base_type_member(type, cu);
|
2009-04-19 18:48:51 +02:00
|
|
|
|
2019-01-10 14:15:42 +01:00
|
|
|
printed += fprintf(fp, "%.*s/* first biggest size base type member: %s %u %zd */\n",
|
2009-04-19 18:48:51 +02:00
|
|
|
cconf.indent, tabs,
|
2021-06-24 15:01:27 +02:00
|
|
|
class_member__name(m), m->byte_offset,
|
2009-04-19 18:48:51 +02:00
|
|
|
m->byte_size);
|
|
|
|
}
|
|
|
|
|
dwarves_fprintf: Count bitfield member sizes separately
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>
2019-03-26 20:53:30 +01:00
|
|
|
size_diff = type->size * 8 - (sum_bytes * 8 + sum_bits + sum_holes * 8 + sum_bit_holes +
|
2019-03-18 05:23:40 +01:00
|
|
|
class->padding * 8 + class->bit_padding);
|
|
|
|
if (size_diff && type->nr_members != 0)
|
dwarves_fprintf: Count bitfield member sizes separately
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>
2019-03-26 20:53:30 +01:00
|
|
|
printed += fprintf(fp, "\n%.*s/* BRAIN FART ALERT! %d bytes != "
|
|
|
|
"%u (member bytes) + %u (member bits) "
|
2019-03-18 05:23:40 +01:00
|
|
|
"+ %u (byte holes) + %u (bit holes), diff = %d bits */\n",
|
2009-04-19 18:48:51 +02:00
|
|
|
cconf.indent, tabs,
|
dwarves_fprintf: Count bitfield member sizes separately
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>
2019-03-26 20:53:30 +01:00
|
|
|
type->size, sum_bytes, sum_bits, sum_holes, sum_bit_holes, size_diff);
|
2009-04-19 18:48:51 +02:00
|
|
|
out:
|
core: Infer if a struct is packed by the offsets/natural alignments
As DWARF (nor BTF) provides explicit attributes, we need to look at the
natural alignments, a byte is always alignted, etc.
This probably fails with things like __attribute__(__aligned(power-of-two)),
but with it most of the kernel data structures are full circled, i.e.
'pfunct --compile' regenerates source code from debug info that when
compiled generats debug info that end up matching the original sources.
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
const char * uptr;
int refcnt;
};
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
const char * uptr; /* 8 8 */
int refcnt; /* 16 4 */
/* size: 24, cachelines: 1, members: 3 */
/* padding: 4 */
/* last cacheline: 24 bytes */
};
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
const char * uptr;
int refcnt;
} __packed;
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
const char * uptr; /* 8 8 */
int refcnt; /* 16 4 */
/* size: 20, cachelines: 1, members: 3 */
/* last cacheline: 20 bytes */
} __attribute__((__packed__));
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
int refcnt;
const char * uptr;
};
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
int refcnt; /* 8 4 */
/* XXX 4 bytes hole, try to pack */
const char * uptr; /* 16 8 */
/* size: 24, cachelines: 1, members: 3 */
/* sum members: 20, holes: 1, sum holes: 4 */
/* last cacheline: 24 bytes */
};
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
int refcnt;
const char * uptr;
} __packed;
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
int refcnt; /* 8 4 */
const char * uptr; /* 12 8 */
/* size: 20, cachelines: 1, members: 3 */
/* last cacheline: 20 bytes */
} __attribute__((__packed__));
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
const char * uptr;
unsigned char refcnt;
};
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
const char * uptr; /* 8 8 */
unsigned char refcnt; /* 16 1 */
/* size: 24, cachelines: 1, members: 3 */
/* padding: 7 */
/* last cacheline: 24 bytes */
};
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
const char * uptr;
unsigned char refcnt;
} __packed;
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
const char * uptr; /* 8 8 */
unsigned char refcnt; /* 16 1 */
/* size: 17, cachelines: 1, members: 3 */
/* last cacheline: 17 bytes */
} __attribute__((__packed__));
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
unsigned char refcnt;
const char * uptr;
};
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
unsigned char refcnt; /* 8 1 */
/* XXX 7 bytes hole, try to pack */
const char * uptr; /* 16 8 */
/* size: 24, cachelines: 1, members: 3 */
/* sum members: 17, holes: 1, sum holes: 7 */
/* last cacheline: 24 bytes */
};
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
unsigned char refcnt;
const char * uptr;
} __packed;
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
unsigned char refcnt; /* 8 1 */
const char * uptr; /* 9 8 */
/* size: 17, cachelines: 1, members: 3 */
/* last cacheline: 17 bytes */
} __attribute__((__packed__));
$
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-10 22:41:59 +02:00
|
|
|
printed += fprintf(fp, "%.*s}", indent, tabs);
|
|
|
|
|
2019-05-04 14:04:46 +02:00
|
|
|
if (class->is_packed && !cconf.suppress_packed)
|
core: Infer if a struct is packed by the offsets/natural alignments
As DWARF (nor BTF) provides explicit attributes, we need to look at the
natural alignments, a byte is always alignted, etc.
This probably fails with things like __attribute__(__aligned(power-of-two)),
but with it most of the kernel data structures are full circled, i.e.
'pfunct --compile' regenerates source code from debug info that when
compiled generats debug info that end up matching the original sources.
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
const char * uptr;
int refcnt;
};
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
const char * uptr; /* 8 8 */
int refcnt; /* 16 4 */
/* size: 24, cachelines: 1, members: 3 */
/* padding: 4 */
/* last cacheline: 24 bytes */
};
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
const char * uptr;
int refcnt;
} __packed;
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
const char * uptr; /* 8 8 */
int refcnt; /* 16 4 */
/* size: 20, cachelines: 1, members: 3 */
/* last cacheline: 20 bytes */
} __attribute__((__packed__));
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
int refcnt;
const char * uptr;
};
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
int refcnt; /* 8 4 */
/* XXX 4 bytes hole, try to pack */
const char * uptr; /* 16 8 */
/* size: 24, cachelines: 1, members: 3 */
/* sum members: 20, holes: 1, sum holes: 4 */
/* last cacheline: 24 bytes */
};
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
int refcnt;
const char * uptr;
} __packed;
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
int refcnt; /* 8 4 */
const char * uptr; /* 12 8 */
/* size: 20, cachelines: 1, members: 3 */
/* last cacheline: 20 bytes */
} __attribute__((__packed__));
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
const char * uptr;
unsigned char refcnt;
};
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
const char * uptr; /* 8 8 */
unsigned char refcnt; /* 16 1 */
/* size: 24, cachelines: 1, members: 3 */
/* padding: 7 */
/* last cacheline: 24 bytes */
};
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
const char * uptr;
unsigned char refcnt;
} __packed;
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
const char * uptr; /* 8 8 */
unsigned char refcnt; /* 16 1 */
/* size: 17, cachelines: 1, members: 3 */
/* last cacheline: 17 bytes */
} __attribute__((__packed__));
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
unsigned char refcnt;
const char * uptr;
};
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
unsigned char refcnt; /* 8 1 */
/* XXX 7 bytes hole, try to pack */
const char * uptr; /* 16 8 */
/* size: 24, cachelines: 1, members: 3 */
/* sum members: 17, holes: 1, sum holes: 7 */
/* last cacheline: 24 bytes */
};
$ cat a.c
#define __packed __attribute__((__packed__))
struct filename {
const char * name;
unsigned char refcnt;
const char * uptr;
} __packed;
void m(struct filename *f) {}
$ gcc -g -c a.c
$ pahole a.o
struct filename {
const char * name; /* 0 8 */
unsigned char refcnt; /* 8 1 */
const char * uptr; /* 9 8 */
/* size: 17, cachelines: 1, members: 3 */
/* last cacheline: 17 bytes */
} __attribute__((__packed__));
$
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-10 22:41:59 +02:00
|
|
|
printed += fprintf(fp, " __attribute__((__packed__))");
|
|
|
|
|
|
|
|
if (cconf.suffix)
|
|
|
|
printed += fprintf(fp, " %s", cconf.suffix);
|
2019-04-09 22:03:54 +02:00
|
|
|
|
emit: Handle structs with DW_AT_alignment=1 meaning __packed__
In the following struct the ceph_entity_addr entries all appear marked with a
__attribute__((__aligned__(8)), which, for the first two members of this type,
'peer_addr' and 'peer_addr_for_me', don't cause the regenerated struct to
differ in layout from the original layout put in place by the compiler as per
the original source code.
But the third member of this type, 'actual_peer_addr' ends up in a different
offset, even in a different cacheline, here is how it looks like in the code generated
from the original source code, at offset 568.
char in_banner[30]; /* 472 30 */
struct ceph_msg_connect out_connect; /* 502 33 */
/* --- cacheline 8 boundary (512 bytes) was 23 bytes ago --- */
struct ceph_msg_connect_reply in_reply; /* 535 26 */
struct ceph_entity_addr actual_peer_addr __attribute__((__aligned__(1))); /* 561 136 */
/* --- cacheline 10 boundary (640 bytes) was 57 bytes ago --- */
struct ceph_msg_header out_hdr; /* 697 53 */
/* XXX 2 bytes hole, try to pack */
/* --- cacheline 11 boundary (704 bytes) was 48 bytes ago --- */
And here is how it looks like when built from the regenerated source code, at
offset 568:
$ pfunct --compile /home/acme/git/build/v5.1-rc4+/fs/ceph/super.o > ceph.c
$ gcc -g -c ceph.c
$ pahole -C ceph_connection ceph.o | head -46
struct ceph_connection {
void * private; /* 0 8 */
const struct ceph_connection_operations * ops; /* 8 8 */
struct ceph_messenger * msgr; /* 16 8 */
atomic_t sock_state; /* 24 4 */
/* XXX 4 bytes hole, try to pack */
struct socket * sock; /* 32 8 */
struct ceph_entity_addr peer_addr __attribute__((__aligned__(8))); /* 40 136 */
/* --- cacheline 2 boundary (128 bytes) was 48 bytes ago --- */
struct ceph_entity_addr peer_addr_for_me __attribute__((__aligned__(8))); /* 176 136 */
/* --- cacheline 4 boundary (256 bytes) was 56 bytes ago --- */
long unsigned int flags; /* 312 8 */
/* --- cacheline 5 boundary (320 bytes) --- */
long unsigned int state; /* 320 8 */
const char * error_msg; /* 328 8 */
struct ceph_entity_name peer_name; /* 336 9 */
/* XXX 7 bytes hole, try to pack */
u64 peer_features; /* 352 8 */
u32 connect_seq; /* 360 4 */
u32 peer_global_seq; /* 364 4 */
struct ceph_auth_handshake * auth; /* 368 8 */
int auth_retry; /* 376 4 */
/* XXX 4 bytes hole, try to pack */
/* --- cacheline 6 boundary (384 bytes) --- */
struct mutex mutex; /* 384 32 */
struct list_head out_queue; /* 416 16 */
struct list_head out_sent; /* 432 16 */
/* --- cacheline 7 boundary (448 bytes) --- */
u64 out_seq; /* 448 8 */
u64 in_seq; /* 456 8 */
u64 in_seq_acked; /* 464 8 */
char in_banner[30]; /* 472 30 */
struct ceph_msg_connect out_connect; /* 502 33 */
/* --- cacheline 8 boundary (512 bytes) was 23 bytes ago --- */
struct ceph_msg_connect_reply in_reply; /* 535 26 */
/* XXX 7 bytes hole, try to pack */
struct ceph_entity_addr actual_peer_addr __attribute__((__aligned__(8))); /* 568 136 */
/* --- cacheline 11 boundary (704 bytes) --- */
$
That happens because 'struct ceph_entity_addr' has that __attribute__
((__aligned__(8)) in the regenerated source code, above, now look at how it
gets regenerated:
$ pahole -C ceph_entity_addr ceph.o
struct ceph_entity_addr {
__le32 type; /* 0 4 */
__le32 nonce; /* 4 4 */
struct __kernel_sockaddr_storage in_addr __attribute__((__aligned__(8))); /* 8 128 */
/* size: 136, cachelines: 3, members: 3 */
/* forced alignments: 1 */
/* last cacheline: 8 bytes */
} __attribute__((__aligned__(8)));
$
While when looking at the original DWARF:
$ pahole -C ceph_entity_addr /home/acme/git/build/v5.1-rc4+/fs/ceph/super.o
struct ceph_entity_addr {
__le32 type; /* 0 4 */
__le32 nonce; /* 4 4 */
struct __kernel_sockaddr_storage in_addr __attribute__((__aligned__(1))); /* 8 128 */
/* size: 136, cachelines: 3, members: 3 */
/* forced alignments: 1 */
/* last cacheline: 8 bytes */
} __attribute__((__aligned__(1)));
$
The confusion may further come from the fact that 'struct __kernel_sockaddr_storage' has,
in the regenerated source code, the __attribute__((__aligned__8)))
$ pahole -C __kernel_sockaddr_storage ceph.o
struct __kernel_sockaddr_storage {
__kernel_sa_family_t ss_family; /* 0 2 */
char __data[126]; /* 2 126 */
/* size: 128, cachelines: 2, members: 2 */
} __attribute__((__aligned__(8)));
$
Which is the same as in the original DWARF:
$ pahole -C __kernel_sockaddr_storage /home/acme/git/build/v5.1-rc4+/fs/ceph/super.o
struct __kernel_sockaddr_storage {
__kernel_sa_family_t ss_family; /* 0 2 */
char __data[126]; /* 2 126 */
/* size: 128, cachelines: 2, members: 2 */
} __attribute__((__aligned__(8)));
$
Looking at the original original source code for 'struct ceph_entity_addr'
helps here, as it reads:
include/linux/ceph/msgr.h, line 63:
/*
* entity_addr -- network address
*/
struct ceph_entity_addr {
__le32 type;
__le32 nonce; /* unique id for process (e.g. pid) */
struct sockaddr_storage in_addr;
} __attribute__ ((packed));
So the original code has no __attribute__((__aligned__(1))), so, lets look at
what the compiler generates for 'struct ceph_entity_addr':
$ readelf -wi /home/acme/git/build/v5.1-rc4+/fs/ceph/super.o | grep ceph_entity_addr -A7
<193a6> DW_AT_name : (indirect string, offset: 0x1586): ceph_entity_addr
<193aa> DW_AT_byte_size : 136
<193ab> DW_AT_alignment : 1
<193ac> DW_AT_decl_file : 296
<193ae> DW_AT_decl_line : 63
<193af> DW_AT_decl_column : 8
<193b0> DW_AT_sibling : <0x193e0>
<2><193b4>: Abbrev Number: 5 (DW_TAG_member)
$
So the natural alignment for 'struct ceph_entity_addr' ends up being the
natural alignment for 'struct __kernel_sockaddr_storage', which is 8, but
since 'struct ceph_entity_addr' was marked in the original source code as __packed__,
the compiler added the DW_AT_alignment: 1 to override that.
The heuristic in pahole, so far, took that __attribute__((__aligned__(1)))
literally:
$ pahole -C ceph_entity_addr /home/acme/git/build/v5.1-rc4+/fs/ceph/super.o
struct ceph_entity_addr {
__le32 type; /* 0 4 */
__le32 nonce; /* 4 4 */
struct __kernel_sockaddr_storage in_addr __attribute__((__aligned__(1))); /* 8 128 */
/* size: 136, cachelines: 3, members: 3 */
/* forced alignments: 1 */
/* last cacheline: 8 bytes */
} __attribute__((__aligned__(1)));
$
which ends up making the regenerated source code (with the __aligned__((1))),
generate a different layout, the __aligned__((8)) in one of its members
overrode that __aligned__((1)).
Take this into account and when faced with a structure which natural alignment
is not one and that has a DW_AT_alignment:1 to mean it really is __packed__.
Doing that makes the regenerated source code match the original structure
layouts, i.e. after the patch we get:
$ pahole -C ceph_entity_addr /home/acme/git/build/v5.1-rc4+/fs/ceph/super.o
struct ceph_entity_addr {
__le32 type; /* 0 4 */
__le32 nonce; /* 4 4 */
struct __kernel_sockaddr_storage in_addr __attribute__((__aligned__(1))); /* 8 128 */
/* size: 136, cachelines: 3, members: 3 */
/* forced alignments: 1 */
/* last cacheline: 8 bytes */
} __attribute__((__packed__));
$
And that member in 'struct ceph_connection', in the original, continues to read:
$ pahole -C ceph_connection /home/acme/git/build/v5.1-rc4+/fs/ceph/super.o | grep -w actual_peer_addr -B4 -A6
char in_banner[30]; /* 472 30 */
struct ceph_msg_connect out_connect; /* 502 33 */
/* --- cacheline 8 boundary (512 bytes) was 23 bytes ago --- */
struct ceph_msg_connect_reply in_reply; /* 535 26 */
struct ceph_entity_addr actual_peer_addr __attribute__((__aligned__(1))); /* 561 136 */
/* --- cacheline 10 boundary (640 bytes) was 57 bytes ago --- */
struct ceph_msg_header out_hdr; /* 697 53 */
/* XXX 2 bytes hole, try to pack */
/* --- cacheline 11 boundary (704 bytes) was 48 bytes ago --- */
$
While in the regenerated DWARF from the regenerated source code reads:
$ pfunct --compile /home/acme/git/build/v5.1-rc4+/fs/ceph/super.o > ceph.c
$ gcc -g -c ceph.c
$ pahole -C ceph_connection ceph.o | grep -w actual_peer_addr -B4 -A6
char in_banner[30]; /* 472 30 */
struct ceph_msg_connect out_connect; /* 502 33 */
/* --- cacheline 8 boundary (512 bytes) was 23 bytes ago --- */
struct ceph_msg_connect_reply in_reply; /* 535 26 */
struct ceph_entity_addr actual_peer_addr __attribute__((__aligned__(1))); /* 561 136 */
/* --- cacheline 10 boundary (640 bytes) was 57 bytes ago --- */
struct ceph_msg_header out_hdr; /* 697 53 */
/* XXX 2 bytes hole, try to pack */
/* --- cacheline 11 boundary (704 bytes) was 48 bytes ago --- */
$
I.e. it now matches.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2019-04-15 18:25:48 +02:00
|
|
|
/*
|
|
|
|
* A class that was marked packed by class__infer_packed_attributes
|
|
|
|
* because it has an alignment that is different than its natural
|
|
|
|
* alignment, should not print the __alignment__ here, just the
|
|
|
|
* __packed__ attribute.
|
|
|
|
*/
|
|
|
|
if (!cconf.suppress_aligned_attribute && type->alignment != 0 && !class->is_packed)
|
2019-04-09 22:03:54 +02:00
|
|
|
printed += fprintf(fp, " __attribute__((__aligned__(%u)))", type->alignment);
|
|
|
|
|
|
|
|
return printed;
|
2009-04-19 18:48:51 +02:00
|
|
|
}
|
|
|
|
|
2016-06-29 21:19:20 +02:00
|
|
|
size_t class__fprintf(struct class *class, const struct cu *cu, FILE *fp)
|
|
|
|
{
|
|
|
|
return __class__fprintf(class, cu, NULL, fp);
|
|
|
|
}
|
|
|
|
|
2009-04-19 18:48:51 +02:00
|
|
|
static size_t variable__fprintf(const struct tag *tag, const struct cu *cu,
|
|
|
|
const struct conf_fprintf *conf, FILE *fp)
|
|
|
|
{
|
|
|
|
const struct variable *var = tag__variable(tag);
|
2021-06-30 15:07:14 +02:00
|
|
|
const char *name = variable__name(var);
|
2009-04-19 18:48:51 +02:00
|
|
|
size_t printed = 0;
|
|
|
|
|
|
|
|
if (name != NULL) {
|
2009-06-04 22:30:06 +02:00
|
|
|
struct tag *type = cu__type(cu, var->ip.tag.type);
|
2009-04-19 18:48:51 +02:00
|
|
|
if (type != NULL) {
|
|
|
|
const char *varprefix = variable__prefix(var);
|
|
|
|
|
|
|
|
if (varprefix != NULL)
|
|
|
|
printed += fprintf(fp, "%s", varprefix);
|
|
|
|
printed += type__fprintf(type, cu, name, conf, fp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return printed;
|
|
|
|
}
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
static size_t namespace__fprintf(const struct tag *tag, const struct cu *cu,
|
2009-04-19 18:48:51 +02:00
|
|
|
const struct conf_fprintf *conf, FILE *fp)
|
|
|
|
{
|
2012-08-17 23:47:15 +02:00
|
|
|
struct namespace *space = tag__namespace(tag);
|
2009-04-19 18:48:51 +02:00
|
|
|
struct conf_fprintf cconf = *conf;
|
2021-06-24 15:01:27 +02:00
|
|
|
size_t printed = fprintf(fp, "namespace %s {\n", namespace__name(space));
|
2009-04-19 18:48:51 +02:00
|
|
|
struct tag *pos;
|
|
|
|
|
|
|
|
++cconf.indent;
|
|
|
|
cconf.no_semicolon = 0;
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
namespace__for_each_tag(space, pos) {
|
2009-04-19 18:48:51 +02:00
|
|
|
printed += tag__fprintf(pos, cu, &cconf, fp);
|
|
|
|
printed += fprintf(fp, "\n\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
return printed + fprintf(fp, "}");
|
|
|
|
}
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
size_t tag__fprintf(struct tag *tag, const struct cu *cu,
|
2009-04-19 18:48:51 +02:00
|
|
|
const struct conf_fprintf *conf, FILE *fp)
|
|
|
|
{
|
|
|
|
size_t printed = 0;
|
|
|
|
struct conf_fprintf tconf;
|
|
|
|
const struct conf_fprintf *pconf = conf;
|
|
|
|
|
|
|
|
if (conf == NULL) {
|
|
|
|
tconf = conf_fprintf__defaults;
|
|
|
|
pconf = &tconf;
|
|
|
|
|
|
|
|
if (tconf.expand_types)
|
|
|
|
tconf.name_spacing = 55;
|
2012-08-17 23:47:15 +02:00
|
|
|
else if (tag__is_union(tag))
|
2009-04-19 18:48:51 +02:00
|
|
|
tconf.name_spacing = 21;
|
|
|
|
} else if (conf->name_spacing == 0 || conf->type_spacing == 0) {
|
|
|
|
tconf = *conf;
|
|
|
|
pconf = &tconf;
|
|
|
|
|
|
|
|
if (tconf.name_spacing == 0) {
|
|
|
|
if (tconf.expand_types)
|
|
|
|
tconf.name_spacing = 55;
|
|
|
|
else
|
2012-08-17 23:47:15 +02:00
|
|
|
tconf.name_spacing = tag__is_union(tag) ? 21 : 23;
|
2009-04-19 18:48:51 +02:00
|
|
|
}
|
|
|
|
if (tconf.type_spacing == 0)
|
|
|
|
tconf.type_spacing = 26;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pconf->expand_types)
|
2012-08-17 23:47:15 +02:00
|
|
|
++tag->recursivity_level;
|
2009-04-19 18:48:51 +02:00
|
|
|
|
|
|
|
if (pconf->show_decl_info) {
|
pahole: Show the file where a struct was used
To help with using just that object file, avoiding processing big files
such as vmlinux, e.g.:
$ pahole -I vmlinux
<SNIP>
/* Used at: /home/acme/git/perf/init/main.c */
/* <1f4a5> /home/acme/git/perf/arch/x86/include/asm/orc_types.h:85 */
struct orc_entry {
s16 sp_offset; /* 0 2 */
s16 bp_offset; /* 2 2 */
unsigned int sp_reg:4; /* 4:28 4 */
unsigned int bp_reg:4; /* 4:24 4 */
unsigned int type:2; /* 4:22 4 */
/* size: 6, cachelines: 1, members: 5 */
/* padding: 65534 */
/* bit_padding: 22 bits */
/* last cacheline: 6 bytes */
/* BRAIN FART ALERT! 6 != 8 + 0(holes), diff = -2 */
};
<SNIP>
So I noticed that BFA, need to work on it, to make the testing process
faster, better not process vmlinux.o, instead, do:
$ pahole -C orc_entry ${kernel_build_dir}/init/main.o
Much faster, as main.o is much smaller than the vmlinux file.
Now to fix the processing of 'struct orc_entry'.
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2017-12-15 19:30:38 +01:00
|
|
|
printed += fprintf(fp, "%.*s", pconf->indent, tabs);
|
|
|
|
printed += fprintf(fp, "/* Used at: %s */\n", cu->name);
|
2009-04-19 18:48:51 +02:00
|
|
|
printed += fprintf(fp, "%.*s", pconf->indent, tabs);
|
2012-08-17 23:47:15 +02:00
|
|
|
printed += tag__fprintf_decl_info(tag, cu, fp);
|
2009-04-19 18:48:51 +02:00
|
|
|
}
|
|
|
|
printed += fprintf(fp, "%.*s", pconf->indent, tabs);
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
switch (tag->tag) {
|
2009-04-19 18:48:51 +02:00
|
|
|
case DW_TAG_array_type:
|
2012-08-17 23:47:15 +02:00
|
|
|
printed += array_type__fprintf(tag, cu, "array", pconf, fp);
|
2009-04-19 18:48:51 +02:00
|
|
|
break;
|
|
|
|
case DW_TAG_enumeration_type:
|
2021-06-25 14:56:00 +02:00
|
|
|
printed += enumeration__fprintf(tag, pconf, fp);
|
2009-04-19 18:48:51 +02:00
|
|
|
break;
|
|
|
|
case DW_TAG_typedef:
|
2012-08-17 23:47:15 +02:00
|
|
|
printed += typedef__fprintf(tag, cu, pconf, fp);
|
2009-04-19 18:48:51 +02:00
|
|
|
break;
|
|
|
|
case DW_TAG_class_type:
|
2009-04-25 06:54:21 +02:00
|
|
|
case DW_TAG_interface_type:
|
2009-04-19 18:48:51 +02:00
|
|
|
case DW_TAG_structure_type:
|
2016-06-29 21:19:20 +02:00
|
|
|
printed += __class__fprintf(tag__class(tag), cu, pconf, fp);
|
2009-04-19 18:48:51 +02:00
|
|
|
break;
|
2019-11-05 14:16:25 +01:00
|
|
|
case DW_TAG_subroutine_type:
|
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>
2019-11-05 15:30:51 +01:00
|
|
|
printed += ftype__fprintf(tag__ftype(tag), cu, NULL, false, false, 0, true, pconf, fp);
|
2019-11-05 14:16:25 +01:00
|
|
|
break;
|
2009-04-19 18:48:51 +02:00
|
|
|
case DW_TAG_namespace:
|
2012-08-17 23:47:15 +02:00
|
|
|
printed += namespace__fprintf(tag, cu, pconf, fp);
|
2009-04-19 18:48:51 +02:00
|
|
|
break;
|
|
|
|
case DW_TAG_subprogram:
|
2012-08-17 23:47:15 +02:00
|
|
|
printed += function__fprintf(tag, cu, pconf, fp);
|
2009-04-19 18:48:51 +02:00
|
|
|
break;
|
|
|
|
case DW_TAG_union_type:
|
2012-08-17 23:47:15 +02:00
|
|
|
printed += union__fprintf(tag__type(tag), cu, pconf, fp);
|
2009-04-19 18:48:51 +02:00
|
|
|
break;
|
|
|
|
case DW_TAG_variable:
|
2012-08-17 23:47:15 +02:00
|
|
|
printed += variable__fprintf(tag, cu, pconf, fp);
|
2009-04-19 18:48:51 +02:00
|
|
|
break;
|
|
|
|
case DW_TAG_imported_declaration:
|
2012-08-17 23:47:15 +02:00
|
|
|
printed += imported_declaration__fprintf(tag, cu, fp);
|
2009-04-19 18:48:51 +02:00
|
|
|
break;
|
|
|
|
case DW_TAG_imported_module:
|
2012-08-17 23:47:15 +02:00
|
|
|
printed += imported_module__fprintf(tag, cu, fp);
|
2009-04-19 18:48:51 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
printed += fprintf(fp, "/* %s: %s tag not supported! */",
|
2012-08-17 23:47:15 +02:00
|
|
|
__func__, dwarf_tag_name(tag->tag));
|
2009-04-19 18:48:51 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pconf->no_semicolon) {
|
|
|
|
fputc(';', fp);
|
|
|
|
++printed;
|
|
|
|
}
|
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
if (tag__is_function(tag) && !pconf->suppress_comments) {
|
|
|
|
const struct function *func = tag__function(tag);
|
2009-04-19 18:48:51 +02:00
|
|
|
|
2012-08-17 23:47:15 +02:00
|
|
|
if (func->linkage_name)
|
2021-06-24 15:01:27 +02:00
|
|
|
printed += fprintf(fp, " /* linkage=%s */", function__linkage_name(func));
|
2009-04-19 18:48:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (pconf->expand_types)
|
2012-08-17 23:47:15 +02:00
|
|
|
--tag->recursivity_level;
|
2009-04-19 18:48:51 +02:00
|
|
|
|
|
|
|
return printed;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cus__print_error_msg(const char *progname, const struct cus *cus,
|
|
|
|
const char *filename, const int err)
|
|
|
|
{
|
2021-06-22 15:42:55 +02:00
|
|
|
if (err == -EINVAL || (cus != NULL && cus__empty(cus)))
|
2009-04-19 18:48:51 +02:00
|
|
|
fprintf(stderr, "%s: couldn't load debugging info from %s\n",
|
|
|
|
progname, filename);
|
|
|
|
else
|
codiff: Fix usage of negative errno values with strerror(), reported by covscan
Error: NEGATIVE_RETURNS (CWE-394):
dwarves-1.21/codiff.c:816: negative_return_fn: Function "cus__load_file(old_cus, &conf_load, old_filename)" returns a negative number.
dwarves-1.21/codiff.c:816: assign: Assigning: "err" = "cus__load_file(old_cus, &conf_load, old_filename)".
dwarves-1.21/codiff.c:818: negative_returns: "err" is passed to a parameter that cannot be negative.
# 816| err = cus__load_file(old_cus, &conf_load, old_filename);
# 817| if (err != 0) {
# 818|-> cus__print_error_msg("codiff", old_cus, old_filename, err);
# 819| goto out_cus_delete_priv;
# 820| }
Error: NEGATIVE_RETURNS (CWE-394):
dwarves-1.21/codiff.c:830: negative_return_fn: Function "cus__load_file(new_cus, &conf_load, new_filename)" returns a negative number.
dwarves-1.21/codiff.c:830: assign: Assigning: "err" = "cus__load_file(new_cus, &conf_load, new_filename)".
dwarves-1.21/codiff.c:832: negative_returns: "err" is passed to a parameter that cannot be negative.
# 830| err = cus__load_file(new_cus, &conf_load, new_filename);
# 831| if (err != 0) {
# 832|-> cus__print_error_msg("codiff", new_cus, new_filename, err);
# 833| goto out_cus_delete_priv;
# 834| }
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2021-05-17 21:42:19 +02:00
|
|
|
fprintf(stderr, "%s: %s\n", progname, strerror(-err));
|
2009-04-19 18:48:51 +02:00
|
|
|
}
|
|
|
|
|
2021-08-13 16:45:45 +02:00
|
|
|
#ifndef _SC_LEVEL1_DCACHE_LINESIZE
|
|
|
|
int filename__read_int(const char *filename, int *value)
|
|
|
|
{
|
|
|
|
char line[64];
|
|
|
|
int fd = open(filename, O_RDONLY), err = -1;
|
|
|
|
|
|
|
|
if (fd < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (read(fd, line, sizeof(line)) > 0) {
|
|
|
|
*value = atoi(line);
|
|
|
|
err = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
close(fd);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static long cacheline__size(void)
|
|
|
|
{
|
|
|
|
#ifdef _SC_LEVEL1_DCACHE_LINESIZE
|
|
|
|
return sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
|
|
|
|
#else
|
|
|
|
int value;
|
|
|
|
return filename__read_int("/sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size", &value) == 0 ? value : -1;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2021-10-28 14:27:08 +02:00
|
|
|
void dwarves__resolve_cacheline_size(const struct conf_load *conf, uint16_t user_cacheline_size)
|
2009-04-19 18:48:51 +02:00
|
|
|
{
|
2021-10-28 14:27:08 +02:00
|
|
|
uint16_t size;
|
|
|
|
|
2009-04-19 18:48:51 +02:00
|
|
|
if (user_cacheline_size == 0) {
|
2021-08-13 16:45:45 +02:00
|
|
|
long sys_cacheline_size = cacheline__size();
|
2009-04-19 18:48:51 +02:00
|
|
|
|
|
|
|
if (sys_cacheline_size > 0)
|
2021-10-28 14:27:08 +02:00
|
|
|
size = sys_cacheline_size;
|
2009-04-19 18:48:51 +02:00
|
|
|
else
|
2021-10-28 14:27:08 +02:00
|
|
|
size = 64; /* Fall back to a sane value */
|
2009-04-19 18:48:51 +02:00
|
|
|
} else
|
2021-10-28 14:27:08 +02:00
|
|
|
size = user_cacheline_size;
|
|
|
|
|
|
|
|
if (conf)
|
|
|
|
conf->conf_fprintf->cacheline_size = size;
|
|
|
|
|
|
|
|
conf_fprintf__defaults.cacheline_size = size;
|
2009-04-19 18:48:51 +02:00
|
|
|
}
|