]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
ctf: capture all DIEs with structs/enums as their ultimate supertype
authorNick Alcock <nick.alcock@oracle.com>
Thu, 23 Jan 2014 23:22:21 +0000 (23:22 +0000)
committerNick Alcock <nick.alcock@oracle.com>
Tue, 21 Jul 2015 14:29:29 +0000 (15:29 +0100)
dwarf2ctf has to scan all the DWARF in the kernel repeatedly while generating
its compressed type representation: it even has to scan inside functions, even
though it is only interested in file-scope types and variables, because opaque
structure, union, or enumeration references at the top level can contain
references to a definition inside a function.  Repeated scanning of gigabytes of
DWARF is slow: dwarf2ctf spends most of its time doing this, not emitting types.
To make this less abominably slow, dwarf2ctf contains a filtration mechanism to
avoid scanning or emitting DIEs which will never be useful.

An iron rule of dwarf2ctf is that *all* DIEs that are emitted as types must
first be spotted by this 'duplicate detector' scanning phase, since it not only
determines which types are duplicated but also where they should go: whether
they are shared between kernel modules (and should go into the shared type
repository in ctf.ko) or are shared between translation units in a single module
or not duplicated at all, in which case they go into the module's local CTF.  So
if the filtration mechanism skips types which are later emitted, dwarf2ctf can
do little but abort (in general dwarf2ctf aborts if it finds internal
consistency problems which may affect the generation of many types, but skips
types and warns if it finds problems with single types which are not likely to
affect the generation of other types).

One of these filtration functions is filter_ctf_file_scope().  This is called to
filter out DIEs representing most qualifiers and base types when they appear
inside functions, by simply checking if the parent of the DIE is
DW_TAG_compile_unit.  In GCC 4.4.x, applying this to a variety of DIEs was
sufficient to filter out all DIEs inside functions that we weren't going to emit
CTF representations of.

Unfortunately GCC has since changed its debugging information representation
somewhat in a fashion which breaks this.  Inside drivers/firmware/dmi-id.c we
see the example dwarf2ctf calls out when it fails:

    static const struct mafield {
    const char *prefix;
    int field;
    } fields[] = {
    [...]
    { NULL,  DMI_NONE }
    };

    const struct mafield *f;

This is a structure inside a function, but even so GCC generates some of its
type DIEs at global scope and they are emitted into the CTF.  Over time, GCC is
moving more of the DIEs into subprogram scope, but not yet all of them: this is
what has broken us.

The variable 'f' is represented by the following DIEs, where A, B, C and D are
DIE offsets:

 [  A]     structure_type
           name                 (strp) "mafield"
[...]
 [  B]     const_type
           type                 (ref4) [  A]

 [  C]     pointer_type
           byte_size            (data1) 8
           type                 (ref4) [  B]

In GCC 4.4.x, all of these other than the structure_type itself are at the
global scope, not the subprogram scope, so the filter never kicks in for these
DIEs and all of them are emitted successfully.  Between 4.4.x and 4.8.0, the
'const_type' moved into subprogram scope, but the pointer_type did not, so we
have a pointer_type at global scope referring to a const_type at subprogram
scope -- which has been filtered out by the filtration function, so emission of
the pointer_type fails and dwarf2ctf aborts because its type graph is
incomplete, with a type pointing at a type it has no record of.

Fix this by having filter_ctf_file_scope() look at the DW_AT_type attribute of
its target DIE, chaining to its type-attributeless terminus and note whether
that terminus is a structure, union, or enumeration: all DIEs having such types
at the terminus of their type chains must not be filtered out.

Orabug: 18117464
Signed-off-by: Nick Alcock <nick.alcock@oracle.com>
Reviewed-by: Chuck Anderson <chuck.anderson@oracle.com>
Documentation/dwarf2ctf
scripts/dwarf2ctf/dwarf2ctf.c

index 2e28359afa8255e0a2b4894c16f80cc00cff1d9a..4ebe1fc14e79cbfaeef4052922847e6dd9ecbd36 100644 (file)
@@ -359,10 +359,10 @@ tag doesn't appear in the assembly table at all: there's no point doing
 duplicate detection or anything else for a DWARF DIE we won't be generating CTF
 from.  There are currently two filters defined: filter_ctf_file_scope(), which
 is called for every DWARF DIE whose tag is one we never expect to see a
-reference to if it is inside a function, and filter_ctf_uninteresting(), which
-is called for variables to see if they are worthy of recording (top-level named
-variables with external linkage not part of the internal workings of macros
-only).
+reference to if it is inside a function (except if they relate to a structure or
+union, as above), and filter_ctf_uninteresting(), which is called for variables
+to see if they are worthy of recording (top-level named variables with external
+linkage not part of the internal workings of macros only).
 
 
 Type IDs
index 445728cd9d45ab4b54e5d65b5ad8654bbd4ddec2..af3f18e18447b9f3017ae507a43046b33ce0727f 100644 (file)
@@ -499,9 +499,13 @@ typedef int (*ctf_assembly_filter_fun)(Dwarf_Die *die,
 
 /*
  * A CTF assembly filter function which excludes all types not at the global
- * scope (i.e. whose immediate parent is not a CU DIE).
+ * scope (i.e. whose immediate parent is not a CU DIE) and which does not have a
+ * structure or union as its ultimate dependent type.  (All structures and
+ * unions and everything dependent on them must be recorded, even inside
+ * functions, because GCC may emit references to the opaque variants of those
+ * types from file scope.)
  */
-static int filter_ctf_file_scope(Dwarf_Die *die __unused__,
+static int filter_ctf_file_scope(Dwarf_Die *die,
                                 Dwarf_Die *parent_die);
 
 /*
@@ -2581,11 +2585,40 @@ static ctf_id_t lookup_ctf_type(const char *module_name, const char *file_name,
 
 /*
  * A CTF assembly filter function which excludes all types not at the global
- * scope (i.e. whose immediate parent is not a CU DIE).
+ * scope (i.e. whose immediate parent is not a CU DIE) and which does not have a
+ * structure or union as its ultimate dependent type.  (All structures and
+ * unions and everything dependent on them must be recorded, even inside
+ * functions, because GCC may emit references to the opaque variants of those
+ * types from file scope.)
  */
-static int filter_ctf_file_scope(Dwarf_Die *die __unused__, Dwarf_Die *parent_die)
+static int filter_ctf_file_scope(Dwarf_Die *die, Dwarf_Die *parent_die)
 {
-       return (dwarf_tag(parent_die) == DW_TAG_compile_unit);
+       /*
+        * Find the ultimate parent of this DIE.
+        */
+
+       Dwarf_Die dependent_die;
+       Dwarf_Die *dependent_diep = private_dwarf_type(die, &dependent_die);
+
+       if (dependent_diep != NULL) {
+               Dwarf_Die *possible_depp = dependent_diep;
+               do {
+                       Dwarf_Die possible_dep;
+                       possible_depp = private_dwarf_type(possible_depp,
+                                                          &possible_dep);
+
+                       if (possible_depp != NULL)
+                               dependent_die = possible_dep;
+               } while (possible_depp != NULL);
+       }
+
+       if (dependent_diep)
+               return (dwarf_tag(dependent_diep) == DW_TAG_structure_type ||
+                       dwarf_tag(dependent_diep) == DW_TAG_union_type ||
+                       dwarf_tag(dependent_diep) == DW_TAG_enumeration_type ||
+                       dwarf_tag(parent_die) == DW_TAG_compile_unit);
+       else
+               return (dwarf_tag(parent_die) == DW_TAG_compile_unit);
 }
 
 /*