dwarves/dwarves_fprintf.c

1716 lines
47 KiB
C
Raw Normal View History

/*
SPDX-License-Identifier: GPL-2.0-only
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>
#include <string.h>
#include <unistd.h>
#include <inttypes.h>
#include <elfutils/version.h>
#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",
#ifdef STB_GNU_UNIQUE
[DW_TAG_type_unit] = "type_unit",
[DW_TAG_rvalue_reference_type] = "rvalue_reference_type",
#endif
};
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",
#ifdef STB_GNU_UNIQUE
[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",
#endif
#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
};
const char *dwarf_tag_name(const uint32_t tag)
{
if (tag >= DW_TAG_array_type && tag <=
#ifdef STB_GNU_UNIQUE
DW_TAG_rvalue_reference_type
#else
DW_TAG_shared_type
#endif
)
return dwarf_tag_names[tag];
else if (tag >= DW_TAG_MIPS_loop && tag <=
#if _ELFUTILS_PREREQ(0, 153)
DW_TAG_GNU_call_site_parameter
#elif STB_GNU_UNIQUE
DW_TAG_GNU_formal_parameter_pack
#else
DW_TAG_class_template
#endif
)
return dwarf_gnu_tag_names[tag - DW_TAG_MIPS_loop];
return "INVALID";
}
static const struct conf_fprintf conf_fprintf__defaults = {
.name_spacing = 23,
.type_spacing = 26,
.emit_stats = 1,
};
static const char tabs[] = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
static size_t cacheline_size;
size_t tag__nr_cachelines(const struct tag *tag, const struct cu *cu)
{
return (tag__size(tag, cu) + cacheline_size - 1) / cacheline_size;
}
static const char *tag__accessibility(const struct tag *tag)
{
int a;
switch (tag->tag) {
case DW_TAG_inheritance:
case DW_TAG_member:
a = tag__class_member(tag)->accessibility;
break;
case DW_TAG_subprogram:
a = tag__function(tag)->accessibility;
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;
}
static size_t __tag__id_not_found_snprintf(char *bf, size_t len, uint16_t id,
const char *fn, int line)
{
return snprintf(bf, len, "<ERROR(%s:%d): %#llx not found!>", fn, line,
(unsigned long long)id);
}
#define tag__id_not_found_snprintf(bf, len, id) \
__tag__id_not_found_snprintf(bf, len, id, __func__, __LINE__)
size_t tag__fprintf_decl_info(const struct tag *tag,
const struct cu *cu, FILE *fp)
{
return fprintf(fp, "/* <%llx> %s:%u */\n", tag__orig_id(tag, cu),
tag__decl_file(tag, cu), tag__decl_line(tag, cu));
return 0;
}
static size_t type__fprintf(struct tag *type, const struct cu *cu,
const char *name, const struct conf_fprintf *conf,
FILE *fp);
static size_t array_type__fprintf(const struct tag *tag,
const struct cu *cu, const char *name,
const struct conf_fprintf *conf,
FILE *fp)
{
struct array_type *at = tag__array_type(tag);
struct tag *type = cu__type(cu, tag->type);
size_t printed;
unsigned long long flat_dimensions = 0;
int i;
if (type == NULL)
return tag__id_not_found_fprintf(fp, tag->type);
printed = type__fprintf(type, cu, name, conf, fp);
for (i = 0; i < at->dimensions; ++i) {
if (conf->flat_arrays || at->is_vector) {
/*
* Seen on the Linux kernel on tun_filter:
*
* __u8 addr[0][ETH_ALEN];
*/
if (at->nr_entries[i] == 0 && i == 0)
break;
if (!flat_dimensions)
flat_dimensions = at->nr_entries[i];
else
flat_dimensions *= at->nr_entries[i];
} else
printed += fprintf(fp, "[%u]", at->nr_entries[i]);
}
if (at->is_vector) {
type = tag__follow_typedef(tag, cu);
if (flat_dimensions == 0)
flat_dimensions = 1;
printed += fprintf(fp, " __attribute__ ((__vector_size__ (%llu)))",
flat_dimensions * tag__size(type, cu));
} else if (conf->flat_arrays)
printed += fprintf(fp, "[%llu]", flat_dimensions);
return printed;
}
size_t typedef__fprintf(const struct tag *tag, const struct cu *cu,
const struct conf_fprintf *conf, FILE *fp)
{
struct type *type = tag__type(tag);
const struct conf_fprintf *pconf = conf ?: &conf_fprintf__defaults;
const struct tag *tag_type;
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?
*/
if (tag->type == 0)
return fprintf(fp, "typedef void %s", type__name(type, cu));
tag_type = cu__type(cu, tag->type);
if (tag_type == NULL) {
printed = fprintf(fp, "typedef ");
printed += tag__id_not_found_fprintf(fp, tag->type);
return printed + fprintf(fp, " %s", type__name(type, cu));
}
switch (tag_type->tag) {
case DW_TAG_array_type:
printed = fprintf(fp, "typedef ");
return printed + array_type__fprintf(tag_type, cu,
type__name(type, cu),
pconf, fp);
case DW_TAG_pointer_type:
if (tag_type->type == 0) /* void pointer */
break;
ptr_type = cu__type(cu, tag_type->type);
if (ptr_type == NULL) {
printed = fprintf(fp, "typedef ");
printed += tag__id_not_found_fprintf(fp, tag_type->type);
return printed + fprintf(fp, " *%s",
type__name(type, cu));
}
if (ptr_type->tag != DW_TAG_subroutine_type)
break;
tag_type = ptr_type;
is_pointer = 1;
/* Fall thru */
case DW_TAG_subroutine_type:
printed = fprintf(fp, "typedef ");
return printed + ftype__fprintf(tag__ftype(tag_type), cu,
type__name(type, cu),
0, is_pointer, 0,
pconf, fp);
case DW_TAG_class_type:
case DW_TAG_structure_type: {
struct type *ctype = tag__type(tag_type);
if (type__name(ctype, cu) != NULL)
return fprintf(fp, "typedef struct %s %s",
type__name(ctype, cu),
type__name(type, cu));
}
}
return fprintf(fp, "typedef %s %s",
tag__name(tag_type, cu, bf, sizeof(bf), pconf),
type__name(type, cu));
}
static size_t imported_declaration__fprintf(const struct tag *tag,
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];
size_t printed = fprintf(fp, "using ::");
const struct tag *decl = cu__function(cu, tag->type);
if (decl == NULL) {
decl = cu__tag(cu, tag->type);
if (decl == NULL)
return printed + tag__id_not_found_fprintf(fp, tag->type);
}
return printed + fprintf(fp, "%s", tag__name(decl, cu, bf, sizeof(bf), NULL));
}
static size_t imported_module__fprintf(const struct tag *tag,
const struct cu *cu, FILE *fp)
{
const struct tag *module = cu__tag(cu, tag->type);
const char *name = "<IMPORTED MODULE ERROR!>";
if (tag__is_namespace(module))
name = namespace__name(tag__namespace(module), cu);
return fprintf(fp, "using namespace %s", name);
}
size_t enumeration__fprintf(const struct tag *tag, const struct cu *cu,
const struct conf_fprintf *conf, FILE *fp)
{
struct type *type = tag__type(tag);
struct enumerator *pos;
size_t printed = fprintf(fp, "enum%s%s {\n",
type__name(type, cu) ? " " : "",
type__name(type, cu) ?: "");
int indent = conf->indent;
if (indent >= (int)sizeof(tabs))
indent = sizeof(tabs) - 1;
type__for_each_enumerator(type, pos)
printed += fprintf(fp, "%.*s\t%s = %u,\n", indent, tabs,
enumerator__name(pos, cu), pos->value);
return printed + fprintf(fp, "%.*s}%s%s", indent, tabs,
conf->suffix ? " " : "", conf->suffix ?: "");
}
static const char *tag__prefix(const struct cu *cu, const uint32_t tag,
const struct conf_fprintf *conf)
{
switch (tag) {
case DW_TAG_enumeration_type: return "enum ";
case DW_TAG_structure_type:
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 ";
case DW_TAG_union_type: return "union ";
case DW_TAG_pointer_type: return " *";
case DW_TAG_reference_type: return " &";
}
return "";
}
static const char *__tag__name(const struct tag *tag, const struct cu *cu,
char *bf, size_t len,
const struct conf_fprintf *conf);
static const char *tag__ptr_name(const struct tag *tag, const struct cu *cu,
char *bf, size_t len, const char *ptr_suffix)
{
if (tag->type == 0) /* No type == void */
snprintf(bf, len, "void %s", ptr_suffix);
else {
const struct tag *type = cu__type(cu, tag->type);
if (type == NULL) {
size_t l = tag__id_not_found_snprintf(bf, len, tag->type);
snprintf(bf + l, len - l, " %s", ptr_suffix);
} else if (!tag__has_type_loop(tag, type, bf, len, NULL)) {
char tmpbf[1024];
snprintf(bf, len, "%s %s",
__tag__name(type, cu,
tmpbf, sizeof(tmpbf), NULL),
ptr_suffix);
}
}
return bf;
}
static const char *__tag__name(const struct tag *tag, const struct cu *cu,
char *bf, size_t len,
const struct conf_fprintf *conf)
{
struct tag *type;
const struct conf_fprintf *pconf = conf ?: &conf_fprintf__defaults;
if (tag == NULL)
strncpy(bf, "void", len);
else switch (tag->tag) {
case DW_TAG_base_type: {
const struct base_type *bt = tag__base_type(tag);
const char *name = "nameless base type!";
char bf2[64];
if (bt->name)
name = base_type__name(tag__base_type(tag), cu,
bf2, sizeof(bf2));
strncpy(bf, name, len);
}
break;
case DW_TAG_subprogram:
strncpy(bf, function__name(tag__function(tag), cu), len);
break;
case DW_TAG_pointer_type:
return tag__ptr_name(tag, cu, bf, len, "*");
case DW_TAG_reference_type:
return tag__ptr_name(tag, cu, bf, len, "&");
case DW_TAG_ptr_to_member_type: {
char suffix[512];
uint16_t id = tag__ptr_to_member_type(tag)->containing_type;
type = cu__type(cu, id);
if (type != NULL)
snprintf(suffix, sizeof(suffix), "%s::*",
class__name(tag__class(type), cu));
else {
size_t l = tag__id_not_found_snprintf(suffix,
sizeof(suffix),
id);
snprintf(suffix + l, sizeof(suffix) - l, "::*");
}
return tag__ptr_name(tag, cu, bf, len, suffix);
}
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:
case DW_TAG_unspecified_type:
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)) {
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 = "",
*type_str = __tag__name(type, cu, tmpbf,
sizeof(tmpbf),
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);
}
break;
case DW_TAG_array_type:
type = cu__type(cu, tag->type);
if (type == NULL)
tag__id_not_found_snprintf(bf, len, tag->type);
else if (!tag__has_type_loop(tag, type, bf, len, NULL))
return __tag__name(type, cu, bf, len, pconf);
break;
case DW_TAG_subroutine_type: {
FILE *bfp = fmemopen(bf, len, "w");
if (bfp != NULL) {
ftype__fprintf(tag__ftype(tag), cu, NULL, 0, 0, 0,
pconf, bfp);
fclose(bfp);
} else
snprintf(bf, len, "<ERROR(%s): fmemopen failed!>",
__func__);
}
break;
case DW_TAG_member:
snprintf(bf, len, "%s", class_member__name(tag__class_member(tag), cu));
break;
case DW_TAG_variable:
snprintf(bf, len, "%s", variable__name(tag__variable(tag), cu));
break;
default:
snprintf(bf, len, "%s%s", tag__prefix(cu, tag->tag, pconf),
type__name(tag__type(tag), cu) ?: "");
break;
}
return bf;
}
const char *tag__name(const struct tag *tag, const struct cu *cu,
char *bf, size_t len, const struct conf_fprintf *conf)
{
bool starts_with_const = false;
if (tag == NULL) {
strncpy(bf, "void", len);
return bf;
}
if (tag->tag == DW_TAG_const_type) {
starts_with_const = true;
tag = cu__type(cu, tag->type);
}
__tag__name(tag, cu, bf, len, conf);
if (starts_with_const)
strncat(bf, "const", len);
return bf;
}
static const char *variable__prefix(const struct variable *var)
{
switch (variable__scope(var)) {
case VSCOPE_REGISTER:
return "register ";
case VSCOPE_UNKNOWN:
if (var->external && var->declaration)
return "extern ";
break;
case VSCOPE_GLOBAL:
if (!var->external)
return "static ";
break;
case VSCOPE_LOCAL:
case VSCOPE_OPTIMIZED:
break;
}
return NULL;
}
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,
tag__nr_cachelines(type__tag(type), cu), type->nr_members);
if (type->nr_static_members != 0)
printed += fprintf(fp, ", static members: %u */\n", type->nr_static_members);
else
printed += fprintf(fp, " */\n");
return printed;
}
static size_t union__fprintf(struct type *type, const struct cu *cu,
const struct conf_fprintf *conf, FILE *fp);
static size_t __class__fprintf(struct class *class, const struct cu *cu,
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];
struct type *ctype;
struct conf_fprintf tconf;
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;
while (type->tag == DW_TAG_pointer_type && type->type != 0) {
struct tag *ttype = cu__type(cu, type->type);
if (ttype == NULL)
goto out_type_not_found;
else {
printed = tag__has_type_loop(type, ttype,
NULL, 0, fp);
if (printed)
return printed;
}
type = ttype;
++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;
}
if (expand_types) {
int typedef_expanded = 0;
while (tag__is_typedef(type)) {
struct tag *type_type;
int n;
ctype = tag__type(type);
if (typedef_expanded)
printed += fprintf(fp, " -> %s",
type__name(ctype, cu));
else {
printed += fprintf(fp, "/* typedef %s",
type__name(ctype, cu));
typedef_expanded = 1;
}
type_type = cu__type(cu, type->type);
if (type_type == NULL)
goto out_type_not_found;
n = tag__has_type_loop(type, type_type, NULL, 0, fp);
if (n)
return printed + n;
type = type_type;
}
if (typedef_expanded)
printed += fprintf(fp, " */ ");
}
if (tag__is_struct(type) || tag__is_union(type) ||
tag__is_enumeration(type)) {
tconf = *conf;
tconf.type_spacing -= 8;
tconf.prefix = NULL;
tconf.suffix = name;
tconf.emit_stats = 0;
tconf.suppress_offset_comment = suppress_offset_comment;
}
switch (type->tag) {
case DW_TAG_pointer_type:
if (type->type != 0) {
int n;
struct tag *ptype = cu__type(cu, type->type);
if (ptype == NULL)
goto out_type_not_found;
n = tag__has_type_loop(type, ptype, NULL, 0, fp);
if (n)
return printed + n;
if (ptype->tag == DW_TAG_subroutine_type) {
printed += ftype__fprintf(tag__ftype(ptype),
cu, name, 0, 1,
conf->type_spacing,
conf, fp);
break;
}
}
/* Fall Thru */
default:
printed += fprintf(fp, "%-*s %s", conf->type_spacing,
tag__name(type, cu, tbf, sizeof(tbf), conf),
name);
break;
case DW_TAG_subroutine_type:
printed += ftype__fprintf(tag__ftype(type), cu, name, 0, 0,
conf->type_spacing, conf, fp);
break;
case DW_TAG_array_type:
printed += array_type__fprintf(type, cu, name, conf, fp);
break;
case DW_TAG_class_type:
case DW_TAG_structure_type:
ctype = tag__type(type);
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
if (type__name(ctype, cu) != NULL && !expand_types) {
printed += fprintf(fp, "%s %-*s %s",
(type->tag == DW_TAG_class_type &&
!conf->classes_as_structs) ? "class" : "struct",
conf->type_spacing - 7,
type__name(ctype, cu), 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);
if (!conf->suppress_comments)
class__find_holes(cclass);
printed += __class__fprintf(cclass, cu, &tconf, fp);
}
break;
case DW_TAG_union_type:
ctype = tag__type(type);
if (type__name(ctype, cu) != NULL && !expand_types)
printed += fprintf(fp, "union %-*s %s",
conf->type_spacing - 6,
type__name(ctype, cu), name);
else
printed += union__fprintf(ctype, cu, &tconf, fp);
break;
case DW_TAG_enumeration_type:
ctype = tag__type(type);
if (type__name(ctype, cu) != NULL)
printed += fprintf(fp, "enum %-*s %s",
conf->type_spacing - 5,
type__name(ctype, cu), name);
else
printed += enumeration__fprintf(type, cu, &tconf, fp);
break;
}
out:
if (conf->expand_types)
--type->recursivity_level;
return printed;
out_type_not_found:
printed = fprintf(fp, "%-*s %s", conf->type_spacing, "<ERROR>", name);
goto out;
}
dwarves_fprintf: Fixup cacheline boundary printing on expanded structs A diff for 'pahole -EC task_struct vmlinux' should clarify what this fixes: [acme@jouet linux]$ diff -u /tmp/before.c /tmp/after.c | head -30 --- /tmp/before.c 2016-06-29 17:00:38.082647281 -0300 +++ /tmp/a.c 2016-06-29 17:03:36.913124779 -0300 @@ -43,8 +43,8 @@ struct list_head * prev; /* 176 8 */ } group_node; /* 168 16 */ unsigned int on_rq; /* 184 4 */ + /* --- cacheline 3 boundary (192 bytes) --- */ /* typedef u64 */ long long unsigned int exec_start; /* 192 8 */ - /* --- cacheline 1 boundary (64 bytes) was 4 bytes ago --- */ /* typedef u64 */ long long unsigned int sum_exec_runtime; /* 200 8 */ /* typedef u64 */ long long unsigned int vruntime; /* 208 8 */ /* typedef u64 */ long long unsigned int prev_sum_exec_runtime; /* 216 8 */ @@ -53,40 +53,40 @@ /* typedef u64 */ long long unsigned int wait_start; /* 232 8 */ /* typedef u64 */ long long unsigned int wait_max; /* 240 8 */ /* typedef u64 */ long long unsigned int wait_count; /* 248 8 */ + /* --- cacheline 4 boundary (256 bytes) --- */ /* typedef u64 */ long long unsigned int wait_sum; /* 256 8 */ /* typedef u64 */ long long unsigned int iowait_count; /* 264 8 */ /* typedef u64 */ long long unsigned int iowait_sum; /* 272 8 */ /* typedef u64 */ long long unsigned int sleep_start; /* 280 8 */ /* typedef u64 */ long long unsigned int sleep_max; /* 288 8 */ - /* --- cacheline 1 boundary (64 bytes) --- */ /* typedef s64 */ long long int sum_sleep_runtime; /* 296 8 */ /* typedef u64 */ long long unsigned int block_start; /* 304 8 */ /* typedef u64 */ long long unsigned int block_max; /* 312 8 */ + /* --- cacheline 5 boundary (320 bytes) --- */ /* typedef u64 */ long long unsigned int exec_max; /* 320 8 */ /* typedef u64 */ long long unsigned int slice_max; /* 328 8 */ /* typedef u64 */ long long unsigned int nr_migrations_cold; /* 336 8 */ [acme@jouet linux]$ I.e. the boundary detection was being reset at each expanded struct, do the math globally, using the member offset, that was already done globally and correctly. Reported-and-Tested-by: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-06-29 22:27:51 +02:00
static size_t class__fprintf_cacheline_boundary(struct conf_fprintf *conf,
uint32_t offset,
FILE *fp);
dwarves_fprintf: Show offsets at union members In complex structs with multiple complex unions figuring out the offset for a given union member is difficult, as one needs to figure out the union, go to the end of it to see the offset. So just turn struct_member__fprintf() into class_member__fprintf() and pass a 'union_member' boolean to share all the aspects of struct and union members, just not advancing the offset when processing union members. This way, for instance, the Linux kernel's 'struct page' goes from: struct page { long unsigned int flags; /* 0 8 */ union { struct address_space * mapping; /* 8 8 */ void * s_mem; /* 8 8 */ atomic_t compound_mapcount; /* 8 4 */ }; /* 8 8 */ union { long unsigned int index; /* 16 8 */ void * freelist; /* 16 8 */ }; /* 16 8 */ union { long unsigned int counters; /* 24 8 */ struct { union { atomic_t _mapcount; /* 24 4 */ unsigned int active; /* 24 4 */ struct { unsigned int inuse:16; /* 24:16 4 */ unsigned int objects:15; /* 24: 1 4 */ unsigned int frozen:1; /* 24: 0 4 */ }; /* 24 4 */ int units; /* 24 4 */ }; /* 24 4 */ atomic_t _refcount; /* 28 4 */ }; /* 24 8 */ }; /* 24 8 */ union { struct list_head lru; /* 32 16 */ struct dev_pagemap * pgmap; /* 32 8 */ struct { struct page * next; /* 32 8 */ int pages; /* 40 4 */ int pobjects; /* 44 4 */ }; /* 32 16 */ struct callback_head callback_head; /* 32 16 */ struct { long unsigned int compound_head; /* 32 8 */ unsigned int compound_dtor; /* 40 4 */ unsigned int compound_order; /* 44 4 */ }; /* 32 16 */ struct { long unsigned int __pad; /* 32 8 */ pgtable_t pmd_huge_pte; /* 40 8 */ }; /* 32 16 */ }; /* 32 16 */ union { long unsigned int private; /* 48 8 */ spinlock_t ptl; /* 48 4 */ struct kmem_cache * slab_cache; /* 48 8 */ }; /* 48 8 */ struct mem_cgroup * mem_cgroup; /* 56 8 */ /* size: 64, cachelines: 1, members: 7 */ }; To: struct page { long unsigned int flags; /* 0 8 */ union { struct address_space * mapping; /* 8 8 */ void * s_mem; /* 8 8 */ atomic_t compound_mapcount; /* 8 4 */ }; /* 8 8 */ union { long unsigned int index; /* 16 8 */ void * freelist; /* 16 8 */ }; /* 16 8 */ union { long unsigned int counters; /* 24 8 */ struct { union { atomic_t _mapcount; /* 24 4 */ unsigned int active; /* 24 4 */ struct { unsigned int inuse:16; /* 24:16 4 */ unsigned int objects:15; /* 24: 1 4 */ unsigned int frozen:1; /* 24: 0 4 */ }; /* 24 4 */ int units; /* 24 4 */ }; /* 24 4 */ atomic_t _refcount; /* 28 4 */ }; /* 24 8 */ }; /* 24 8 */ union { struct list_head lru; /* 32 16 */ struct dev_pagemap * pgmap; /* 32 8 */ struct { struct page * next; /* 32 8 */ int pages; /* 40 4 */ int pobjects; /* 44 4 */ }; /* 32 16 */ struct callback_head callback_head; /* 32 16 */ struct { long unsigned int compound_head; /* 32 8 */ unsigned int compound_dtor; /* 40 4 */ unsigned int compound_order; /* 44 4 */ }; /* 32 16 */ struct { long unsigned int __pad; /* 32 8 */ pgtable_t pmd_huge_pte; /* 40 8 */ }; /* 32 16 */ }; /* 32 16 */ union { long unsigned int private; /* 48 8 */ spinlock_t ptl; /* 48 4 */ struct kmem_cache * slab_cache; /* 48 8 */ }; /* 48 8 */ struct mem_cgroup * mem_cgroup; /* 56 8 */ /* size: 64, cachelines: 1, members: 7 */ }; Suggested-by: Matthew Wilcox <willy@infradead.org> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2017-12-15 17:33:10 +01:00
static size_t class_member__fprintf(struct class_member *member, bool union_member,
struct tag *type, const struct cu *cu,
dwarves_fprintf: Fixup cacheline boundary printing on expanded structs A diff for 'pahole -EC task_struct vmlinux' should clarify what this fixes: [acme@jouet linux]$ diff -u /tmp/before.c /tmp/after.c | head -30 --- /tmp/before.c 2016-06-29 17:00:38.082647281 -0300 +++ /tmp/a.c 2016-06-29 17:03:36.913124779 -0300 @@ -43,8 +43,8 @@ struct list_head * prev; /* 176 8 */ } group_node; /* 168 16 */ unsigned int on_rq; /* 184 4 */ + /* --- cacheline 3 boundary (192 bytes) --- */ /* typedef u64 */ long long unsigned int exec_start; /* 192 8 */ - /* --- cacheline 1 boundary (64 bytes) was 4 bytes ago --- */ /* typedef u64 */ long long unsigned int sum_exec_runtime; /* 200 8 */ /* typedef u64 */ long long unsigned int vruntime; /* 208 8 */ /* typedef u64 */ long long unsigned int prev_sum_exec_runtime; /* 216 8 */ @@ -53,40 +53,40 @@ /* typedef u64 */ long long unsigned int wait_start; /* 232 8 */ /* typedef u64 */ long long unsigned int wait_max; /* 240 8 */ /* typedef u64 */ long long unsigned int wait_count; /* 248 8 */ + /* --- cacheline 4 boundary (256 bytes) --- */ /* typedef u64 */ long long unsigned int wait_sum; /* 256 8 */ /* typedef u64 */ long long unsigned int iowait_count; /* 264 8 */ /* typedef u64 */ long long unsigned int iowait_sum; /* 272 8 */ /* typedef u64 */ long long unsigned int sleep_start; /* 280 8 */ /* typedef u64 */ long long unsigned int sleep_max; /* 288 8 */ - /* --- cacheline 1 boundary (64 bytes) --- */ /* typedef s64 */ long long int sum_sleep_runtime; /* 296 8 */ /* typedef u64 */ long long unsigned int block_start; /* 304 8 */ /* typedef u64 */ long long unsigned int block_max; /* 312 8 */ + /* --- cacheline 5 boundary (320 bytes) --- */ /* typedef u64 */ long long unsigned int exec_max; /* 320 8 */ /* typedef u64 */ long long unsigned int slice_max; /* 328 8 */ /* typedef u64 */ long long unsigned int nr_migrations_cold; /* 336 8 */ [acme@jouet linux]$ I.e. the boundary detection was being reset at each expanded struct, do the math globally, using the member offset, that was already done globally and correctly. Reported-and-Tested-by: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-06-29 22:27:51 +02:00
struct conf_fprintf *conf, FILE *fp)
{
const int size = member->byte_size;
struct conf_fprintf sconf = *conf;
uint32_t offset = member->byte_offset;
dwarves_fprintf: Fixup cacheline boundary printing on expanded structs A diff for 'pahole -EC task_struct vmlinux' should clarify what this fixes: [acme@jouet linux]$ diff -u /tmp/before.c /tmp/after.c | head -30 --- /tmp/before.c 2016-06-29 17:00:38.082647281 -0300 +++ /tmp/a.c 2016-06-29 17:03:36.913124779 -0300 @@ -43,8 +43,8 @@ struct list_head * prev; /* 176 8 */ } group_node; /* 168 16 */ unsigned int on_rq; /* 184 4 */ + /* --- cacheline 3 boundary (192 bytes) --- */ /* typedef u64 */ long long unsigned int exec_start; /* 192 8 */ - /* --- cacheline 1 boundary (64 bytes) was 4 bytes ago --- */ /* typedef u64 */ long long unsigned int sum_exec_runtime; /* 200 8 */ /* typedef u64 */ long long unsigned int vruntime; /* 208 8 */ /* typedef u64 */ long long unsigned int prev_sum_exec_runtime; /* 216 8 */ @@ -53,40 +53,40 @@ /* typedef u64 */ long long unsigned int wait_start; /* 232 8 */ /* typedef u64 */ long long unsigned int wait_max; /* 240 8 */ /* typedef u64 */ long long unsigned int wait_count; /* 248 8 */ + /* --- cacheline 4 boundary (256 bytes) --- */ /* typedef u64 */ long long unsigned int wait_sum; /* 256 8 */ /* typedef u64 */ long long unsigned int iowait_count; /* 264 8 */ /* typedef u64 */ long long unsigned int iowait_sum; /* 272 8 */ /* typedef u64 */ long long unsigned int sleep_start; /* 280 8 */ /* typedef u64 */ long long unsigned int sleep_max; /* 288 8 */ - /* --- cacheline 1 boundary (64 bytes) --- */ /* typedef s64 */ long long int sum_sleep_runtime; /* 296 8 */ /* typedef u64 */ long long unsigned int block_start; /* 304 8 */ /* typedef u64 */ long long unsigned int block_max; /* 312 8 */ + /* --- cacheline 5 boundary (320 bytes) --- */ /* typedef u64 */ long long unsigned int exec_max; /* 320 8 */ /* typedef u64 */ long long unsigned int slice_max; /* 328 8 */ /* typedef u64 */ long long unsigned int nr_migrations_cold; /* 336 8 */ [acme@jouet linux]$ I.e. the boundary detection was being reset at each expanded struct, do the math globally, using the member offset, that was already done globally and correctly. Reported-and-Tested-by: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-06-29 22:27:51 +02:00
size_t printed = 0, printed_cacheline = 0;
const char *cm_name = class_member__name(member, cu),
*name = cm_name;
if (!sconf.rel_offset) {
dwarves_fprintf: Show offsets at union members In complex structs with multiple complex unions figuring out the offset for a given union member is difficult, as one needs to figure out the union, go to the end of it to see the offset. So just turn struct_member__fprintf() into class_member__fprintf() and pass a 'union_member' boolean to share all the aspects of struct and union members, just not advancing the offset when processing union members. This way, for instance, the Linux kernel's 'struct page' goes from: struct page { long unsigned int flags; /* 0 8 */ union { struct address_space * mapping; /* 8 8 */ void * s_mem; /* 8 8 */ atomic_t compound_mapcount; /* 8 4 */ }; /* 8 8 */ union { long unsigned int index; /* 16 8 */ void * freelist; /* 16 8 */ }; /* 16 8 */ union { long unsigned int counters; /* 24 8 */ struct { union { atomic_t _mapcount; /* 24 4 */ unsigned int active; /* 24 4 */ struct { unsigned int inuse:16; /* 24:16 4 */ unsigned int objects:15; /* 24: 1 4 */ unsigned int frozen:1; /* 24: 0 4 */ }; /* 24 4 */ int units; /* 24 4 */ }; /* 24 4 */ atomic_t _refcount; /* 28 4 */ }; /* 24 8 */ }; /* 24 8 */ union { struct list_head lru; /* 32 16 */ struct dev_pagemap * pgmap; /* 32 8 */ struct { struct page * next; /* 32 8 */ int pages; /* 40 4 */ int pobjects; /* 44 4 */ }; /* 32 16 */ struct callback_head callback_head; /* 32 16 */ struct { long unsigned int compound_head; /* 32 8 */ unsigned int compound_dtor; /* 40 4 */ unsigned int compound_order; /* 44 4 */ }; /* 32 16 */ struct { long unsigned int __pad; /* 32 8 */ pgtable_t pmd_huge_pte; /* 40 8 */ }; /* 32 16 */ }; /* 32 16 */ union { long unsigned int private; /* 48 8 */ spinlock_t ptl; /* 48 4 */ struct kmem_cache * slab_cache; /* 48 8 */ }; /* 48 8 */ struct mem_cgroup * mem_cgroup; /* 56 8 */ /* size: 64, cachelines: 1, members: 7 */ }; To: struct page { long unsigned int flags; /* 0 8 */ union { struct address_space * mapping; /* 8 8 */ void * s_mem; /* 8 8 */ atomic_t compound_mapcount; /* 8 4 */ }; /* 8 8 */ union { long unsigned int index; /* 16 8 */ void * freelist; /* 16 8 */ }; /* 16 8 */ union { long unsigned int counters; /* 24 8 */ struct { union { atomic_t _mapcount; /* 24 4 */ unsigned int active; /* 24 4 */ struct { unsigned int inuse:16; /* 24:16 4 */ unsigned int objects:15; /* 24: 1 4 */ unsigned int frozen:1; /* 24: 0 4 */ }; /* 24 4 */ int units; /* 24 4 */ }; /* 24 4 */ atomic_t _refcount; /* 28 4 */ }; /* 24 8 */ }; /* 24 8 */ union { struct list_head lru; /* 32 16 */ struct dev_pagemap * pgmap; /* 32 8 */ struct { struct page * next; /* 32 8 */ int pages; /* 40 4 */ int pobjects; /* 44 4 */ }; /* 32 16 */ struct callback_head callback_head; /* 32 16 */ struct { long unsigned int compound_head; /* 32 8 */ unsigned int compound_dtor; /* 40 4 */ unsigned int compound_order; /* 44 4 */ }; /* 32 16 */ struct { long unsigned int __pad; /* 32 8 */ pgtable_t pmd_huge_pte; /* 40 8 */ }; /* 32 16 */ }; /* 32 16 */ union { long unsigned int private; /* 48 8 */ spinlock_t ptl; /* 48 4 */ struct kmem_cache * slab_cache; /* 48 8 */ }; /* 48 8 */ struct mem_cgroup * mem_cgroup; /* 56 8 */ /* size: 64, cachelines: 1, members: 7 */ }; Suggested-by: Matthew Wilcox <willy@infradead.org> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2017-12-15 17:33:10 +01:00
offset += sconf.base_offset;
if (!union_member)
sconf.base_offset = offset;
}
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;
dwarves_fprintf: Fixup cacheline boundary printing on expanded structs A diff for 'pahole -EC task_struct vmlinux' should clarify what this fixes: [acme@jouet linux]$ diff -u /tmp/before.c /tmp/after.c | head -30 --- /tmp/before.c 2016-06-29 17:00:38.082647281 -0300 +++ /tmp/a.c 2016-06-29 17:03:36.913124779 -0300 @@ -43,8 +43,8 @@ struct list_head * prev; /* 176 8 */ } group_node; /* 168 16 */ unsigned int on_rq; /* 184 4 */ + /* --- cacheline 3 boundary (192 bytes) --- */ /* typedef u64 */ long long unsigned int exec_start; /* 192 8 */ - /* --- cacheline 1 boundary (64 bytes) was 4 bytes ago --- */ /* typedef u64 */ long long unsigned int sum_exec_runtime; /* 200 8 */ /* typedef u64 */ long long unsigned int vruntime; /* 208 8 */ /* typedef u64 */ long long unsigned int prev_sum_exec_runtime; /* 216 8 */ @@ -53,40 +53,40 @@ /* typedef u64 */ long long unsigned int wait_start; /* 232 8 */ /* typedef u64 */ long long unsigned int wait_max; /* 240 8 */ /* typedef u64 */ long long unsigned int wait_count; /* 248 8 */ + /* --- cacheline 4 boundary (256 bytes) --- */ /* typedef u64 */ long long unsigned int wait_sum; /* 256 8 */ /* typedef u64 */ long long unsigned int iowait_count; /* 264 8 */ /* typedef u64 */ long long unsigned int iowait_sum; /* 272 8 */ /* typedef u64 */ long long unsigned int sleep_start; /* 280 8 */ /* typedef u64 */ long long unsigned int sleep_max; /* 288 8 */ - /* --- cacheline 1 boundary (64 bytes) --- */ /* typedef s64 */ long long int sum_sleep_runtime; /* 296 8 */ /* typedef u64 */ long long unsigned int block_start; /* 304 8 */ /* typedef u64 */ long long unsigned int block_max; /* 312 8 */ + /* --- cacheline 5 boundary (320 bytes) --- */ /* typedef u64 */ long long unsigned int exec_max; /* 320 8 */ /* typedef u64 */ long long unsigned int slice_max; /* 328 8 */ /* typedef u64 */ long long unsigned int nr_migrations_cold; /* 336 8 */ [acme@jouet linux]$ I.e. the boundary detection was being reset at each expanded struct, do the math globally, using the member offset, that was already done globally and correctly. Reported-and-Tested-by: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-06-29 22:27:51 +02:00
if (!conf->suppress_comments)
printed_cacheline = class__fprintf_cacheline_boundary(conf, offset, fp);
if (member->tag.tag == DW_TAG_inheritance) {
name = "<ancestor>";
printed += fprintf(fp, "/* ");
}
if (member->is_static)
printed += fprintf(fp, "static ");
printed += type__fprintf(type, cu, name, &sconf, fp);
if (member->is_static) {
if (member->const_value != 0)
printed += fprintf(fp, " = %" PRIu64 ";", member->const_value);
} else if (member->bitfield_size != 0) {
printed += fprintf(fp, ":%u;", member->bitfield_size);
} else {
fputc(';', fp);
++printed;
}
if ((tag__is_union(type) || tag__is_struct(type) ||
tag__is_enumeration(type)) &&
/* Look if is a type defined inline */
type__name(tag__type(type), cu) == NULL) {
if (!sconf.suppress_offset_comment) {
/* Check if this is a anonymous union */
const int slen = cm_name ? (int)strlen(cm_name) : -1;
printed += fprintf(fp, sconf.hex_fmt ?
"%*s/* %#5x %#5x */" :
"%*s/* %5u %5u */",
(sconf.type_spacing +
sconf.name_spacing - slen - 3),
" ", offset, size);
}
} else {
int spacing = sconf.type_spacing + sconf.name_spacing - printed;
if (member->tag.tag == DW_TAG_inheritance) {
const size_t p = fprintf(fp, " */");
printed += p;
spacing -= p;
}
if (!sconf.suppress_offset_comment) {
int size_spacing = 5;
printed += fprintf(fp, sconf.hex_fmt ?
"%*s/* %#5x" : "%*s/* %5u",
spacing > 0 ? spacing : 0, " ",
offset);
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;
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);
size_spacing -= 3;
}
printed += fprintf(fp, sconf.hex_fmt ?
" %#*x */" : " %*u */",
size_spacing, size);
}
}
dwarves_fprintf: Fixup cacheline boundary printing on expanded structs A diff for 'pahole -EC task_struct vmlinux' should clarify what this fixes: [acme@jouet linux]$ diff -u /tmp/before.c /tmp/after.c | head -30 --- /tmp/before.c 2016-06-29 17:00:38.082647281 -0300 +++ /tmp/a.c 2016-06-29 17:03:36.913124779 -0300 @@ -43,8 +43,8 @@ struct list_head * prev; /* 176 8 */ } group_node; /* 168 16 */ unsigned int on_rq; /* 184 4 */ + /* --- cacheline 3 boundary (192 bytes) --- */ /* typedef u64 */ long long unsigned int exec_start; /* 192 8 */ - /* --- cacheline 1 boundary (64 bytes) was 4 bytes ago --- */ /* typedef u64 */ long long unsigned int sum_exec_runtime; /* 200 8 */ /* typedef u64 */ long long unsigned int vruntime; /* 208 8 */ /* typedef u64 */ long long unsigned int prev_sum_exec_runtime; /* 216 8 */ @@ -53,40 +53,40 @@ /* typedef u64 */ long long unsigned int wait_start; /* 232 8 */ /* typedef u64 */ long long unsigned int wait_max; /* 240 8 */ /* typedef u64 */ long long unsigned int wait_count; /* 248 8 */ + /* --- cacheline 4 boundary (256 bytes) --- */ /* typedef u64 */ long long unsigned int wait_sum; /* 256 8 */ /* typedef u64 */ long long unsigned int iowait_count; /* 264 8 */ /* typedef u64 */ long long unsigned int iowait_sum; /* 272 8 */ /* typedef u64 */ long long unsigned int sleep_start; /* 280 8 */ /* typedef u64 */ long long unsigned int sleep_max; /* 288 8 */ - /* --- cacheline 1 boundary (64 bytes) --- */ /* typedef s64 */ long long int sum_sleep_runtime; /* 296 8 */ /* typedef u64 */ long long unsigned int block_start; /* 304 8 */ /* typedef u64 */ long long unsigned int block_max; /* 312 8 */ + /* --- cacheline 5 boundary (320 bytes) --- */ /* typedef u64 */ long long unsigned int exec_max; /* 320 8 */ /* typedef u64 */ long long unsigned int slice_max; /* 328 8 */ /* typedef u64 */ long long unsigned int nr_migrations_cold; /* 336 8 */ [acme@jouet linux]$ I.e. the boundary detection was being reset at each expanded struct, do the math globally, using the member offset, that was already done globally and correctly. Reported-and-Tested-by: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-06-29 22:27:51 +02:00
return printed + printed_cacheline;
}
dwarves_fprintf: Show offsets at union members In complex structs with multiple complex unions figuring out the offset for a given union member is difficult, as one needs to figure out the union, go to the end of it to see the offset. So just turn struct_member__fprintf() into class_member__fprintf() and pass a 'union_member' boolean to share all the aspects of struct and union members, just not advancing the offset when processing union members. This way, for instance, the Linux kernel's 'struct page' goes from: struct page { long unsigned int flags; /* 0 8 */ union { struct address_space * mapping; /* 8 8 */ void * s_mem; /* 8 8 */ atomic_t compound_mapcount; /* 8 4 */ }; /* 8 8 */ union { long unsigned int index; /* 16 8 */ void * freelist; /* 16 8 */ }; /* 16 8 */ union { long unsigned int counters; /* 24 8 */ struct { union { atomic_t _mapcount; /* 24 4 */ unsigned int active; /* 24 4 */ struct { unsigned int inuse:16; /* 24:16 4 */ unsigned int objects:15; /* 24: 1 4 */ unsigned int frozen:1; /* 24: 0 4 */ }; /* 24 4 */ int units; /* 24 4 */ }; /* 24 4 */ atomic_t _refcount; /* 28 4 */ }; /* 24 8 */ }; /* 24 8 */ union { struct list_head lru; /* 32 16 */ struct dev_pagemap * pgmap; /* 32 8 */ struct { struct page * next; /* 32 8 */ int pages; /* 40 4 */ int pobjects; /* 44 4 */ }; /* 32 16 */ struct callback_head callback_head; /* 32 16 */ struct { long unsigned int compound_head; /* 32 8 */ unsigned int compound_dtor; /* 40 4 */ unsigned int compound_order; /* 44 4 */ }; /* 32 16 */ struct { long unsigned int __pad; /* 32 8 */ pgtable_t pmd_huge_pte; /* 40 8 */ }; /* 32 16 */ }; /* 32 16 */ union { long unsigned int private; /* 48 8 */ spinlock_t ptl; /* 48 4 */ struct kmem_cache * slab_cache; /* 48 8 */ }; /* 48 8 */ struct mem_cgroup * mem_cgroup; /* 56 8 */ /* size: 64, cachelines: 1, members: 7 */ }; To: struct page { long unsigned int flags; /* 0 8 */ union { struct address_space * mapping; /* 8 8 */ void * s_mem; /* 8 8 */ atomic_t compound_mapcount; /* 8 4 */ }; /* 8 8 */ union { long unsigned int index; /* 16 8 */ void * freelist; /* 16 8 */ }; /* 16 8 */ union { long unsigned int counters; /* 24 8 */ struct { union { atomic_t _mapcount; /* 24 4 */ unsigned int active; /* 24 4 */ struct { unsigned int inuse:16; /* 24:16 4 */ unsigned int objects:15; /* 24: 1 4 */ unsigned int frozen:1; /* 24: 0 4 */ }; /* 24 4 */ int units; /* 24 4 */ }; /* 24 4 */ atomic_t _refcount; /* 28 4 */ }; /* 24 8 */ }; /* 24 8 */ union { struct list_head lru; /* 32 16 */ struct dev_pagemap * pgmap; /* 32 8 */ struct { struct page * next; /* 32 8 */ int pages; /* 40 4 */ int pobjects; /* 44 4 */ }; /* 32 16 */ struct callback_head callback_head; /* 32 16 */ struct { long unsigned int compound_head; /* 32 8 */ unsigned int compound_dtor; /* 40 4 */ unsigned int compound_order; /* 44 4 */ }; /* 32 16 */ struct { long unsigned int __pad; /* 32 8 */ pgtable_t pmd_huge_pte; /* 40 8 */ }; /* 32 16 */ }; /* 32 16 */ union { long unsigned int private; /* 48 8 */ spinlock_t ptl; /* 48 4 */ struct kmem_cache * slab_cache; /* 48 8 */ }; /* 48 8 */ struct mem_cgroup * mem_cgroup; /* 56 8 */ /* size: 64, cachelines: 1, members: 7 */ }; Suggested-by: Matthew Wilcox <willy@infradead.org> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
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);
}
static size_t union_member__fprintf(struct class_member *member,
struct tag *type, const struct cu *cu,
dwarves_fprintf: Show offsets at union members In complex structs with multiple complex unions figuring out the offset for a given union member is difficult, as one needs to figure out the union, go to the end of it to see the offset. So just turn struct_member__fprintf() into class_member__fprintf() and pass a 'union_member' boolean to share all the aspects of struct and union members, just not advancing the offset when processing union members. This way, for instance, the Linux kernel's 'struct page' goes from: struct page { long unsigned int flags; /* 0 8 */ union { struct address_space * mapping; /* 8 8 */ void * s_mem; /* 8 8 */ atomic_t compound_mapcount; /* 8 4 */ }; /* 8 8 */ union { long unsigned int index; /* 16 8 */ void * freelist; /* 16 8 */ }; /* 16 8 */ union { long unsigned int counters; /* 24 8 */ struct { union { atomic_t _mapcount; /* 24 4 */ unsigned int active; /* 24 4 */ struct { unsigned int inuse:16; /* 24:16 4 */ unsigned int objects:15; /* 24: 1 4 */ unsigned int frozen:1; /* 24: 0 4 */ }; /* 24 4 */ int units; /* 24 4 */ }; /* 24 4 */ atomic_t _refcount; /* 28 4 */ }; /* 24 8 */ }; /* 24 8 */ union { struct list_head lru; /* 32 16 */ struct dev_pagemap * pgmap; /* 32 8 */ struct { struct page * next; /* 32 8 */ int pages; /* 40 4 */ int pobjects; /* 44 4 */ }; /* 32 16 */ struct callback_head callback_head; /* 32 16 */ struct { long unsigned int compound_head; /* 32 8 */ unsigned int compound_dtor; /* 40 4 */ unsigned int compound_order; /* 44 4 */ }; /* 32 16 */ struct { long unsigned int __pad; /* 32 8 */ pgtable_t pmd_huge_pte; /* 40 8 */ }; /* 32 16 */ }; /* 32 16 */ union { long unsigned int private; /* 48 8 */ spinlock_t ptl; /* 48 4 */ struct kmem_cache * slab_cache; /* 48 8 */ }; /* 48 8 */ struct mem_cgroup * mem_cgroup; /* 56 8 */ /* size: 64, cachelines: 1, members: 7 */ }; To: struct page { long unsigned int flags; /* 0 8 */ union { struct address_space * mapping; /* 8 8 */ void * s_mem; /* 8 8 */ atomic_t compound_mapcount; /* 8 4 */ }; /* 8 8 */ union { long unsigned int index; /* 16 8 */ void * freelist; /* 16 8 */ }; /* 16 8 */ union { long unsigned int counters; /* 24 8 */ struct { union { atomic_t _mapcount; /* 24 4 */ unsigned int active; /* 24 4 */ struct { unsigned int inuse:16; /* 24:16 4 */ unsigned int objects:15; /* 24: 1 4 */ unsigned int frozen:1; /* 24: 0 4 */ }; /* 24 4 */ int units; /* 24 4 */ }; /* 24 4 */ atomic_t _refcount; /* 28 4 */ }; /* 24 8 */ }; /* 24 8 */ union { struct list_head lru; /* 32 16 */ struct dev_pagemap * pgmap; /* 32 8 */ struct { struct page * next; /* 32 8 */ int pages; /* 40 4 */ int pobjects; /* 44 4 */ }; /* 32 16 */ struct callback_head callback_head; /* 32 16 */ struct { long unsigned int compound_head; /* 32 8 */ unsigned int compound_dtor; /* 40 4 */ unsigned int compound_order; /* 44 4 */ }; /* 32 16 */ struct { long unsigned int __pad; /* 32 8 */ pgtable_t pmd_huge_pte; /* 40 8 */ }; /* 32 16 */ }; /* 32 16 */ union { long unsigned int private; /* 48 8 */ spinlock_t ptl; /* 48 4 */ struct kmem_cache * slab_cache; /* 48 8 */ }; /* 48 8 */ struct mem_cgroup * mem_cgroup; /* 56 8 */ /* size: 64, cachelines: 1, members: 7 */ }; Suggested-by: Matthew Wilcox <willy@infradead.org> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2017-12-15 17:33:10 +01:00
struct conf_fprintf *conf, FILE *fp)
{
dwarves_fprintf: Show offsets at union members In complex structs with multiple complex unions figuring out the offset for a given union member is difficult, as one needs to figure out the union, go to the end of it to see the offset. So just turn struct_member__fprintf() into class_member__fprintf() and pass a 'union_member' boolean to share all the aspects of struct and union members, just not advancing the offset when processing union members. This way, for instance, the Linux kernel's 'struct page' goes from: struct page { long unsigned int flags; /* 0 8 */ union { struct address_space * mapping; /* 8 8 */ void * s_mem; /* 8 8 */ atomic_t compound_mapcount; /* 8 4 */ }; /* 8 8 */ union { long unsigned int index; /* 16 8 */ void * freelist; /* 16 8 */ }; /* 16 8 */ union { long unsigned int counters; /* 24 8 */ struct { union { atomic_t _mapcount; /* 24 4 */ unsigned int active; /* 24 4 */ struct { unsigned int inuse:16; /* 24:16 4 */ unsigned int objects:15; /* 24: 1 4 */ unsigned int frozen:1; /* 24: 0 4 */ }; /* 24 4 */ int units; /* 24 4 */ }; /* 24 4 */ atomic_t _refcount; /* 28 4 */ }; /* 24 8 */ }; /* 24 8 */ union { struct list_head lru; /* 32 16 */ struct dev_pagemap * pgmap; /* 32 8 */ struct { struct page * next; /* 32 8 */ int pages; /* 40 4 */ int pobjects; /* 44 4 */ }; /* 32 16 */ struct callback_head callback_head; /* 32 16 */ struct { long unsigned int compound_head; /* 32 8 */ unsigned int compound_dtor; /* 40 4 */ unsigned int compound_order; /* 44 4 */ }; /* 32 16 */ struct { long unsigned int __pad; /* 32 8 */ pgtable_t pmd_huge_pte; /* 40 8 */ }; /* 32 16 */ }; /* 32 16 */ union { long unsigned int private; /* 48 8 */ spinlock_t ptl; /* 48 4 */ struct kmem_cache * slab_cache; /* 48 8 */ }; /* 48 8 */ struct mem_cgroup * mem_cgroup; /* 56 8 */ /* size: 64, cachelines: 1, members: 7 */ }; To: struct page { long unsigned int flags; /* 0 8 */ union { struct address_space * mapping; /* 8 8 */ void * s_mem; /* 8 8 */ atomic_t compound_mapcount; /* 8 4 */ }; /* 8 8 */ union { long unsigned int index; /* 16 8 */ void * freelist; /* 16 8 */ }; /* 16 8 */ union { long unsigned int counters; /* 24 8 */ struct { union { atomic_t _mapcount; /* 24 4 */ unsigned int active; /* 24 4 */ struct { unsigned int inuse:16; /* 24:16 4 */ unsigned int objects:15; /* 24: 1 4 */ unsigned int frozen:1; /* 24: 0 4 */ }; /* 24 4 */ int units; /* 24 4 */ }; /* 24 4 */ atomic_t _refcount; /* 28 4 */ }; /* 24 8 */ }; /* 24 8 */ union { struct list_head lru; /* 32 16 */ struct dev_pagemap * pgmap; /* 32 8 */ struct { struct page * next; /* 32 8 */ int pages; /* 40 4 */ int pobjects; /* 44 4 */ }; /* 32 16 */ struct callback_head callback_head; /* 32 16 */ struct { long unsigned int compound_head; /* 32 8 */ unsigned int compound_dtor; /* 40 4 */ unsigned int compound_order; /* 44 4 */ }; /* 32 16 */ struct { long unsigned int __pad; /* 32 8 */ pgtable_t pmd_huge_pte; /* 40 8 */ }; /* 32 16 */ }; /* 32 16 */ union { long unsigned int private; /* 48 8 */ spinlock_t ptl; /* 48 4 */ struct kmem_cache * slab_cache; /* 48 8 */ }; /* 48 8 */ struct mem_cgroup * mem_cgroup; /* 56 8 */ /* size: 64, cachelines: 1, members: 7 */ }; Suggested-by: Matthew Wilcox <willy@infradead.org> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2017-12-15 17:33:10 +01:00
return class_member__fprintf(member, true, type, cu, conf, fp);
}
static size_t union__fprintf(struct type *type, const struct cu *cu,
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;
int cacheline = 0; /* This will only be used if this is the outermost union */
if (indent >= (int)sizeof(tabs))
indent = sizeof(tabs) - 1;
if (conf->prefix != NULL)
printed += fprintf(fp, "%s ", conf->prefix);
printed += fprintf(fp, "union%s%s {\n", type__name(type, cu) ? " " : "",
type__name(type, cu) ?: "");
uconf = *conf;
uconf.indent = indent + 1;
/*
* 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;
type__for_each_member(type, pos) {
struct tag *type = cu__type(cu, pos->tag.type);
if (type == NULL) {
printed += fprintf(fp, "%.*s", uconf.indent, tabs);
printed += tag__id_not_found_fprintf(fp, pos->tag.type);
continue;
}
printed += fprintf(fp, "%.*s", uconf.indent, tabs);
printed += union_member__fprintf(pos, type, cu, &uconf, fp);
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;
}
return printed + fprintf(fp, "%.*s}%s%s", indent, tabs,
conf->suffix ? " " : "", conf->suffix ?: "");
}
const char *function__prototype(const struct function *func,
const struct cu *cu, char *bf, size_t len)
{
FILE *bfp = fmemopen(bf, len, "w");
if (bfp != NULL) {
ftype__fprintf(&func->proto, cu, NULL, 0, 0, 0,
&conf_fprintf__defaults, bfp);
fclose(bfp);
} else
snprintf(bf, len, "<ERROR(%s): fmemopen failed!>", __func__);
return bf;
}
size_t ftype__fprintf_parms(const struct ftype *ftype,
const struct cu *cu, int indent,
const struct conf_fprintf *conf, FILE *fp)
{
struct parameter *pos;
int first_parm = 1;
char sbf[128];
struct tag *type;
const char *name, *stype;
size_t printed = fprintf(fp, "(");
ftype__for_each_parameter(ftype, pos) {
if (!first_parm) {
if (indent == 0)
printed += fprintf(fp, ", ");
else
printed += fprintf(fp, ",\n%.*s",
indent, tabs);
} else
first_parm = 0;
name = conf->no_parm_names ? NULL : parameter__name(pos, cu);
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;
}
if (type->tag == DW_TAG_pointer_type) {
if (type->type != 0) {
int n;
struct tag *ptype = cu__type(cu, type->type);
if (ptype == NULL) {
printed +=
tag__id_not_found_fprintf(fp, type->type);
continue;
}
n = tag__has_type_loop(type, ptype, NULL, 0, fp);
if (n)
return printed + n;
if (ptype->tag == DW_TAG_subroutine_type) {
printed +=
ftype__fprintf(tag__ftype(ptype),
cu, name, 0, 1, 0,
conf, fp);
continue;
}
}
} else if (type->tag == DW_TAG_subroutine_type) {
printed += ftype__fprintf(tag__ftype(type), cu, name,
0, 0, 0, conf, fp);
continue;
}
stype = tag__name(type, cu, sbf, sizeof(sbf), conf);
print_it:
printed += fprintf(fp, "%s%s%s", stype, name ? " " : "",
name ?: "");
}
/* No parameters? */
if (first_parm)
printed += fprintf(fp, "void)");
else if (ftype->unspec_parms)
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;
const struct tag *talias = cu__function(cu, exp->ip.tag.type);
struct function *alias = tag__function(talias);
const char *name;
if (alias == NULL) {
printed += tag__id_not_found_fprintf(fp, exp->ip.tag.type);
break;
}
printed = fprintf(fp, "%.*s", indent, tabs);
name = function__name(alias, cu);
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 */",
exp->size, (unsigned long long)exp->ip.addr);
#if 0
n = fprintf(fp, "%s(); /* size=%zd, low_pc=%#llx */",
function__name(alias, cu), exp->size,
(unsigned long long)exp->ip.addr);
#endif
c = 69;
printed += n;
}
break;
case DW_TAG_variable:
printed = fprintf(fp, "%.*s", indent, tabs);
n = fprintf(fp, "%s %s; /* scope: %s */",
variable__type_name(vtag, cu, bf, sizeof(bf)),
variable__name(vtag, cu),
variable__scope_str(vtag));
c += n;
printed += n;
break;
case DW_TAG_label: {
const struct label *label = vtag;
printed = fprintf(fp, "%.*s", indent, tabs);
fputc('\n', fp);
++printed;
c = fprintf(fp, "%s:", label__name(label, cu));
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));
}
size_t lexblock__fprintf(const struct lexblock *block, const struct cu *cu,
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);
if (block->ip.addr != 0) {
uint64_t offset = block->ip.addr - function->lexblock.ip.addr;
if (offset == 0)
printed += fprintf(fp, " /* low_pc=%#llx */",
(unsigned long long)block->ip.addr);
else
printed += fprintf(fp, " /* %s+%#llx */",
function__name(function, cu),
(unsigned long long)offset);
}
printed += fprintf(fp, "\n");
list_for_each_entry(pos, &block->tags, node)
printed += function__tag_fprintf(pos, cu, function, indent + 1,
conf, fp);
printed += fprintf(fp, "%.*s}", indent, tabs);
if (function->lexblock.ip.addr != block->ip.addr)
printed += fprintf(fp, " /* lexblock size=%d */", block->size);
return printed;
}
size_t ftype__fprintf(const struct ftype *ftype, const struct cu *cu,
const char *name, const int inlined,
const int is_pointer, int type_spacing,
const struct conf_fprintf *conf, FILE *fp)
{
struct tag *type = cu__type(cu, ftype->tag.type);
char sbf[128];
const char *stype = tag__name(type, cu, sbf, sizeof(sbf), conf);
size_t printed = fprintf(fp, "%s%-*s %s%s%s%s",
inlined ? "inline " : "",
type_spacing, stype,
ftype->tag.tag == DW_TAG_subroutine_type ?
"(" : "",
is_pointer ? "*" : "", name ?: "",
ftype->tag.tag == DW_TAG_subroutine_type ?
")" : "");
return printed + ftype__fprintf_parms(ftype, cu, 0, conf, fp);
}
static size_t function__fprintf(const struct tag *tag, const struct cu *cu,
const struct conf_fprintf *conf, FILE *fp)
{
struct function *func = tag__function(tag);
size_t printed = 0;
if (func->virtuality == DW_VIRTUALITY_virtual ||
func->virtuality == DW_VIRTUALITY_pure_virtual)
printed += fprintf(fp, "virtual ");
printed += ftype__fprintf(&func->proto, cu, function__name(func, cu),
function__declared_inline(func), 0, 0,
conf, fp);
if (func->virtuality == DW_VIRTUALITY_pure_virtual)
printed += fprintf(fp, " = 0");
return printed;
}
size_t function__fprintf_stats(const struct tag *tag, const struct cu *cu,
const struct conf_fprintf *conf, FILE *fp)
{
struct function *func = tag__function(tag);
size_t printed = lexblock__fprintf(&func->lexblock, cu, func, 0, conf, fp);
printed += fprintf(fp, "/* size: %d", function__size(func));
if (func->lexblock.nr_variables > 0)
printed += fprintf(fp, ", variables: %u",
func->lexblock.nr_variables);
if (func->lexblock.nr_labels > 0)
printed += fprintf(fp, ", goto labels: %u",
func->lexblock.nr_labels);
if (func->lexblock.nr_inline_expansions > 0)
printed += fprintf(fp, ", inline expansions: %u (%d bytes)",
func->lexblock.nr_inline_expansions,
func->lexblock.size_inline_expansions);
return printed + fprintf(fp, " */\n");
}
dwarves_fprintf: Fixup cacheline boundary printing on expanded structs A diff for 'pahole -EC task_struct vmlinux' should clarify what this fixes: [acme@jouet linux]$ diff -u /tmp/before.c /tmp/after.c | head -30 --- /tmp/before.c 2016-06-29 17:00:38.082647281 -0300 +++ /tmp/a.c 2016-06-29 17:03:36.913124779 -0300 @@ -43,8 +43,8 @@ struct list_head * prev; /* 176 8 */ } group_node; /* 168 16 */ unsigned int on_rq; /* 184 4 */ + /* --- cacheline 3 boundary (192 bytes) --- */ /* typedef u64 */ long long unsigned int exec_start; /* 192 8 */ - /* --- cacheline 1 boundary (64 bytes) was 4 bytes ago --- */ /* typedef u64 */ long long unsigned int sum_exec_runtime; /* 200 8 */ /* typedef u64 */ long long unsigned int vruntime; /* 208 8 */ /* typedef u64 */ long long unsigned int prev_sum_exec_runtime; /* 216 8 */ @@ -53,40 +53,40 @@ /* typedef u64 */ long long unsigned int wait_start; /* 232 8 */ /* typedef u64 */ long long unsigned int wait_max; /* 240 8 */ /* typedef u64 */ long long unsigned int wait_count; /* 248 8 */ + /* --- cacheline 4 boundary (256 bytes) --- */ /* typedef u64 */ long long unsigned int wait_sum; /* 256 8 */ /* typedef u64 */ long long unsigned int iowait_count; /* 264 8 */ /* typedef u64 */ long long unsigned int iowait_sum; /* 272 8 */ /* typedef u64 */ long long unsigned int sleep_start; /* 280 8 */ /* typedef u64 */ long long unsigned int sleep_max; /* 288 8 */ - /* --- cacheline 1 boundary (64 bytes) --- */ /* typedef s64 */ long long int sum_sleep_runtime; /* 296 8 */ /* typedef u64 */ long long unsigned int block_start; /* 304 8 */ /* typedef u64 */ long long unsigned int block_max; /* 312 8 */ + /* --- cacheline 5 boundary (320 bytes) --- */ /* typedef u64 */ long long unsigned int exec_max; /* 320 8 */ /* typedef u64 */ long long unsigned int slice_max; /* 328 8 */ /* typedef u64 */ long long unsigned int nr_migrations_cold; /* 336 8 */ [acme@jouet linux]$ I.e. the boundary detection was being reset at each expanded struct, do the math globally, using the member offset, that was already done globally and correctly. Reported-and-Tested-by: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-06-29 22:27:51 +02:00
static size_t class__fprintf_cacheline_boundary(struct conf_fprintf *conf,
uint32_t offset,
FILE *fp)
{
dwarves_fprintf: Fixup cacheline boundary printing on expanded structs A diff for 'pahole -EC task_struct vmlinux' should clarify what this fixes: [acme@jouet linux]$ diff -u /tmp/before.c /tmp/after.c | head -30 --- /tmp/before.c 2016-06-29 17:00:38.082647281 -0300 +++ /tmp/a.c 2016-06-29 17:03:36.913124779 -0300 @@ -43,8 +43,8 @@ struct list_head * prev; /* 176 8 */ } group_node; /* 168 16 */ unsigned int on_rq; /* 184 4 */ + /* --- cacheline 3 boundary (192 bytes) --- */ /* typedef u64 */ long long unsigned int exec_start; /* 192 8 */ - /* --- cacheline 1 boundary (64 bytes) was 4 bytes ago --- */ /* typedef u64 */ long long unsigned int sum_exec_runtime; /* 200 8 */ /* typedef u64 */ long long unsigned int vruntime; /* 208 8 */ /* typedef u64 */ long long unsigned int prev_sum_exec_runtime; /* 216 8 */ @@ -53,40 +53,40 @@ /* typedef u64 */ long long unsigned int wait_start; /* 232 8 */ /* typedef u64 */ long long unsigned int wait_max; /* 240 8 */ /* typedef u64 */ long long unsigned int wait_count; /* 248 8 */ + /* --- cacheline 4 boundary (256 bytes) --- */ /* typedef u64 */ long long unsigned int wait_sum; /* 256 8 */ /* typedef u64 */ long long unsigned int iowait_count; /* 264 8 */ /* typedef u64 */ long long unsigned int iowait_sum; /* 272 8 */ /* typedef u64 */ long long unsigned int sleep_start; /* 280 8 */ /* typedef u64 */ long long unsigned int sleep_max; /* 288 8 */ - /* --- cacheline 1 boundary (64 bytes) --- */ /* typedef s64 */ long long int sum_sleep_runtime; /* 296 8 */ /* typedef u64 */ long long unsigned int block_start; /* 304 8 */ /* typedef u64 */ long long unsigned int block_max; /* 312 8 */ + /* --- cacheline 5 boundary (320 bytes) --- */ /* typedef u64 */ long long unsigned int exec_max; /* 320 8 */ /* typedef u64 */ long long unsigned int slice_max; /* 328 8 */ /* typedef u64 */ long long unsigned int nr_migrations_cold; /* 336 8 */ [acme@jouet linux]$ I.e. the boundary detection was being reset at each expanded struct, do the math globally, using the member offset, that was already done globally and correctly. Reported-and-Tested-by: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-06-29 22:27:51 +02:00
int indent = conf->indent;
uint32_t cacheline = offset / cacheline_size;
size_t printed = 0;
dwarves_fprintf: Fixup cacheline boundary printing on expanded structs A diff for 'pahole -EC task_struct vmlinux' should clarify what this fixes: [acme@jouet linux]$ diff -u /tmp/before.c /tmp/after.c | head -30 --- /tmp/before.c 2016-06-29 17:00:38.082647281 -0300 +++ /tmp/a.c 2016-06-29 17:03:36.913124779 -0300 @@ -43,8 +43,8 @@ struct list_head * prev; /* 176 8 */ } group_node; /* 168 16 */ unsigned int on_rq; /* 184 4 */ + /* --- cacheline 3 boundary (192 bytes) --- */ /* typedef u64 */ long long unsigned int exec_start; /* 192 8 */ - /* --- cacheline 1 boundary (64 bytes) was 4 bytes ago --- */ /* typedef u64 */ long long unsigned int sum_exec_runtime; /* 200 8 */ /* typedef u64 */ long long unsigned int vruntime; /* 208 8 */ /* typedef u64 */ long long unsigned int prev_sum_exec_runtime; /* 216 8 */ @@ -53,40 +53,40 @@ /* typedef u64 */ long long unsigned int wait_start; /* 232 8 */ /* typedef u64 */ long long unsigned int wait_max; /* 240 8 */ /* typedef u64 */ long long unsigned int wait_count; /* 248 8 */ + /* --- cacheline 4 boundary (256 bytes) --- */ /* typedef u64 */ long long unsigned int wait_sum; /* 256 8 */ /* typedef u64 */ long long unsigned int iowait_count; /* 264 8 */ /* typedef u64 */ long long unsigned int iowait_sum; /* 272 8 */ /* typedef u64 */ long long unsigned int sleep_start; /* 280 8 */ /* typedef u64 */ long long unsigned int sleep_max; /* 288 8 */ - /* --- cacheline 1 boundary (64 bytes) --- */ /* typedef s64 */ long long int sum_sleep_runtime; /* 296 8 */ /* typedef u64 */ long long unsigned int block_start; /* 304 8 */ /* typedef u64 */ long long unsigned int block_max; /* 312 8 */ + /* --- cacheline 5 boundary (320 bytes) --- */ /* typedef u64 */ long long unsigned int exec_max; /* 320 8 */ /* typedef u64 */ long long unsigned int slice_max; /* 328 8 */ /* typedef u64 */ long long unsigned int nr_migrations_cold; /* 336 8 */ [acme@jouet linux]$ I.e. the boundary detection was being reset at each expanded struct, do the math globally, using the member offset, that was already done globally and correctly. Reported-and-Tested-by: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-06-29 22:27:51 +02:00
if (cacheline > *conf->cachelinep) {
const uint32_t cacheline_pos = offset % cacheline_size;
const uint32_t cacheline_in_bytes = offset - cacheline_pos;
if (cacheline_pos == 0)
printed += fprintf(fp, "/* --- cacheline %u boundary "
dwarves_fprintf: Fixup cacheline boundary printing on expanded structs A diff for 'pahole -EC task_struct vmlinux' should clarify what this fixes: [acme@jouet linux]$ diff -u /tmp/before.c /tmp/after.c | head -30 --- /tmp/before.c 2016-06-29 17:00:38.082647281 -0300 +++ /tmp/a.c 2016-06-29 17:03:36.913124779 -0300 @@ -43,8 +43,8 @@ struct list_head * prev; /* 176 8 */ } group_node; /* 168 16 */ unsigned int on_rq; /* 184 4 */ + /* --- cacheline 3 boundary (192 bytes) --- */ /* typedef u64 */ long long unsigned int exec_start; /* 192 8 */ - /* --- cacheline 1 boundary (64 bytes) was 4 bytes ago --- */ /* typedef u64 */ long long unsigned int sum_exec_runtime; /* 200 8 */ /* typedef u64 */ long long unsigned int vruntime; /* 208 8 */ /* typedef u64 */ long long unsigned int prev_sum_exec_runtime; /* 216 8 */ @@ -53,40 +53,40 @@ /* typedef u64 */ long long unsigned int wait_start; /* 232 8 */ /* typedef u64 */ long long unsigned int wait_max; /* 240 8 */ /* typedef u64 */ long long unsigned int wait_count; /* 248 8 */ + /* --- cacheline 4 boundary (256 bytes) --- */ /* typedef u64 */ long long unsigned int wait_sum; /* 256 8 */ /* typedef u64 */ long long unsigned int iowait_count; /* 264 8 */ /* typedef u64 */ long long unsigned int iowait_sum; /* 272 8 */ /* typedef u64 */ long long unsigned int sleep_start; /* 280 8 */ /* typedef u64 */ long long unsigned int sleep_max; /* 288 8 */ - /* --- cacheline 1 boundary (64 bytes) --- */ /* typedef s64 */ long long int sum_sleep_runtime; /* 296 8 */ /* typedef u64 */ long long unsigned int block_start; /* 304 8 */ /* typedef u64 */ long long unsigned int block_max; /* 312 8 */ + /* --- cacheline 5 boundary (320 bytes) --- */ /* typedef u64 */ long long unsigned int exec_max; /* 320 8 */ /* typedef u64 */ long long unsigned int slice_max; /* 328 8 */ /* typedef u64 */ long long unsigned int nr_migrations_cold; /* 336 8 */ [acme@jouet linux]$ I.e. the boundary detection was being reset at each expanded struct, do the math globally, using the member offset, that was already done globally and correctly. Reported-and-Tested-by: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-06-29 22:27:51 +02:00
"(%u bytes) --- */\n", cacheline,
cacheline_in_bytes);
else
printed += fprintf(fp, "/* --- cacheline %u boundary "
"(%u bytes) was %u bytes ago --- "
dwarves_fprintf: Fixup cacheline boundary printing on expanded structs A diff for 'pahole -EC task_struct vmlinux' should clarify what this fixes: [acme@jouet linux]$ diff -u /tmp/before.c /tmp/after.c | head -30 --- /tmp/before.c 2016-06-29 17:00:38.082647281 -0300 +++ /tmp/a.c 2016-06-29 17:03:36.913124779 -0300 @@ -43,8 +43,8 @@ struct list_head * prev; /* 176 8 */ } group_node; /* 168 16 */ unsigned int on_rq; /* 184 4 */ + /* --- cacheline 3 boundary (192 bytes) --- */ /* typedef u64 */ long long unsigned int exec_start; /* 192 8 */ - /* --- cacheline 1 boundary (64 bytes) was 4 bytes ago --- */ /* typedef u64 */ long long unsigned int sum_exec_runtime; /* 200 8 */ /* typedef u64 */ long long unsigned int vruntime; /* 208 8 */ /* typedef u64 */ long long unsigned int prev_sum_exec_runtime; /* 216 8 */ @@ -53,40 +53,40 @@ /* typedef u64 */ long long unsigned int wait_start; /* 232 8 */ /* typedef u64 */ long long unsigned int wait_max; /* 240 8 */ /* typedef u64 */ long long unsigned int wait_count; /* 248 8 */ + /* --- cacheline 4 boundary (256 bytes) --- */ /* typedef u64 */ long long unsigned int wait_sum; /* 256 8 */ /* typedef u64 */ long long unsigned int iowait_count; /* 264 8 */ /* typedef u64 */ long long unsigned int iowait_sum; /* 272 8 */ /* typedef u64 */ long long unsigned int sleep_start; /* 280 8 */ /* typedef u64 */ long long unsigned int sleep_max; /* 288 8 */ - /* --- cacheline 1 boundary (64 bytes) --- */ /* typedef s64 */ long long int sum_sleep_runtime; /* 296 8 */ /* typedef u64 */ long long unsigned int block_start; /* 304 8 */ /* typedef u64 */ long long unsigned int block_max; /* 312 8 */ + /* --- cacheline 5 boundary (320 bytes) --- */ /* typedef u64 */ long long unsigned int exec_max; /* 320 8 */ /* typedef u64 */ long long unsigned int slice_max; /* 328 8 */ /* typedef u64 */ long long unsigned int nr_migrations_cold; /* 336 8 */ [acme@jouet linux]$ I.e. the boundary detection was being reset at each expanded struct, do the math globally, using the member offset, that was already done globally and correctly. Reported-and-Tested-by: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-06-29 22:27:51 +02:00
"*/\n", cacheline,
cacheline_in_bytes, cacheline_pos);
dwarves_fprintf: Fixup cacheline boundary printing on expanded structs A diff for 'pahole -EC task_struct vmlinux' should clarify what this fixes: [acme@jouet linux]$ diff -u /tmp/before.c /tmp/after.c | head -30 --- /tmp/before.c 2016-06-29 17:00:38.082647281 -0300 +++ /tmp/a.c 2016-06-29 17:03:36.913124779 -0300 @@ -43,8 +43,8 @@ struct list_head * prev; /* 176 8 */ } group_node; /* 168 16 */ unsigned int on_rq; /* 184 4 */ + /* --- cacheline 3 boundary (192 bytes) --- */ /* typedef u64 */ long long unsigned int exec_start; /* 192 8 */ - /* --- cacheline 1 boundary (64 bytes) was 4 bytes ago --- */ /* typedef u64 */ long long unsigned int sum_exec_runtime; /* 200 8 */ /* typedef u64 */ long long unsigned int vruntime; /* 208 8 */ /* typedef u64 */ long long unsigned int prev_sum_exec_runtime; /* 216 8 */ @@ -53,40 +53,40 @@ /* typedef u64 */ long long unsigned int wait_start; /* 232 8 */ /* typedef u64 */ long long unsigned int wait_max; /* 240 8 */ /* typedef u64 */ long long unsigned int wait_count; /* 248 8 */ + /* --- cacheline 4 boundary (256 bytes) --- */ /* typedef u64 */ long long unsigned int wait_sum; /* 256 8 */ /* typedef u64 */ long long unsigned int iowait_count; /* 264 8 */ /* typedef u64 */ long long unsigned int iowait_sum; /* 272 8 */ /* typedef u64 */ long long unsigned int sleep_start; /* 280 8 */ /* typedef u64 */ long long unsigned int sleep_max; /* 288 8 */ - /* --- cacheline 1 boundary (64 bytes) --- */ /* typedef s64 */ long long int sum_sleep_runtime; /* 296 8 */ /* typedef u64 */ long long unsigned int block_start; /* 304 8 */ /* typedef u64 */ long long unsigned int block_max; /* 312 8 */ + /* --- cacheline 5 boundary (320 bytes) --- */ /* typedef u64 */ long long unsigned int exec_max; /* 320 8 */ /* typedef u64 */ long long unsigned int slice_max; /* 328 8 */ /* typedef u64 */ long long unsigned int nr_migrations_cold; /* 336 8 */ [acme@jouet linux]$ I.e. the boundary detection was being reset at each expanded struct, do the math globally, using the member offset, that was already done globally and correctly. Reported-and-Tested-by: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-06-29 22:27:51 +02:00
printed += fprintf(fp, "%.*s", indent, tabs);
*conf->cachelinep = cacheline;
}
return printed;
}
static size_t class__vtable_fprintf(struct class *class, const struct cu *cu,
const struct conf_fprintf *conf, FILE *fp)
{
struct function *pos;
size_t printed = 0;
if (class->nr_vtable_entries == 0)
goto out;
printed += fprintf(fp, "%.*s/* vtable has %u entries: {\n",
conf->indent, tabs, class->nr_vtable_entries);
list_for_each_entry(pos, &class->vtable, vtable_node) {
printed += fprintf(fp, "%.*s [%d] = %s(%s), \n",
conf->indent, tabs, pos->vtable_entry,
function__name(pos, cu),
function__linkage_name(pos, cu));
}
printed += fprintf(fp, "%.*s} */", conf->indent, tabs);
out:
return printed;
}
static size_t __class__fprintf(struct class *class, const struct cu *cu,
const struct conf_fprintf *conf, FILE *fp)
{
struct type *type = &class->type;
size_t last_size = 0, size;
uint8_t newline = 0;
uint16_t nr_paddings = 0;
uint32_t sum = 0;
uint32_t sum_holes = 0;
uint32_t sum_paddings = 0;
uint32_t sum_bit_holes = 0;
dwarves_fprintf: Fixup cacheline boundary printing on expanded structs A diff for 'pahole -EC task_struct vmlinux' should clarify what this fixes: [acme@jouet linux]$ diff -u /tmp/before.c /tmp/after.c | head -30 --- /tmp/before.c 2016-06-29 17:00:38.082647281 -0300 +++ /tmp/a.c 2016-06-29 17:03:36.913124779 -0300 @@ -43,8 +43,8 @@ struct list_head * prev; /* 176 8 */ } group_node; /* 168 16 */ unsigned int on_rq; /* 184 4 */ + /* --- cacheline 3 boundary (192 bytes) --- */ /* typedef u64 */ long long unsigned int exec_start; /* 192 8 */ - /* --- cacheline 1 boundary (64 bytes) was 4 bytes ago --- */ /* typedef u64 */ long long unsigned int sum_exec_runtime; /* 200 8 */ /* typedef u64 */ long long unsigned int vruntime; /* 208 8 */ /* typedef u64 */ long long unsigned int prev_sum_exec_runtime; /* 216 8 */ @@ -53,40 +53,40 @@ /* typedef u64 */ long long unsigned int wait_start; /* 232 8 */ /* typedef u64 */ long long unsigned int wait_max; /* 240 8 */ /* typedef u64 */ long long unsigned int wait_count; /* 248 8 */ + /* --- cacheline 4 boundary (256 bytes) --- */ /* typedef u64 */ long long unsigned int wait_sum; /* 256 8 */ /* typedef u64 */ long long unsigned int iowait_count; /* 264 8 */ /* typedef u64 */ long long unsigned int iowait_sum; /* 272 8 */ /* typedef u64 */ long long unsigned int sleep_start; /* 280 8 */ /* typedef u64 */ long long unsigned int sleep_max; /* 288 8 */ - /* --- cacheline 1 boundary (64 bytes) --- */ /* typedef s64 */ long long int sum_sleep_runtime; /* 296 8 */ /* typedef u64 */ long long unsigned int block_start; /* 304 8 */ /* typedef u64 */ long long unsigned int block_max; /* 312 8 */ + /* --- cacheline 5 boundary (320 bytes) --- */ /* typedef u64 */ long long unsigned int exec_max; /* 320 8 */ /* typedef u64 */ long long unsigned int slice_max; /* 328 8 */ /* typedef u64 */ long long unsigned int nr_migrations_cold; /* 336 8 */ [acme@jouet linux]$ I.e. the boundary detection was being reset at each expanded struct, do the math globally, using the member offset, that was already done globally and correctly. Reported-and-Tested-by: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-06-29 22:27:51 +02:00
uint32_t cacheline = 0;
uint32_t bitfield_real_offset = 0;
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;
const uint16_t t = type->namespace.tag.tag;
size_t printed = fprintf(fp, "%s%s%s%s%s",
cconf.prefix ?: "", cconf.prefix ? " " : "",
((cconf.classes_as_structs ||
t == DW_TAG_structure_type) ? "struct" :
t == DW_TAG_class_type ? "class" :
"interface"),
type__name(type, cu) ? " " : "",
type__name(type, cu) ?: "");
int indent = cconf.indent;
if (indent >= (int)sizeof(tabs))
indent = sizeof(tabs) - 1;
dwarves_fprintf: Fixup cacheline boundary printing on expanded structs A diff for 'pahole -EC task_struct vmlinux' should clarify what this fixes: [acme@jouet linux]$ diff -u /tmp/before.c /tmp/after.c | head -30 --- /tmp/before.c 2016-06-29 17:00:38.082647281 -0300 +++ /tmp/a.c 2016-06-29 17:03:36.913124779 -0300 @@ -43,8 +43,8 @@ struct list_head * prev; /* 176 8 */ } group_node; /* 168 16 */ unsigned int on_rq; /* 184 4 */ + /* --- cacheline 3 boundary (192 bytes) --- */ /* typedef u64 */ long long unsigned int exec_start; /* 192 8 */ - /* --- cacheline 1 boundary (64 bytes) was 4 bytes ago --- */ /* typedef u64 */ long long unsigned int sum_exec_runtime; /* 200 8 */ /* typedef u64 */ long long unsigned int vruntime; /* 208 8 */ /* typedef u64 */ long long unsigned int prev_sum_exec_runtime; /* 216 8 */ @@ -53,40 +53,40 @@ /* typedef u64 */ long long unsigned int wait_start; /* 232 8 */ /* typedef u64 */ long long unsigned int wait_max; /* 240 8 */ /* typedef u64 */ long long unsigned int wait_count; /* 248 8 */ + /* --- cacheline 4 boundary (256 bytes) --- */ /* typedef u64 */ long long unsigned int wait_sum; /* 256 8 */ /* typedef u64 */ long long unsigned int iowait_count; /* 264 8 */ /* typedef u64 */ long long unsigned int iowait_sum; /* 272 8 */ /* typedef u64 */ long long unsigned int sleep_start; /* 280 8 */ /* typedef u64 */ long long unsigned int sleep_max; /* 288 8 */ - /* --- cacheline 1 boundary (64 bytes) --- */ /* typedef s64 */ long long int sum_sleep_runtime; /* 296 8 */ /* typedef u64 */ long long unsigned int block_start; /* 304 8 */ /* typedef u64 */ long long unsigned int block_max; /* 312 8 */ + /* --- cacheline 5 boundary (320 bytes) --- */ /* typedef u64 */ long long unsigned int exec_max; /* 320 8 */ /* typedef u64 */ long long unsigned int slice_max; /* 328 8 */ /* typedef u64 */ long long unsigned int nr_migrations_cold; /* 336 8 */ [acme@jouet linux]$ I.e. the boundary detection was being reset at each expanded struct, do the math globally, using the member offset, that was already done globally and correctly. Reported-and-Tested-by: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-06-29 22:27:51 +02:00
if (cconf.cachelinep == NULL)
cconf.cachelinep = &cacheline;
cconf.indent = indent + 1;
cconf.no_semicolon = 0;
/* First look if we have DW_TAG_inheritance */
type__for_each_tag(type, tag_pos) {
struct tag *type;
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);
type = cu__type(cu, tag_pos->type);
if (type != NULL)
printed += fprintf(fp, " %s",
type__name(tag__type(type), cu));
else
printed += tag__id_not_found_fprintf(fp, tag_pos->type);
}
printed += fprintf(fp, " {\n");
type__for_each_tag(type, tag_pos) {
struct tag *type;
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);
/*
* These paranoid checks doesn't make much sense on
* DW_TAG_inheritance, have to understand why virtual public
* ancestors make the offset go backwards...
*/
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) {
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);
}
if (pos->byte_offset != last->byte_offset)
bitfield_real_offset = last->byte_offset + last_size;
} 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);
}
sum -= last_size;
sum += cc_last_size;
}
}
}
if (newline) {
fputc('\n', fp);
newline = 0;
++printed;
}
type = cu__type(cu, pos->tag.type);
if (type == NULL) {
printed += fprintf(fp, "%.*s", cconf.indent, tabs);
printed += tag__id_not_found_fprintf(fp, pos->tag.type);
continue;
}
size = pos->byte_size;
printed += fprintf(fp, "%.*s", cconf.indent, tabs);
printed += struct_member__fprintf(pos, type, cu, &cconf, fp);
if (tag__is_struct(type) && !cconf.suppress_comments) {
struct class *tclass = tag__class(type);
uint16_t padding;
/*
* We may not yet have looked for holes and paddings
* in this member's struct type.
*/
class__find_holes(tclass);
padding = tclass->padding;
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" : "");
}
}
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;
}
fputc('\n', fp);
++printed;
/* XXX for now just skip these */
if (tag_pos->tag == DW_TAG_inheritance)
continue;
#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
/*
* Check if we have to adjust size because bitfields were
* combined with previous fields.
*/
if (bitfield_real_offset != 0 && last->bitfield_end) {
size_t real_last_size = pos->byte_offset - bitfield_real_offset;
sum -= last_size;
sum += real_last_size;
bitfield_real_offset = 0;
}
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) {
sum += size;
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 /
* }
*/
sum += size - last_size;
last_size = size;
}
last = pos;
}
/*
* Check if we have to adjust size because bitfields were
* combined with previous fields and were the last fields
* in the struct.
*/
if (bitfield_real_offset != 0) {
size_t real_last_size = type->size - bitfield_real_offset;
sum -= last_size;
sum += real_last_size;
bitfield_real_offset = 0;
}
if (!cconf.show_only_data_members)
class__vtable_fprintf(class, cu, &cconf, fp);
if (!cconf.emit_stats)
goto out;
printed += type__fprintf_stats(type, cu, &cconf, fp);
if (sum_holes > 0)
printed += fprintf(fp, "%.*s/* sum members: %u, holes: %d, "
"sum holes: %u */\n",
cconf.indent, tabs,
sum, class->nr_holes, sum_holes);
if (sum_bit_holes > 0)
printed += fprintf(fp, "%.*s/* bit holes: %d, sum bit "
"holes: %u bits */\n",
cconf.indent, tabs,
class->nr_bit_holes, sum_bit_holes);
if (class->padding > 0)
printed += fprintf(fp, "%.*s/* padding: %u */\n",
cconf.indent,
tabs, class->padding);
if (nr_paddings > 0)
printed += fprintf(fp, "%.*s/* paddings: %u, sum paddings: "
"%u */\n",
cconf.indent, tabs,
nr_paddings, sum_paddings);
if (class->bit_padding > 0)
printed += fprintf(fp, "%.*s/* bit_padding: %u bits */\n",
cconf.indent, tabs,
class->bit_padding);
dwarves_fprintf: Fixup cacheline boundary printing on expanded structs A diff for 'pahole -EC task_struct vmlinux' should clarify what this fixes: [acme@jouet linux]$ diff -u /tmp/before.c /tmp/after.c | head -30 --- /tmp/before.c 2016-06-29 17:00:38.082647281 -0300 +++ /tmp/a.c 2016-06-29 17:03:36.913124779 -0300 @@ -43,8 +43,8 @@ struct list_head * prev; /* 176 8 */ } group_node; /* 168 16 */ unsigned int on_rq; /* 184 4 */ + /* --- cacheline 3 boundary (192 bytes) --- */ /* typedef u64 */ long long unsigned int exec_start; /* 192 8 */ - /* --- cacheline 1 boundary (64 bytes) was 4 bytes ago --- */ /* typedef u64 */ long long unsigned int sum_exec_runtime; /* 200 8 */ /* typedef u64 */ long long unsigned int vruntime; /* 208 8 */ /* typedef u64 */ long long unsigned int prev_sum_exec_runtime; /* 216 8 */ @@ -53,40 +53,40 @@ /* typedef u64 */ long long unsigned int wait_start; /* 232 8 */ /* typedef u64 */ long long unsigned int wait_max; /* 240 8 */ /* typedef u64 */ long long unsigned int wait_count; /* 248 8 */ + /* --- cacheline 4 boundary (256 bytes) --- */ /* typedef u64 */ long long unsigned int wait_sum; /* 256 8 */ /* typedef u64 */ long long unsigned int iowait_count; /* 264 8 */ /* typedef u64 */ long long unsigned int iowait_sum; /* 272 8 */ /* typedef u64 */ long long unsigned int sleep_start; /* 280 8 */ /* typedef u64 */ long long unsigned int sleep_max; /* 288 8 */ - /* --- cacheline 1 boundary (64 bytes) --- */ /* typedef s64 */ long long int sum_sleep_runtime; /* 296 8 */ /* typedef u64 */ long long unsigned int block_start; /* 304 8 */ /* typedef u64 */ long long unsigned int block_max; /* 312 8 */ + /* --- cacheline 5 boundary (320 bytes) --- */ /* typedef u64 */ long long unsigned int exec_max; /* 320 8 */ /* typedef u64 */ long long unsigned int slice_max; /* 328 8 */ /* typedef u64 */ long long unsigned int nr_migrations_cold; /* 336 8 */ [acme@jouet linux]$ I.e. the boundary detection was being reset at each expanded struct, do the math globally, using the member offset, that was already done globally and correctly. Reported-and-Tested-by: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-06-29 22:27:51 +02:00
cacheline = (cconf.base_offset + type->size) % cacheline_size;
if (cacheline != 0)
printed += fprintf(fp, "%.*s/* last cacheline: %u bytes */\n",
cconf.indent, tabs,
dwarves_fprintf: Fixup cacheline boundary printing on expanded structs A diff for 'pahole -EC task_struct vmlinux' should clarify what this fixes: [acme@jouet linux]$ diff -u /tmp/before.c /tmp/after.c | head -30 --- /tmp/before.c 2016-06-29 17:00:38.082647281 -0300 +++ /tmp/a.c 2016-06-29 17:03:36.913124779 -0300 @@ -43,8 +43,8 @@ struct list_head * prev; /* 176 8 */ } group_node; /* 168 16 */ unsigned int on_rq; /* 184 4 */ + /* --- cacheline 3 boundary (192 bytes) --- */ /* typedef u64 */ long long unsigned int exec_start; /* 192 8 */ - /* --- cacheline 1 boundary (64 bytes) was 4 bytes ago --- */ /* typedef u64 */ long long unsigned int sum_exec_runtime; /* 200 8 */ /* typedef u64 */ long long unsigned int vruntime; /* 208 8 */ /* typedef u64 */ long long unsigned int prev_sum_exec_runtime; /* 216 8 */ @@ -53,40 +53,40 @@ /* typedef u64 */ long long unsigned int wait_start; /* 232 8 */ /* typedef u64 */ long long unsigned int wait_max; /* 240 8 */ /* typedef u64 */ long long unsigned int wait_count; /* 248 8 */ + /* --- cacheline 4 boundary (256 bytes) --- */ /* typedef u64 */ long long unsigned int wait_sum; /* 256 8 */ /* typedef u64 */ long long unsigned int iowait_count; /* 264 8 */ /* typedef u64 */ long long unsigned int iowait_sum; /* 272 8 */ /* typedef u64 */ long long unsigned int sleep_start; /* 280 8 */ /* typedef u64 */ long long unsigned int sleep_max; /* 288 8 */ - /* --- cacheline 1 boundary (64 bytes) --- */ /* typedef s64 */ long long int sum_sleep_runtime; /* 296 8 */ /* typedef u64 */ long long unsigned int block_start; /* 304 8 */ /* typedef u64 */ long long unsigned int block_max; /* 312 8 */ + /* --- cacheline 5 boundary (320 bytes) --- */ /* typedef u64 */ long long unsigned int exec_max; /* 320 8 */ /* typedef u64 */ long long unsigned int slice_max; /* 328 8 */ /* typedef u64 */ long long unsigned int nr_migrations_cold; /* 336 8 */ [acme@jouet linux]$ I.e. the boundary detection was being reset at each expanded struct, do the math globally, using the member offset, that was already done globally and correctly. Reported-and-Tested-by: Peter Zijlstra <peterz@infradead.org> Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-06-29 22:27:51 +02:00
cacheline);
if (cconf.show_first_biggest_size_base_type_member &&
type->nr_members != 0) {
struct class_member *m = type__find_first_biggest_size_base_type_member(type, cu);
printed += fprintf(fp, "%.*s/* first biggest size base type member: %s %u %zd */\n",
cconf.indent, tabs,
class_member__name(m, cu), m->byte_offset,
m->byte_size);
}
if (sum + sum_holes != type->size - class->padding &&
type->nr_members != 0)
printed += fprintf(fp, "\n%.*s/* BRAIN FART ALERT! %d != %u "
"+ %u(holes), diff = %d */\n",
cconf.indent, tabs,
type->size, sum, sum_holes,
type->size - (sum + sum_holes));
out:
return printed + fprintf(fp, "%.*s}%s%s", indent, tabs,
cconf.suffix ? " ": "", cconf.suffix ?: "");
}
size_t class__fprintf(struct class *class, const struct cu *cu, FILE *fp)
{
return __class__fprintf(class, cu, NULL, fp);
}
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);
const char *name = variable__name(var, cu);
size_t printed = 0;
if (name != NULL) {
struct tag *type = cu__type(cu, var->ip.tag.type);
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;
}
static size_t namespace__fprintf(const struct tag *tag, const struct cu *cu,
const struct conf_fprintf *conf, FILE *fp)
{
struct namespace *space = tag__namespace(tag);
struct conf_fprintf cconf = *conf;
size_t printed = fprintf(fp, "namespace %s {\n",
namespace__name(space, cu));
struct tag *pos;
++cconf.indent;
cconf.no_semicolon = 0;
namespace__for_each_tag(space, pos) {
printed += tag__fprintf(pos, cu, &cconf, fp);
printed += fprintf(fp, "\n\n");
}
return printed + fprintf(fp, "}");
}
size_t tag__fprintf(struct tag *tag, const struct cu *cu,
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;
else if (tag__is_union(tag))
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
tconf.name_spacing = tag__is_union(tag) ? 21 : 23;
}
if (tconf.type_spacing == 0)
tconf.type_spacing = 26;
}
if (pconf->expand_types)
++tag->recursivity_level;
if (pconf->show_decl_info) {
printed += fprintf(fp, "%.*s", pconf->indent, tabs);
printed += fprintf(fp, "/* Used at: %s */\n", cu->name);
printed += fprintf(fp, "%.*s", pconf->indent, tabs);
printed += tag__fprintf_decl_info(tag, cu, fp);
}
printed += fprintf(fp, "%.*s", pconf->indent, tabs);
switch (tag->tag) {
case DW_TAG_array_type:
printed += array_type__fprintf(tag, cu, "array", pconf, fp);
break;
case DW_TAG_enumeration_type:
printed += enumeration__fprintf(tag, cu, pconf, fp);
break;
case DW_TAG_typedef:
printed += typedef__fprintf(tag, cu, pconf, fp);
break;
case DW_TAG_class_type:
case DW_TAG_interface_type:
case DW_TAG_structure_type:
printed += __class__fprintf(tag__class(tag), cu, pconf, fp);
break;
case DW_TAG_namespace:
printed += namespace__fprintf(tag, cu, pconf, fp);
break;
case DW_TAG_subprogram:
printed += function__fprintf(tag, cu, pconf, fp);
break;
case DW_TAG_union_type:
printed += union__fprintf(tag__type(tag), cu, pconf, fp);
break;
case DW_TAG_variable:
printed += variable__fprintf(tag, cu, pconf, fp);
break;
case DW_TAG_imported_declaration:
printed += imported_declaration__fprintf(tag, cu, fp);
break;
case DW_TAG_imported_module:
printed += imported_module__fprintf(tag, cu, fp);
break;
default:
printed += fprintf(fp, "/* %s: %s tag not supported! */",
__func__, dwarf_tag_name(tag->tag));
break;
}
if (!pconf->no_semicolon) {
fputc(';', fp);
++printed;
}
if (tag__is_function(tag) && !pconf->suppress_comments) {
const struct function *func = tag__function(tag);
if (func->linkage_name)
printed += fprintf(fp, " /* linkage=%s */",
function__linkage_name(func, cu));
}
if (pconf->expand_types)
--tag->recursivity_level;
return printed;
}
void cus__print_error_msg(const char *progname, const struct cus *cus,
const char *filename, const int err)
{
if (err == -EINVAL || (cus != NULL && list_empty(&cus->cus)))
fprintf(stderr, "%s: couldn't load debugging info from %s\n",
progname, filename);
else
fprintf(stderr, "%s: %s\n", progname, strerror(err));
}
void dwarves__fprintf_init(uint16_t user_cacheline_size)
{
if (user_cacheline_size == 0) {
long sys_cacheline_size = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);
if (sys_cacheline_size > 0)
cacheline_size = sys_cacheline_size;
else
cacheline_size = 64; /* Fall back to a sane value */
} else
cacheline_size = user_cacheline_size;
}