This is useful to call is_type() under GDB.
* include/abg-fwd.h (is_type): Declare new overload that takes a
naked pointer.
* src/abg-ir.cc (is_type): Define new overload that takes a naked
pointer.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
I am seeing issues related to the fact that a declaration-only class A
would compare different to the full version of class A. This is due
to the fact that that the declaration-only A and the full A have
different hashes, even though they structurally compare equal. So
they have different canonical types, with the current code. This
patch arranges for declaration-only classes to have no canonical type,
forcing it to compare structurally to other types. Then the patch
adjusts strip_typedef() that used to expect that all types it sees
have canonical types. Then the patch changes the type hashing code to
avoid making it cache their hash, because otherwise, in some cases
when we hash a type (too) early, a temporary hash of it gets stored ad
infinitum, even after the type has been later updated. Last but not
least, the patch returns a zero hash for declaration-only classes.
* include/abg-fwd.h (keep_type_alive): Declare new function.
* src/abg-ir.cc (strip_typedef): Simplify logic. Support types
that are not canonicalized.
(type_base::get_canonical_type_for): For declaration-only classes,
return an empty canonical class, forcing the class to be compared
structurally.
(keep_type_alive): Define new function.
* src/abg-hash.cc ({decl_base, type_decl, scope_type_decl,
qualified_type_def, pointer_type_def, reference_type_def,
array_type_def, enum_type_decl, typedef_decl,
class_decl::member_class_template, class_decl, type_tparameter,
template_tparameter, }:#️⃣:operator()): Do not cache the
computed hash.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
Propagating redundancy categorization is broken for cases where there
is a diff node that has children nodes carrying changes that are all
filtered out. In that case, if among those children changes there is
a redundant change, normally the parent diff node we are look at
should be marked redundant too. The bug is that it's not marked as
redundant at the moment. This patch fixes that.
* src/abg-comparison.cc (redundancy_marking_visitor::visit_end):
Consider the cases of changes that are a filtered out.
* tests/data/test-diff-filter/libtest27-redundant-and-filtered-children-nodes-v{0,1}.so:
New test binaries to use as test input.
* tests/data/test-diff-filter/test27-redundant-and-filtered-children-nodes-report-{0,1,2}.txt:
New test result baselines.
* tests/data/test-diff-filter/test27-redundant-and-filtered-children-nodes-v{0,1}.cc:
Source code for the test input binaries above.
* tests/test-diff-filter.cc (in_out_spec): Add the binaries to the
test inputs used for this test harness.
* tests/data/Makefile.am: Add the new test material above to the
distribution.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
From inside the comparison engine, I noticed that there were some
discrepancies between some comparison performed there and the
comparison performed from inside the internal representation of
abigail::ir. This can lead to some change reports in which the
reporter thinks there are changes in the IR where there actually are
not. This patch re-uses comparison operators from the generic IR, rather
than re-implementing them in the comparison engine.
* include/abg-ir.h (operator==(scope_decl_sptr, scope_decl_sptr)):
Declare.
(operator==(type_decl_sptr, type_decl_sptr)): Likewise.
(operator==(enum_type_decl_sptr, enum_type_decl_sptr)): Likewise.
* src/abg-comparison.cc (diff_length_of_decl_bases)
(diff_length_of_type_bases): Remove these static functions.
(class_diff::has_changes): Re-use the comparison operator for
class_decl_sptr.
(type_decl_diff::has_changes): Re-use the comparison operator for
type_decl_sptr.
* src/abg-ir.cc (operator==(scope_decl_sptr, scope_decl_sptr)):
Define.
(operator==(type_decl_sptr, type_decl_sptr)): Likewise.
(operator==(enum_type_decl_sptr, enum_type_decl_sptr)): Likewise.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
While diff-ing libstdc++ against EL 6.5 and EL 7, in the report for
virtual member functions of class types, I noticed that there were
some un-necessary white spaces emitted there. This patch fixes that.
* src/abg-comparison.cc (class_diff::report): When reporting
virtual member functions make sure to emit the newline only if one
report for member function has already been emitted.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
This speeds up comparison of function types.
* src/abg-dwarf-reader.cc (build_function_decl): Call
maybe_canonicalize_type to canonicalize the function type.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
In the code that compares function types, smart pointers handling showed up
on the hot path of some performance profile. This patch uses naked
pointers there.
* src/abg-ir.cc (equals): In the overload for function types, use
more naked pointers, less smart pointers.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
Some smart pointers handling got high on performance profiles. I am
passing those by reference here.
* include/abg-fwd.h (get_member_is_static, is_member_function)
(get_member_function_is_ctor, set_member_function_is_ctor)
(get_member_function_is_dtor, set_member_function_is_dtor)
(get_member_function_is_const, set_member_function_is_const)
(get_member_function_vtable_offset)
(set_member_function_vtable_offset)
(get_member_function_is_virtual): Declare the smart pointer
parameter of these as being passed by reference.
* include/abg-ir.h (get_member_access_specifier)
(get_member_is_static, get_member_access_specifier)
(set_member_function_is_ctor, set_member_function_is_const)
(set_member_function_vtable_offset): Likewise, for these friend
declarations to the decl_base type.
* src/abg-ir.cc (get_member_access_specifier)
(get_member_is_static, is_member_function)
(get_member_function_is_ctor, set_member_function_is_ctor)
(get_member_function_is_dtor, set_member_function_is_dtor)
(get_member_function_is_const, set_member_function_is_const)
(get_member_function_vtable_offset)
(set_member_function_vtable_offset)
(get_member_function_is_virtual): In these definitions, the smart
pointer parameter is passed by reference.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
Accessing the context relationship of declarations and setting some
member properties appear to be high in performance profiles due to
shared pointer handling. This patch makes the context relationship
accessors return a naked pointer and also passes a bunch of shared
pointer as references around.
* include/abg-fwd.h (set_member_is_static): Add an overload that
takes the member as a reference to a smart pointer.
(set_member_function_{is_dtor, is_ctor, is_const, vtable_offset,
is_virtual}): Pass the member function as a reference.
(set_member_function_is_const, set_member_function_is_virtual):
Pass the member function as a non-const reference.
* include/abg-ir.h (decl_base::get_context_rel): Return a naked
pointer.
(set_member_is_static, set_member_function_is_virtual): Adjust
this friend declaration.
(set_member_access_specifier): Add an overload that takes a
reference to the member. Pass a reference to smart pointer to the
other overload.
(set_member_function_is_{is_ctor,is_dtor,is_const,is_virtual,vtable_offset}):
Take a non-const reference to function_decl.
* src/abg-ir.cc (decl_base::get_context_rel): Likewise.
(equals(const decl_base&, const decl_base&, change_kind*)):
Adjust.
(equals(const var_decl&, const var_decl&, change_kind*)):
Likewise.
(get_member_access_specifier, get_member_is_static)
(set_data_member_offset, get_data_member_offset)
(set_data_member_is_laid_out, get_data_member_is_laid_out)
(get_member_function_is_ctor, set_member_function_is_ctor)
(get_member_function_is_dtor, set_member_function_is_dtor)
(get_member_function_is_const, set_member_function_is_const)
(get_member_function_vtable_offset)
(set_member_function_vtable_offset)
(get_member_function_is_virtual, set_member_function_is_virtual):
Likewise.
(set_member_access_specifier): Add an overload that takes a
reference to decl_base.
(set_member_is_static, set_member_function_{is_dtor, is_ctor,
is_const, vtable_offset, is_virtual}): Pass the member function as
a reference.): Add an overload that takes the member as a
reference, and write the older overload in terms of the new one.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
* incude/abg-ir.h (decl::get_{qualified_name,
qualified_parent_name}): Return a reference to a string rather
than a copy of a string.
(qualified_type_def::get_qualified_name): Likewise.
(reference_type_def::get_qualified_name): Likewise.
(array_type_def::get_qualified_name): Likewise.
(class enum_type_decl::enumerator): Make this is an out-of-line
pimpled class implementation.
(enum_type_decl::enumerator::{get, set}_enum_type): Declare new
method.
(enum_type_decl::enumerator::get_qualified_name): Change this so
that it doesn't take the name of the enum type anymore.
* src/abg-comparison.cc (enum_diff::report): Adjust for
enum_type_decl::enumerator::get_qualified_name() not taking the
name of the enum type anymore.
* src/abg-ir.cc (decl_base::get_qualified_parent_name): Return a
reference to string.
(decl_base::get_qualified_name): Likewise.
(decl_base::get_qualified_name(string&)): Use the new verson of
decl_base::get_qualified_name() that returns a reference.
({qualified_type_def, pointer_type_def, reference_type_def,
array_type_def}::get_qualified_name()): Return a string reference.
({qualified_type_def, pointer_type_def, reference_type_def,
array_type_def}::get_qualified_name(string& qualified_name)
const): Use the new qualified_type_def::get_qualified_name() that
returns a string reference.
(class enum_type_decl::priv): New type.
(enum_type_decl::{get_underlying_type, get_enumerators}): Adjust.
(enum_type_decl::{enumerator::enumerator, enumerator::operator==,
enumerator::get_name, enumerator::get_qualified_name,
enumerator::set_name, enumerator::get_value,
enumerator::set_value, enumerator::get_enum_type,
enumerator::set_enum_type}): Define methodes out-of-line here.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
Better support typedefs with void underlying types.
* src/abg-ir.cc (strip_typedef): Consider that the underlying type
can be void.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
The DWARF reader assumes that the DIEs for all member types are seen
by build_class_type_and_add_to_ir(), as member type DIEs of the DIE of
the class. Well that assumption is not correct because there can be
errors in the DWARF we are looking at. One of these errors I stumbled
accross is that a DIE for a typedef that should be a member typedef is
actually a child of a *function* DIE. And that function DIE is a
child of the class. Go figure. In any case, get_scope_for_die()
already fixes that up and behaves as if the DIE of the typedef is a
child of the DIE of the class. A side effect of this is that when
build_class_type_and_add_to_ir() reads the DIE of the class, it never
sees the DIE for that typedef.
The takeaway of this state of affairs is that we cannot rely on
build_class_type_and_add_to_ir() to update the member access specifier
for member types because it does not see all member types. Rather
build_ir_node_from_die() detects (reliably) that the type is a member
type and updates the access specifier there.
I also realize that the "is_member_type" flag of
build_ir_node_from_die() and friends is useless now because inside
build_ir_node_from_die() to know that that the type we are building is
a member type, we just need to look at the scope and see if it's a
class type.
So by doing all this, this patch fixes the fact that some types were
not being canonicalized because build_class_type_and_add_to_ir() was
not seeing them. Ahhhh, DWARF.
* include/abg-fwd.h (is_class(decl_base*)): Return a class_decl*
rather than just a bool.
* abg-ir.cc (is_class(decl_base*)): Return a class_decl* rather
than just a bool. Simplify the implementation.
* src/abg-dwarf-reader.cc
(maybe_set_member_type_access_specifier): Define new static
function.
(build_ir_node_from_die): Remove the is_member_type flag. When
building member types set their access specifier. Simplify the
logic of detecting that a type is a member type; basically
delegate taht to the new maybe_set_member_type_access_specifier().
(build_class_type_and_add_to_ir): Do not try to set the member
type access specifiers anymore.
(build_qualified_type, build_pointer_type, build_reference_type)
(build_typedef_type, build_var_decl, build_function_decl): Adjust.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
Now that we have type canonicalizing, there is no need for trying to
be smart when comparing types; just do the comparison and it should be
fast. Plus in the case of enum_diff, we just getting it wrong as we were
not checking several parts of the enum type, like the member access
specifiers if it was a member type, etc ...
* src/abg-comparison.cc (enum_diff::has_changes): Just use the
normal comparison operator to compare the two enums here. It's
fast now.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
Until now, after the ABI corpus was built from DWARF, the translation
units of the corpus were walked and each function was considered for
addition into the set of exported decls. During that walking, a first
version of the set was put into a std::list and then, a set of filters
(user-provided tunables like a list of regular expressions to keep or
remove some functions from the exported decls) is applied to that list
and the final set of exported decls is put in a std::vector.
Profiling has shown that this process of building the set of exported
decls is a hot spot and also that the current use of std::list was a
big memory consumer especially on binaries with large exported symbol
tables.
So this patch builds the set of exported decls "on the fly", during
DWARF reading, as opposed to waiting after the DWARF is read and
having to walk the corpus again. The corpus defines a policy object
that encapsulates the methods for determining if a function or
variable ought to be part of the set of exported decls. The DWARF
reader uses that policy object to determine which functions and
variables among those built during the reading ought be part of the
exported decls; the policy object also has a reference to the final
vector (managed by the corpus) that must hold the exported decls, so
the decls are put in that vector directly without unnecessary copying.
Profiling also showed that the string copying done by
{var_decl,function_decl}::get_id() was a hot spot. So the patch
returns a reference there.
With this patch applied, the peak memory consumption of abidiff on
libabigail.so itself (abidiff libabigail.so libabigail.so) is 54MB of
resident and takes 2 minutes and 16s (on my slow system). Without the
patch the peak consumption was more than 300MB and it was taking
slightly longer.
For the test of bug
https://sourceware.org/bugzilla/show_bug.cgi?id=17948, memory
consumtion and wall clock time spent is down from 3.4GB and 1m59s to
760MB and 0m43s.
* include/abg-ir.h ({var,function}_decl::get_id): Return a
reference.
* src/abg-ir.cc ({var,function}_decl::get_id): Return a reference
to the string rather than copying it over.
* include/abg-corpus.h (class corpus::exported_decls_builder):
Declare new type.
(corpus::{sort_functions, sort_variables,
maybe_drop_some_exported_decls, get_exported_decls_builder}):
Declare new methods.
* src/abg-corpus.h (corpus::exported_decls_builder::priv): Define
new type.
(class symtab_build_visitor_type): Remove this type that is
useless now.
(corpus::exported_decls_builder::{exported_decls_builder,
exported_functions, exported_variables,
maybe_add_fn_to_exported_fns, maybe_add_var_to_exported_vars}):
Define new functions.
(corpus::priv::is_public_decl_table_built): Remove this data
member. It's now useless.
(corpus::priv::priv): Adjust.
(corpus::priv::build_public_decl_table): Remove this member
function. It's now useless.
(corpus::{priv::build_unreferenced_symbols_tables, get_functions,
get_variables}): No need to build the public decls table here.
It's already built by the time the corpus is read from DWARF now.
(corpus::{sort_functions, sort_variables,
maybe_drop_some_exported_decls, get_exported_decls_builder}):
Define new member functions.
* src/abg-dwarf-reader.cc (read_context::exported_decls_builder):
New data member.
(read_context::read_context): Initialize it.
(read_context::{exported_decls_builder,
maybe_add_fn_to_exported_fns, maybe_add_var_to_exported_vars}):
Define new member functions.
(read_debug_info_into_corpus): Get the the new
'exported_decls_builder' object from the corpus and stick it into
the read context so the DWARF reading code can use it to build the
exported decls set. When the DWARF reading is done, sort the set
of exported functions and variables that was built.
(build_ir_node_from_die): When a function or variable is built,
consider putting it into the set of exported decls.
* tools/abicompat.cc (main): Now that the exported decls is built
*before* we had a chance to stick the list of symbol IDs to keep,
call corpus::maybe_drop_some_exported_decls() to update the set of
exported decls we should consider for the corpus.
was applied to that list and the final
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
So abidiff libabigail.so libabigail.so is broken again. Sigh.
It was broken by this wrong commit:
commit 5b33223cb7
Author: Dodji Seketeli <dodji@redhat.com>
Date: Fri Feb 20 13:48:48 2015 +0100
Simplify canonicalizing handling for typedefs
* src/abg-dwarf-reader.cc (build_ir_node_from_die): For typedefs,
we don't need to test that the current scope is a class to know
that we are looking at a member type. Just looking at the
is_member flag is enough.
So the issue arises when for instance, we are reading a class that
defines a member typedef (or enum) and uses that enum as the type of a
data member. When reading that data member (before reading the
definition of the typedef), we read the type of the data member; so we
hit the typedef. But build_ir_node_from_die() cannot fully construct
the scope of the typedef before handing off the typedef because we are
currently building it! So it hands out a non-complete version of the
class that is being built; 'is_member' is not set to 'true' because
we are getting the type of the data member; it's not *necessarily* a
member type. So we need to check !is_class_type(scope) to know if we
are given a member type. I am now thinking that the "is_member" flag
is actually useless. I think I'll remove it in a later patch.
Anyway, this fixes 'abidiff libabigail.so libabigail.so' again. I
have some stashed patches that brings it's time down to ~ 45 seconds.
So we are getting close to being able to include that *ultimate* test in
regression test suite. Oh well.
* src/abg-dwarf-reader.cc (build_ir_node_from_die): When building
typedefs, enum and memeber classes, check that the scope is a
member class to detect if we are building a member type. In which
case the caller is going to handle the canonicalizing of the
member type *after* it's access specification has been adjusted.
Otherwise, that adjustments happens after the type has been
canonicalized and bad things happen at comparison type.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
Bug URL: https://sourceware.org/bugzilla/show_bug.cgi?id=17649.
abidiff stumbled accross a diff graph with cycles. And it kept
walking that graph endlessly. Of course.
It turned out on such graphs with cycles, the categorizing code that
uses abigail::comparison::diff::traverse() to walk the graph and
categorize the diff nodes was traversing the same class of equivalence
of certain diff nodes more than once without even noticing.
This patch changes the logic of the diff graph traversing code to make
it always call diff_node_visitor::visit_begin() on the visitor for a
diff node prior to visiting it (visiting means calling
diff_node_visitor::visit()) and diff_node_visitor::visit_end() after
visiting it.
But when the diff node has already been visited and it's reached again
by the traversing code (in case of a cycle) then the
diff_node_visitor::visit_begin() is called, but
diff_node_visitor::visit() is *NOT*. Then
diff_node_visitor::visit_end() is called. In other words, even when
the diff node is not visited (because it's already been visited) the
pair diff_node_visitor::{visit_begin,visit_end}() is called.
This avoids traversing the diff node (or rather the equivalence class
of the diff node) more than once even in presence of cycles, but still
gives a chance to custom visitors to detect that they are seeing a
cycle and act accordingly if need be. This is a kind of cycle
detection feature.
Then the code of the (harmless and harmful categorization) filters has
been adapted to always rely on the cycle detection feature. The code
of the category propagation visitor has also been adapted to propagate
the category of a given diff node to and from its canonical diff node.
* include/abg-comp-filter.h (harm{less,ful}_filter::visit_end):
Declare new methods.
* include/abg-comparison.h (diff_context::maybe_apply_filters):
Remove the traverse_nodes_once flag.
* src/abg-comp-filter.cc (apply_filter): Force the traversing to
operate in cycle avoidance mode.
(harm{less,ful}_filter::visit): Update the category of the
canonical node too.
(harm{less,ful}_filter::visit_end): Define new method.
* src/abg-comparison.cc (diff_context::maybe_apply_filters):
Remove the traverse_nodes_once flag. Adjust. Simplify logic.
(diff::traverse): Always call diff_node_visitor::{begin,end}. If
the node has already been visited previously then do not call
diff_node_visitor::visit() and do not visit the children nodes.
(category_propagation_visitor::visit_end): If the node has
already been visited, then propagate the category from the
canonical nodes of the children nodes.
(propagate_categories): Force the traversing to operate in cycle
avoidance mode.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
* src/abg-comparison.cc (distinct_diff::report): After calling
report_size_and_alignment_changes, one needs to add a new line if
some stuff got emitted out the output stream.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
Since we now have proper diff node filtering capabilities, it appears
that there can be distinct diff node that is deemed to be reported
even though it's child node is not; this happens when the distinct
diff node does carry local changes. So remove the assert that was
saying otherwise and enjoy one less abort.
* src/abg-comparison.cc (distinct_diff::report): Remove over-eager
assert.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
The late canonicalizing code needed factorizing to increase
maintainability.
* src/abg-dwarf-reader.cc
(read_context::{canonicalize_types_scheduled,
perform_late_type_canonicalizing}): Factorize these from ...
(build_translation_unit_and_add_to_ir): ... here.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
It turns out the new 'is_member' flag of build_ir_node_from_die()
really means 'is this DIE for member type'. So let's make it clear
now.
* src/abg-dwarf-reader.cc (build_ir_node_from_die): Rename
is_member into is_member_type. Adjust.
(get_scope_for_die, build_translation_unit_and_add_to_ir)
(build_namespace_decl_and_add_to_ir): Adjust.
(build_class_type_and_add_to_ir): Adjust. Adjust set to false
when calling build_ir_node_from_die() to build a function_decl.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
* src/abg-dwarf-reader.cc (build_ir_node_from_die): For typedefs,
we don't need to test that the current scope is a class to know
that we are looking at a member type. Just looking at the
is_member flag is enough.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
* src/abg-dwarf-reader.cc (build_ir_node_from_die): When a class
is not a member type, then it at least ought to be scheduled for
late canonicalizing.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
A member enum type's canonicalizing is handled by the member type
handling code of the build_class_type_and_add_to_ir(). So do not try
to canonicalize from elsewhere.
* src/abg-dwarf-reader.cc (build_ir_node_from_die): Once we've
built the enum type by calling build_enum_type(), do not try to
canonicalize it here if it's a member type. The calling
build_class_type_and_add_to_ir() must deal with it already.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
In the debug info of least up to GCC 4.4.x, pointer, reference, array and
qualified types were in the global scope. In GCC 4.8.x they can
belong to the scope of their sub-type. The comparison code of
libabigail can thus (wrongly) consider that a qualified type described
by GCC 4.4.x debug info is different from the debug info of the *same*
qualified type emitted by GCC 4.8.x just because their scopes are
different. The scope of qualified, pointer and reference types should
not matter anyway. So this patch makes these composite types belong
to the global scope, irrespective of where they appear in the debug
info. I have seen this when comparing libstdc++ from RHEL 6.5 and 7.
This is visible now that we have type canonicalizing.
* src/abg-dwarf-reader.cc (build_class_type_and_add_to_ir): Do not
consider qualified, pointer, reference and array types as member
types. Only typedef, class and enum types are.
(build_ir_node_from_die): Stick base, pointer, reference,
qualified and array types into the global scope.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
Sometimes during the reading of debug info, when creating a given
composite T, calling build_ir_node_from_die() to get the IR node for
their sub-type might lead to the creation of T, while we are in the
function that is supposed to create it. In that case, just return the
type that has then be created from underneath us, rather than creating
a new one.
* src/abg-dwarf-reader.cc (build_qualified_type)
(build_pointer_type_def, build_reference_type, build_array_type)
(build_typedef_type): If the composite type we are about to create
was already created, just return the one that exists already.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
It appears we were not canonicalizing the underlying type of enum type
and the void type. We could catch this now that we are requiring that
abigail::ir::strip_typedef() works on canoncialized types only. This
patch fixes that.
* src/abg-dwarf-reader.cc (build_enum_type): Canoncialize the
underlying type of the enum type.
(build_ir_node_for_void_type): Canonicalize the void type.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
There are cases where we forget to associate some DIEs to the types
they represent. I have seen this while comparing libstdc++ from RHEL
6.5 and RHEL 7.
This patch moves the DIE->type association to the build_* functions
that actually creates the types, as opposed to doing it in the callers
of these build_* functions.
* src/abg-dwarf-reader.cc (build_type_decl, build_enum_type)
(build_qualified_type, build_pointer_type_def)
(build_reference_type, build_typedef_type)
(build_class_type_and_add_to_ir): Take a new flag that says if the
DIE is from the alternate debug info section or not. Perform the
DIE->type association in these functions. Note that in
build_class_type_and_add_to_ir we are now doing the DIE->type
association even for declaration-only classes. And for member
types, do not bother doing the association because it's already
been done by build_ir_node_from_die().
(build_ir_node_from_die): Do not do the DIE->type association here
anymore. Adjust to the new signature of the build_* functions
above that actually build the types.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
A corpus is made of several translation units; when reading the
corpus, there is data (maintained in the reading context) that is
relevant to all the translation units. But there is also data that is
relevant to the current translation unit being read only; and data
should cleared before starting to read the next translation unit.
Otherwise, bad and subtle issues happen. This patch clears some
per-TU data that I forgot to clear recently.
The patch also does some code factorizing to increase maintainability.
* src/abg-dwarf-reader.cc
(read_context::die_type_map): New accessor for the two DIE->Type
maps we have; the one of the main debug info section and the one
of the alternate debug info section.
(read_context::{associate_die_to_type,
lookup_type_from_die_offset}): use the new die_type_map()
accessor.
(read_context::clear_per_translation_unit_data): Factorize this
from build_translation_unit_and_add_to_ir(). Also, add code to
clear the DIE->type map as well as the vectors of offsets of the
types of the DIEs to canonicalize after the translation unit has
been read.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
* tests/runtestcanonicalizetypes.sh.in (binaries): Refer to
abg-tools-utils, not abg-tools-utils.o; the extension is computed
automatically, depending on the underlying platform.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
strip_typedef(), when constructing new pointers,
references and other composite types was building new types that
weakly referred to their sub-types; for instance, a pointer type
has a weak reference on its pointed-type. That means the referred-to
type must be 'own' by something else. That means that strip_typedef()
needs to create types which lifetime is "long enough". This patch
ensures that strip_typedef() returns a canonical type; and we are sure
that a canonical type is live during the entire life time of the
libabigail library itself.
So that means strip_typedef can only be used after types have been
canonicalized. To that end, this patch changes is_class_type() to
make it not strip typedefs. That way, is_class_type() can be used
even when canonicalized types are not yet available. The patch then
introduces a new is_compatible_with_class_type() function that strips
typedef. The code of type_size_changed() that wanted to strip
typedefs is then adjusted to use this new
is_compatible_with_class_type() instead.
* include/abg-fwd.h (is_compatible_with_class_type): Declare new
function.
(canonicalize): Move the declaration here, from ...
* include/abg-ir.h (canonicalize): ... here.
* src/abg-ir.cc (strip_typedef): Assert that the input type is
canonicalized. Make sure that weak references are on
canonicalized types. Make sure that the returned type is a
canonical one.
(canonicalize): Make this return the canonical type that it has
computed.
* src/abg-comp-filter.cc (type_size_changed): Use the new
is_compatible_with_class_type() function, instead of
is_class_type().
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
function_decl::get_id() var_decl::get_id() showed up high on CPU usage
profiles. This patch thus implements a caching-version of these
version and it incurred a 10% (at least) speed on binaries with a lot
publicly exported symbols.
* src/abg-ir.cc (var_decl::priv::id_): New data member.
(var_decl::get_id): Cache the result on the first invocation and
and returns it on subsequent invocations.
(function_dec::priv::id_): New data member.
(function_decl::get_id): Cache the result on the first invocation
and and returns it on subsequent invocations.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
Profiling showed that symbol version info reading was on the hot spot.
This patch implements caching the symbol table and symbol
version-related sections. It incurred speed ups of at least 20% on
big binaries with a lot versionned symbols.
* src/abg-dwarf-reader.cc (find_symbol_table_section)
(get_symbol_versionning_sections): Forward declare these existing
static functions.
(read_context::{symtab_section_,
symbol_versionning_sections_loaded_,
symbol_versionning_sections_found_, versym_section_
verdef_section, verneed_section}): New data members.
(read_context::read_context): Initialize them.
(read_context::{find_symbol_table_section,
get_symbol_versionning_sections, get_version_for_symbol}):
Implement a caching version of their exisiting non-caching
counterpart.
(read_context::lookup_elf_symbol_from_index): Use the new caching
functions read_context::find_symbol_table_section and
read_context::get_version_for_symbol.
(read_context::load_symbol_maps): Likewise, use the new caching
function read_context::find_symbol_table_section.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
During the building of the list of publicly exported functions and
variables from a corpus, when we visit a function it's useless (and
time consuming) to visit the sub-nodes of the function. This patch
does away with that.
* src/abg-corpus.cc (symtab_build_visitor_type::visit_begin):
Replace symtab_build_visitor_type::visit_end with this and return
false.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
Some *huge* sub-trees might not carry any change. In that case do not
bother applying the filter because eventually no filter is going to be
applied anyway. This can save us a lot of walking time.
* src/abg-comp-filter.cc ({harmless, harmful}_filter::visit): Do
not try to do the categorizing on a diff sub-tree that does
not carry any change.
* src/abg-comparison.cc (diff_context::maybe_apply_filters): Do
not bother trying to apply the filters on a diff sub-tree that
does not carry any change.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
While trying to diff two identical files (abidiff foo.so foo.so) it
appeared that canonicalizing types during e.g, the DWARF reading
process was leading to subtle errors because it's extremely hard to
know when a type is complete. That is, during the building of a class
type C, a pointer to C can be built before C is complete. Worse, even
after reading the DIE (from DWARF) of class C, there can be DIE seen
later in the translation unit that modifies type C. In these late
cases, one needs to wait -- not only until C is fully built, but also
sometimes, after the translation unit is fully built -- to
canonicalize C and then the pointer to C. This kind of things.
So now there are two possible points in time when canonicalization of
a type can happen. It can happen early, when the type is built. This
is the case for basic types and composite types for which all
sub-types are canonicalized already. It can happen late, right after
we've finished reading the debug info for the current translation
unit.
So this patch fixes the IR traversal and uses that to walk the
translation unit (or even types) after it's built. It does away with
the first attempt to perform early canonicalizing only.
The patch also handles type canonicalizing while reading xml-abi
format.
* include/abg-fwd.h (is_class_type)
(type_has_non_canonicalized_subtype): Declare new functions.
(is_member_type): Remove the overload that takes a decl_base_sptr.
It's superfluous. We just need the one that takes a
type_base_sptr.
* include/abg-ir.h (translation_unit::{is_constructed,
set_is_constructed}): Add new methods.
(class_decl::has_virtual_member_functions): Likewise.
(class decl_base): Makes it virtually inherit ir_traversable_base.
(class type_base): Make this virtually inherit traversable_base
too.
(type_base::canonicalize): Renamed enable_canonical_equality
into this.
(type_base::traverse): Declare new virtual method.
(canonicalize): Renamed enable_canonical_equality into this.
(scope_type_decl::traverse): Declare new virtual method.
(namespace_decl::get_pretty_representation): Declare new virtual
method.
(function_type::traverse): Likewise.
(class_decl::base_spec::traverse): Likewise.
(ir_node_visitor::visit): Remove the overloads and replace each of
them with a pair of ...
(ir_node_visitor::{visit_begin, visit_end}): ... of these.
* include/abg-traverse.h (traversable_base::visiting): New
method.
(traversable_base::visiting_): New data member.
(traversable_base::traversable_base): New constructor.
* src/abg-ir.cc ({scope_decl, type_decl, namespace_decl,
qualified_type_def, pointer_type_def, reference_type_def,
array_type_def, enum_type_decl, typedef_decl, var_decl,
function_decl, function_decl::parameter, class_decl,
class_decl::member_function_template,
class_decl::member_class_template, function_tdecl,
class_tdecl}::traverse): Fix this to properly set the
traversable_base::visiting_ flag and to reflect the new signatures
of the ir_node_visitor methods.
({type_base, scope_type_decl, function_type,
class_decl::base_spec}::traverse): New method.
(type_base::get_canonical_type_for): Handle the case of the type
already having a canonical type. Properly hash the type using the
dynamic type hasher. Look through declaration-only classes to
consider the definition of the class instead. Fix logic to have a
single pointer of return, to ease debugging.
(canonicalize): Renamed enable_canonical_equality into this.
(namespace_decl::get_pretty_representation): Define new method.
(ir_node_visitor::visit): Replace each of these overloads with a
pair of visit_begin/visit_end ones.
(translation_unit::priv::is_constructed_): New data member.
(translation_unit::priv::priv): Initialize it.
(translation_unit::{is_constructed, set_is_constructed}): Define
new methods.
(is_member_type(const decl_base_sptr)): Remove.
(is_class_type(decl_base *d)): Define new function.
(class_decl::has_virtual_member_functions): Define new method.
(equals(const class_decl&, const class_decl&, change_kind*)): If
the containing translation unit is not constructed yet, do not
take virtual member functions in account when comparing the
classes. This is because when reading from DWARF, there can be
DIEs that change the number of virtual member functions after the
DIE of the class. So one needs to start taking virtual members
into account only after the translation unit has been constructed.
(class non_canonicalized_subtype_detector): Define new type.
(type_has_non_canonicalized_subtype): Define new function.
* src/abg-corpus.cc (symtab_build_visitor_type::visit): Renamed
this into symtab_build_visitor_type::visit_end.
* src/abg-dwarf-reader.cc (die_type_map_type): New typedef.
(die_class_map_type): This is now a typedef on a map of
Dwarf_Off/class_decl_sptr.
(read_context::{die_type_map_, alternate_die_type_map_,
types_to_canonicalize_, alt_types_to_canonicalize_}): New data
members.
(read_context::{associate_die_to_decl,
associate_die_to_decl_primary}): Make these methods public.
(read_context::{associate_die_to_type,
lookup_type_from_die_offset, is_wip_class_die_offset,
types_to_canonicalize, schedule_type_for_canonicalization}):
Define new methods.
(build_type_decl, build_enum_type)
(build_class_type_and_add_to_ir, build_qualified_type)
(build_pointer_type_def, build_reference_type, build_array_type)
(build_typedef_type, build_function_decl): Do not canonicalize
types here.
(maybe_canonicalize_type): Define new function.
(build_ir_node_from_die): Take a new flag that says if the ir node
is a member type/function or not. Early-canonicalize base types.
Canonicalize composite types that have only canonicalized
sub-types. Schedule the other types for late canonicalizing. For
class types, early canonicalize those that are non-member types,
that are fully constructed and that have only canonicalized
sub-types. Adjust to the new signature of build_ir_node_from_die.
(get_scope_for_die, build_namespace_decl_and_add_to_ir)
(build_qualified_type, build_pointer_type_def)
(build_reference_type, build_array_type, build_typedef_type)
(build_var_decl, build_function_decl): Adjust for the new
signature of build_ir_node_from_die.
(build_translation_unit_and_add_to_ir): Likewise. Perform the
late canonicalizing of the types that have been scheduled for
that.
(build_class_type_and_add_to_ir): Return a class_decl_sptr, not a
decl_base_sptr. Adjust for the new signature of
build_ir_node_from_die. Early canonicalize member types that are
created and added to a given class, or schedule them for late
canonicalizing.
* src/abg-reader.cc (class read_context::{m_wip_classes_map,
m_types_to_canonicalize}): New data members.
(read_context::{clear_types_to_canonicalize,
clear_wip_classes_map, mark_class_as_wip, unmark_class_as_wip,
is_wip_class, maybe_canonicalize_type,
schedule_type_for_late_canonicalizing,
perform_late_type_canonicalizing}): Add new method definitions.
(read_context::clear_per_translation_unit_data): Call
read_context::clear_types_to_canonicalize().
(read_translation_unit_from_input): Call
read_context::perform_late_type_canonicalizing() at the end of the
function.
(build_function_decl): Fix the function type canonicalizing (per
translation) that was already in place. Do the canonicalizing of
these only when the type is fully built. Oops. This was really
brokend. Also, when the function type is constructed, consider it
for type canonicalizing.
(build_type_decl): Early canonicalize basic types.
(build_qualified_type_decl, build_pointer_type_def)
(build_pointer_type_def, build_reference_type_def)
(build_array_type_def, build_enum_type_decl, build_typedef_decl):
Handle the canonicalizing for these composite types: either early
or late.
(build_class_decl): Likewise. Also, mark this class a 'being
built' until it's fully built. This helps the canonicalizing code
to know that it should leave a class alone until it's fully built.
* tests/test-ir-walker.cc (struct name_printing_visitor): Adjust
to the visitor methods naming change.
* configure.ac: Generate the tests/runtestcanonicalizetypes.sh
testing script from tests/runtestcanonicalizetypes.sh.in.
* tests/runtestcanonicalizetypes.sh.in: Add the template for the
new runtestcanonicalizetypes.sh script that test for type
canonicalizing.
* tests/Makefile.am: Add the new runtestcanonicalizetypes.sh
regression testing script to the build system.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
It's more maintainable to have a function that clears the per
translation unit data that needs to be erased before we parse the next
translation unit using the same reading context. This patch does just
that. Subsequent patches use this cleanup.
* src/abg-reader.cc
(read_context::clear_per_translation_unit_data): Factorize this
function out of ...
(read_context::read_translation_unit_from_input): ... this one.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
While looking at something else, it occurs to me that we ought to use
the deep sptr equality operator when we can.
* src/abg-ir.cc (equals): On function_decl overload, use the deep
sptr type equality operator when comparing types.
(non_type_tparameter::operator==): Likewise.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
* src/abg-ir.cc (equals(const function_decl&, const
function_decl&, change_kind*)): Compare virtualness of member
function before comparing their vtable offsets.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
* include/abg-ir.h (reference_type_def::get_pointed_to_type): use
type_base_sptr, rather than shared_ptr<type_base>
(typdef_decl::get_underlying_type): Likewise.
(function_decl::get_return_type): Likewise.
(function_decl::set_type): Likewise.
(class_decl::member_class_template::as_class_tdecl): Likewise.
* src/abg-comparison.cc (compute_diff): Remove useless vertical
space.
(corpus_diff::traverse): Add a vertical space after this.
* src/abg-dwarf-reader.cc (type_ptr_map): Remove this unused
typedef.
(get_version_for_symbol)
(finish_member_function_reading): Fix the comments of these
functions.
* src/abg-reader.cc (build_function_decl): Return a
function_decl_sptr rather than a shared_ptr<function_decl>.
(build_qualified_type_decl)
(build_pointer_type_def, build_reference_type_def)
(build_array_type_def, build_typedef_decl, build_class_decl): Use
the is_<someking_of_type> functions here, rather than using the
dynamic cast. This increases maintainability.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
Profiling of abidiff or rather abidw on an Xorg binary that is
dwarf-compressed revealed that find_last_import_unit_point_before_die
was a hot spot. This patch optimizes it for speed by ooking for the
inclusion point in reverse topological order.
* src/abg-dwarf-reader.cc
(find_last_import_unit_point_before_die): Look for the inclusion
point of the partial unit in reverse topological order.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
Profiling has shown that there can be a *lot* of class_diff nodes that
actually represent the same diffs. In other words, these instances of
class_diff that is they are in the same equivalence class. In that
case the private data of these class_diff node consume a lot of
redundant memory. This patch is an optimization that leverages that
insight. It shares the private data of the class_diff nodes that are
in the same equivalence class.
This makes the memory consumption of abidiff on the Xorg binaries of
RHEL 6 and 7 drop from more than 6GB to less than 370MB; execution
time drops from 15 to 7 minutes.
* src/abg-comparison.cc (class_diff::class_diff): Do not
initialize the private data of class_diff here.
(compute_diff): In the overload for class_diff, initialize the
private data of the new instance of class_diff to the private data
of its canonical instance.
(redundancy_marking_visitor::visit_begin): If a node is marked
redundant, do not dare visit its children. In cases of classes
that have members that reference themselves, this prevents us from
wrongly marking some of the data member changes as being
redundant.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
While looking at something else, I stumbled accross this two-liner.
* src/abg-comparison.cc (diff_context::maybe_apply_filters): Do
not crash when called with a NULL diff.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
Comparing types that are equal showed up high in profiles. This patch
is an answer to that. It implements the notion of canonical type for
types known to libabigail. Then when comparing two types, if they
have a canonical types, just comparing the pointer value of their
canonical type is enough. This speeds up type comparison somewhat;
comparing the Xorg binaries from rhel 6 and 7 goes from more than 20h
(I gave up after that) to under 15 minutes.
* include/abg-ir.h (class type_base): Pimplify this class.
(type_base::canonical_types_map_type): New typedef.
(type_base::{get_canonical_types_map, get_canonical_type_for,
get_canonical_type}): Declare new member functions.
(enable_canonical_equality): Declare new function.
(struct type_base::hash): Declare this functor here.
* src/abg-ir.cc ():
* src/abg-dwarf-reader.cc (build_type_decl, build_enum_type)
(build_class_type_and_add_to_ir, build_qualified_type)
(build_pointer_type_def, build_reference_type, build_array_type)
(build_typedef_type, build_function_decl): Enable canonical
equality for the resulting type returned by these functions.
* src/abg-hash.cc (type_base:#️⃣:operator()(const type_base&)):
Adjust as this is now out-of-line. Also, add two overloads for
type_base* and type_base_sptr.
(struct type_base::priv): Define new type for private data of
type_base.
(type_base::{get_canonical_types_map, get_canonical_type_for,
get_canonical_type}): Define new member functions.
(enable_canonical_equality): Define new function
(type_base::{type_base, set_size_in_bits, get_size_in_bits,
set_alignment_in_bits, get_alignment_in_bits}): Adjust.
({type_decl, scope_type_decl, qualified_type_def,
pointer_type_def, reference_type_def, array_type_def,
enum_type_decl, typedef_decl, function_type,
class_decl}::operator==): If the types being compared have
canonical type then use them for comparison.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
Profiling showed that we were walking the diff tree to apply
suppressions even when there were no suppressions to apply. This
patch does away with that behaviour.
* src/abg-comparison.cc (apply_suppressions): Do not walk the diff
tree to apply suppressions when there are no suppressions to
apply.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>