public inbox for libabigail@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 0/4, applied] Speed up type DIEs canonicalization
@ 2022-09-20 10:50 Dodji Seketeli
  2022-09-20 11:27 ` [PATCH 1/4, applied] dwarf-reader: Revamp the canonical type DIE propagation algorithm Dodji Seketeli
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Dodji Seketeli @ 2022-09-20 10:50 UTC (permalink / raw)
  To: libabigail; +Cc: dodji

Hello,

This patch set is a response to the problem entitled "abidw
performance regression on vmlinux", reported at
https://sourceware.org/bugzilla/show_bug.cgi?id=29464.

There was a first version of this patch sent to
https://inbox.sourceware.org/libabigail/87o7vsizdl.fsf@seketeli.org/T/#t.

This patch addresses the comments that were generously submitted in
the problem report.

In that problem report, "abidw --noout vmlinux" would take more than
hour.

Before that performance regression, type DIEs canonicalization by the
DWARF reader was fast, because it was wrong, basically.  During
canonicalization, the reader was using a speed optimization called
"canonical type propagation".  I argue that the optimization was
beeing done too eagerly.  Meaning it was being done even in cases
where it should not have been, often leading to wrong (but very fast)
outcomes.

This patch was:

    commit 7ecef6361799326b99129a479b43b138f0b237ae
    Author: Dodji Seketeli <dodji@redhat.com>
    Date:   Mon Apr 4 15:35:48 2022 +0200

	Canonicalize DIEs w/o assuming ODR & handle typedefs transparently

That patch was mostly meant to affect only C++, Ada and Java binaries.
But then, the patch stopped the reader from doing canonical type
propagation on types that are not aggregates (pointers, typedefs
etc).  Doing type propagation on types that are not aggregate is wrong
because that can lead to cases where two "pointers to aggregate" could
be considered equal where in the end, the aggregates are different.
This can happen for instance on aggregates that are detected as redundant.

Please note that these terms (redundant, canonical type propagation,
etc) are defined in the introductory comments of the first patches
below.

Anyway, that patch restricted canonicalization and canonical type
propagation to aggregate types.  It also restricted the amount
canonical type propagation performed because there are cases where
these should not be performed.

I believe that these changes lead the many more type DIEs comparison
being performed, even for C programs.  On libraries where there tend
to be less deep type trees, comparison time didn't explode.  But on
some applications like the Linux Kernel with deep type trees
containing lots of redundant type the number of DIEs comparison
exploded.

The first patch of the series below tracks all the types that are
subject to the canonical type propagation optimization and tries to
apply that optimization to a maximum of types.  That makes comparisons
be as fast as possible as many types now have a canonical type.  But
whenever it detects that the optimization should not have taken place,
it cancels it on all the types where it happened.  This is like what
we already do when canonicalyzing IR types.

This brought comparison times of vmlinux below 5 minutes (from more
than hour).

The second patch restricts the analysis of DIEs to the interfaces that
have exported symbols.  Otherwise, several other kinds of interfaces
where being analyzed at the DIE level.  It was just later that only
the IR of the exported interfaces where "chosen" to be put into the
final ABI Corpus.  That lead to potentially "too many" DIEs being
analyzed in the first place, leading to many comparisons in the case
of huge applications like vmlinux.  I believe this brings the time to
under a minute or so when analyzing vmlinux.

That second patch thus introduces a new "--exported-interfaces-only"
option supported by the tools abidw, abidiff, abipkgdiff, kmidiff,
etc.  That option triggers the new mode where only exported interfaces
are analyzed at the DIE level.  Note that when looking at the Linux
Kernel, that option is enabled by default.

It also introduces a new "--allow-non-exported-interfaces" for those
tools.  Note that this option is enabled by default when looking at
any binary that is not the Linux Kernel.

The last two patches are additions to the initial series.

Third one fixes issues that were latent in the way we were caching the
result of some comparisons at the IR level.  These issues are
uncovered by the first patch.

The last patch is a return to canonicalizing typedefs in the IR.  I
tried to stop canonicalizing typedefs before to give a change to the
comparison engine to avoid detecting harmless type changes due to
typedef name changes.  It turned out not canonicalizing typedefs
causes more harm than good as it makes comparing and addressing them
(in the abixml writer for instance) much harder and slower.  So I am
reverting back to square one and I am relying on subsequent passes on
the final diff graph to mark diff nodes as being harmless, leaving
leeway to reporters to handle that information at reporting time.

I am applying the patch set to master as the discussion on the problem
was really fruitful.  Many thanks to Giuliano Procida for the feedback.

Dodji Seketeli (4):
  dwarf-reader: Revamp the canonical type DIE propagation algorithm
  Allow restricting analyzed decls to exported symbols
  Fix IR comparison result caching and canonical type propagation tracking
  ir, writer: Go back to canonicalizing typedefs in the IR

 doc/manuals/Makefile.am                       |     3 +-
 doc/manuals/abidiff.rst                       |    52 +
 doc/manuals/abidw.rst                         |    82 +-
 doc/manuals/abipkgdiff.rst                    |    51 +
 doc/manuals/kmidiff.rst                       |    52 +-
 doc/manuals/tools-use-libabigail.txt          |    16 +
 include/abg-fwd.h                             |     5 +
 include/abg-ir.h                              |    67 +-
 src/abg-dwarf-reader.cc                       |  1514 +-
 src/abg-ir-priv.h                             |    64 +-
 src/abg-ir.cc                                 |   552 +-
 src/abg-reader.cc                             |     3 +
 src/abg-writer.cc                             |   166 +-
 .../test-abidiff/test-PR18791-report0.txt     |   238 +-
 tests/data/test-annotate/libtest23.so.abi     |   724 +-
 .../test-annotate/libtest24-drop-fns-2.so.abi |   804 +-
 .../test-annotate/libtest24-drop-fns.so.abi   |   804 +-
 .../test-anonymous-members-0.o.abi            |    96 +-
 tests/data/test-annotate/test0.abi            |     4 +-
 tests/data/test-annotate/test1.abi            |    32 +-
 .../data/test-annotate/test13-pr18894.so.abi  |  2322 +-
 .../data/test-annotate/test14-pr18893.so.abi  | 18390 ++---
 .../data/test-annotate/test15-pr18892.so.abi  | 62714 ++++++++--------
 .../data/test-annotate/test17-pr19027.so.abi  | 46135 ++++++------
 ...st18-pr19037-libvtkRenderingLIC-6.1.so.abi | 18052 +++--
 ...19-pr19023-libtcmalloc_and_profiler.so.abi | 49442 ++++++------
 tests/data/test-annotate/test2.so.abi         |    38 +-
 ...st20-pr19025-libvtkParallelCore-6.1.so.abi | 30100 ++++----
 .../data/test-annotate/test21-pr19092.so.abi  |  7608 +-
 .../PR25058-liblttng-ctl-report-1.txt         |     7 +-
 .../test42-PR21296-clanggcc-report0.txt       |     6 +
 .../test31-pr18535-libstdc++-report-0.txt     |    31 +-
 .../test31-pr18535-libstdc++-report-1.txt     |    31 +-
 .../data/test-diff-filter/test41-report-0.txt |    30 +-
 ...x86_64--2.24.2-30.fc30.x86_64-report-0.txt |     2 +-
 .../PR24690/PR24690-report-0.txt              |     2 +-
 .../nss-3.23.0-1.0.fc23.x86_64-report-0.txt   |     6 +-
 ...l7.x86_64-0.12.8-1.el7.x86_64-report-2.txt |   113 +-
 ...bb-4.3-3.20141204.fc23.x86_64-report-0.txt |    47 +-
 ...bb-4.3-3.20141204.fc23.x86_64-report-1.txt |    13 +-
 .../PR22015-libboost_iostreams.so.abi         |  2858 +-
 .../test-read-dwarf/PR22122-libftdc.so.abi    | 16923 ++++-
 .../data/test-read-dwarf/PR25007-sdhci.ko.abi | 14620 ++--
 .../PR25042-libgdbm-clang-dwarf5.so.6.0.0.abi |   642 +-
 .../test-read-dwarf/PR26261/PR26261-exe.abi   |    10 +-
 .../test-read-dwarf/PR27700/test-PR27700.abi  |     2 +-
 tests/data/test-read-dwarf/libtest23.so.abi   |   626 +-
 .../libtest24-drop-fns-2.so.abi               |   702 +-
 .../test-read-dwarf/libtest24-drop-fns.so.abi |   702 +-
 .../data/test-read-dwarf/test-PR26568-1.o.abi |    16 +-
 .../data/test-read-dwarf/test-PR26568-2.o.abi |    22 +-
 .../test-read-dwarf/test-libaaudio.so.abi     |   238 +-
 .../test-read-dwarf/test-libandroid.so.abi    | 56274 +++++++-------
 tests/data/test-read-dwarf/test0.abi          |     2 +-
 tests/data/test-read-dwarf/test0.hash.abi     |     2 +-
 tests/data/test-read-dwarf/test1.abi          |    26 +-
 tests/data/test-read-dwarf/test1.hash.abi     |     6 +-
 .../test-read-dwarf/test10-pr18818-gcc.so.abi |  7990 +-
 .../test-read-dwarf/test11-pr18828.so.abi     | 23566 +++---
 .../test-read-dwarf/test12-pr18844.so.abi     | 33361 ++++----
 .../test-read-dwarf/test13-pr18894.so.abi     |  2090 +-
 .../test-read-dwarf/test14-pr18893.so.abi     | 15146 ++--
 .../test-read-dwarf/test15-pr18892.so.abi     | 27826 +++----
 .../test-read-dwarf/test16-pr18904.so.abi     | 41341 +++++-----
 .../test-read-dwarf/test17-pr19027.so.abi     | 24602 +++---
 ...st18-pr19037-libvtkRenderingLIC-6.1.so.abi | 14733 ++--
 ...19-pr19023-libtcmalloc_and_profiler.so.abi | 22933 +++---
 tests/data/test-read-dwarf/test2.so.abi       |    34 +-
 tests/data/test-read-dwarf/test2.so.hash.abi  |     4 +-
 ...st20-pr19025-libvtkParallelCore-6.1.so.abi | 21807 +++---
 .../test-read-dwarf/test21-pr19092.so.abi     |  6494 +-
 .../test22-pr19097-libstdc++.so.6.0.17.so.abi | 60456 +++++++--------
 .../test9-pr18818-clang.so.abi                |  5013 +-
 tests/data/test-read-write/test18.xml         |     2 +-
 .../test28-without-std-fns-ref.xml            |   860 +-
 .../test28-without-std-vars-ref.xml           |   780 +-
 tools/abidiff.cc                              |    12 +
 tools/abidw.cc                                |    15 +
 tools/abipkgdiff.cc                           |    21 +
 tools/kmidiff.cc                              |    12 +
 80 files changed, 326407 insertions(+), 316780 deletions(-)
 create mode 100644 doc/manuals/tools-use-libabigail.txt

-- 
2.37.2


-- 
		Dodji

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 1/4, applied] dwarf-reader: Revamp the canonical type DIE propagation algorithm
  2022-09-20 10:50 [PATCH 0/4, applied] Speed up type DIEs canonicalization Dodji Seketeli
@ 2022-09-20 11:27 ` Dodji Seketeli
  2022-09-20 11:29 ` [PATCH 2/4, applied] Allow restricting analyzed decls to exported symbols Dodji Seketeli
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Dodji Seketeli @ 2022-09-20 11:27 UTC (permalink / raw)
  To: libabigail; +Cc: dodji

[-- Attachment #1: Type: text/plain, Size: 12384 bytes --]

Hello,

During the type DIEs comparison for canonicalization we need to
perform the optimization called "canonical type propagation".  This is
very similar to what we do for type canonicalization at the IR level.
It's described in-extenso in abg-ir.cc, in the comment that starts
with "@defgroup OnTheFlyCanonicalization".

Basically during canonicalization, suppose we end-up comparing a
sub-type pair {Sl, Sr}.  Suppose Sr does already have a canonical
type.  Suppose also, that {Sl, Sr} ends up comparing equal.  We can
thus deduce that Sl would have the same canonical type as Sr.  So we
'propagate' the canonical type of Sr onto Sl.  Sl is said to have been
canonical-type-propagated from Sr.

Now, I need to introduce the concept of redundant type.

Consider an aggregate type T and its sub-types ST0, ST1 and ST2 which
looks like this:

	T
	+-- ST0
	|
	+-- ST1
	|    +
	|    |
	|    +-- T
	|
	+-- ST2

Notice how the sub-type ST1 itself has a sub-type T.

Now, consider the type T', similar to T, which looks like:

	T'
	+-- ST0'
	|
	+-- ST1'
	|    +
	|    |
	|    +-- T'
	|
	+-- ST2'
	|
	+-- ST'3

Now consider how libabigail compares those two types T and T'
member-wise, aka structurally:

		T                 T'
		+-- ST0           +-- ST0'
		|                 |
		+-- ST1           +-- ST1' <--- Let's call this point #0.
		|    +            |    +
		|    |            |    |
		|    +-- T        |    +-- T'  <--- Let's call this point #1.
		|                 |                 Note that the sub-types of
		+-- ST2           +-- ST2'          T and T' are not
		|                                   represented at this point
		+-- ST'3                            but they do exist!
						    Representing them would lead
						    to a never-ending graph, right?

The properties of T are compared against the properties of T', but to
complete that comparison, the sub-type ST0 is compared against ST0',
etc.  A comparison stack is thus maintained.  Each member of the stack
is the pair of sub-types being compared.  That content changes as
different sub-types are compared. Let's consider the content of the
stack when we reach the point #0 in the graph above. That stack
content would look like:

	[{T,T'} | {ST0, ST0'}]

If we keep going and reach the point #1, the content of the stack
becomes:

	[{T,T'} |{ST0, ST0'} | {T, T'}]

At this point, we see that {T,T'} appears twice in the stack. If we
keep going and explore again the sub-types of {T,T'}, an infinite loop
appears.  The pair {T,T'} is said to be redundant.

To avoid those infinite loops, redundancy detection is done by
compare_dies in abg-dwarf-reader.cc.  When compare_dies detects at #1 that
{T,T'} was already present in the stack then it returns "true" early,
as if in #1, T compares equal to T'.

But then what does that mean for the value of comparing {ST0,ST0'}?
The sub-type {T,T'} in #1 compared equal, so compare_dies assumes that
{ST0,ST0'} compares equal as well, right?  That is what libabigail
used to assume before the commit

	commit 7ecef6361799326b99129a479b43b138f0b237ae
	Author: Dodji Seketeli <dodji@redhat.com>
	Date:   Mon Apr 4 15:35:48 2022 +0200

	    Canonicalize DIEs w/o assuming ODR & handle typedefs transparently

Well, it turns out we need to compare the entire tree of sub-types of
{T,T'} until we reach {ST2, ST2'}.  At that point, compare_dies sees
that ST2' has a sub-type ST3' where ST2 has none.  So {ST2,ST2'}
compares /different/.  So {T,T'} compares different.  So back to #0,
because {ST1,ST2'} has {T,T'} as sub-type (or said differently,
{ST1,ST2'} depends on the redundant pair {T,T'}) then {ST1,ST1'}
compares different.

So {ST1,ST1'} compares different even though it were initially thought to
compare equal because compare_dies had to get out early in #1,
considering that {T,T'} compared equal at that point.

Now, there are two ways to operate when we reach #1:

1/ Avoid performing canonical type propagation as soon as we detect
that {T,T'} is redundant.  That avoidance lasts until we finish
comparing {ST2,ST2'}, that is, until the complete comparison of
{T,T'}.  That means hat comparing every single sub-type is almost
assured to be done structurally, rather than canonically.

2/ Speculate that {T,T'} compare equal, unless proved otherwise.  If
{T,T'} proves to be equal, then things are well and fast.  If they
prove different, then {ST0,ST0'} must be edited afterwards to cancel
the canonical type propagation that might have taken place.  In other
words, sub-types that depends on redundant types pairs must be tracked
until the comparison of those redundant type pairs is done.  If a
redundant type pair compares different then all the sub-types that
depend on it must be walked to have their propagated canonical types
erase as if no canonical type propagation ever took place.

The first approach is the one I initially put in place with the patch
referred to above.  It proved to be super slow.  Analyzing the kernel
was taking literally hours.  Oops.

This patch implements the second approach.  It's more involved than
the first approach.  But it brings the time down to around 2 minutes
on a reasonably fast machine.  This is still slower than what I would
like to see, but it's way better than what had with the first
approach.

A subsequent patch will bring the analysis time for the kernel further
down.  But in the mean time, this one is really needed, I think.

So the patch introduces a new type named offset_pairs_stack_type to
track the pairs of type DIEs being compared.  Each DIE is now
identified by a new offset_type type.  That type  contains the
traditional DIE offset using the Dwarf_Off type from elfutils, but it
also contains the source of that DIE.  The offset_pairs_stack_type
also tracks redundant type pairs and their dependant type pairs.

compare_dies is modified to return a value that is an instance of the
new enum comparison_result.  The results can be
COMPARISON_RESULT_EQUAL when a type pair compares equal,
COMPARISON_RESULT_DIFFERENT when a type pair compares different,
COMPARISON_RESULT_CYCLE_DETECTED when a redundant type is detected,
leading to a comparison cycle, and COMPARISON_RESULT_UNKNOWN when the
outcome of the comparison cannot be known because we are comparing a
pair that depends on a redundant pair.

A new function return_comparison_result is introduced.  It's intended
to be invoked right before returning from compare_dies.  It looks at
the actual comparison result and depending on the state of the stack
of type pairs being compared, handles the book keeping of redundant
types and their dependant types.  It also handles when to propagate
canonical types if necessary and when to cancel the canonical types
that might have been propagated.

The ABG_RETURN macro has been adapted to invoke
return_comparison_result before returning out from compare_dies.

	* src/abg-ir-priv.h (enum comparison_result): Define new enum.
	* src/abg-dwarf-reader.cc (type_comparison_result_to_be_cached)
	(maybe_cache_type_comparison_result)
	(get_cached_type_comparison_result)
	(maybe_get_cached_type_comparison_result)
	(maybe_propagate_canonical_type, propagate_canonical_type)
	(return_comparison_result): Define new static functions.
	(has_offset_pair, insert_offset_pair, erase_offset_pair)
	(have_offset_pair_in_common): Remove static functions.
	(read_context::die_comparison_visits_): Remove data member.  The
	concept supported by this data member is now replaced by caching
	the results of comparing aggregate types, especially those that
	are not yet canonicalized.  This essentially prevents the same
	aggregate type pair to be compared again and again.
	(read_context::{die_comparison_results_, compare_count_,
	canonical_propagated_count_, cancelled_propagation_count_}): New
	data members.
	(read_context::initialize): Initialize the new data members
	compare_count_, canonical_propagated_count_,
	cancelled_propagation_count_ of integral type.
	(read_context::{erase_canonical_die_offset}): New member
	functions.
	(struct offset_pairs_stack_type): Define new type.
	(die_offset): Remove.
	(is_canon_type_to_be_propagated_tag): Add union types to the set
	of types for which canonical type propagation might occur.
	(is_type_die_to_be_canonicalized): Add function types and array
	types to the types to be canonicalized.
	(ABG_RETURN): Change this macro to consider
	COMPARISON_RESULT_DIFFERENT rather  than the "false" boolean.
	Also, it uses the new return_comparison_result function.
	(ABG_RETURN_FALSE): Likewise, use the new return_comparison_result
	function.
	(SET_RESULT_TO_FALSE): Make this return
	COMPARISON_RESULT_DIFFERENT.
	(SET_RESULT_TO, RETURN_IF_COMPARISON_CYCLE_DETECTED): Define new
	macros.
	(compare_dies): Make this return comparison_result rather than
	just a bool.  This is also the core of the overhaul of the
	canonical DIE propagation algorithm.  The algorithm is now similar
	to the one implemented in the equals function for class_or_union
	types in abg-ir.cc.  It's described in the comment that starts
	with '@defgroup OnTheFlyCanonicalization' in abg-ir.cc.  The type
	of the aggregates_being_compared parameter is now
	offset_pairs_stack_type in parameter.  No more need for the
	redundant_aggregates_being_compared parameter.  The new
	offset_type that also encapsulates the source of the offset is now
	used in lieu of the Dwarf_Off type.  Results of comparing
	aggregates being compared are now cached.  When comparing
	aggregates, use the RETURN_IF_COMPARISON_CYCLE_DETECTED to return
	early if a cycle is detected.  The invocation of the ABG_RETURN
	macro (especially the call to return_comparison_result) is where
	the book keeping for canonical types propagation takes place, so
	remove the explicit code that was handling that from the end of
	this function.
	(read_debug_info_into_corpus): Print statistics about the number
	of aggregates compared and canonical-type-propagated.
	* tests/data/test-annotate/test14-pr18893.so.abi: Adjust.
	* tests/data/test-annotate/test15-pr18892.so.abi: Likewise.
	* tests/data/test-annotate/test17-pr19027.so.abi: Likewise.
	* tests/data/test-annotate/test19-pr19023-libtcmalloc_and_profiler.so.abi:
	Likewise.
	* tests/data/test-annotate/test20-pr19025-libvtkParallelCore-6.1.so.abi:
	Likewise.
	* tests/data/test-diff-pkg/GtkAda-gl-2.24.2-29.fc29.x86_64--2.24.2-30.fc30.x86_64-report-0.txt:
	Likewise.
	* tests/data/test-read-dwarf/PR22122-libftdc.so.abi: Likewise.
	* tests/data/test-read-dwarf/test-libandroid.so.abi: Likewise.
	* tests/data/test-read-dwarf/test14-pr18893.so.abi: Likewise.
	* tests/data/test-read-dwarf/test15-pr18892.so.abi: Likewise.
	* tests/data/test-read-dwarf/test16-pr18904.so.abi: Likewise.
	* tests/data/test-read-dwarf/test17-pr19027.so.abi: Likewise.
	* tests/data/test-read-dwarf/test19-pr19023-libtcmalloc_and_profiler.so.abi:
	Likewise.
	* tests/data/test-read-dwarf/test20-pr19025-libvtkParallelCore-6.1.so.abi:
	Likewise.
	* tests/data/test-read-dwarf/test22-pr19097-libstdc++.so.6.0.17.so.abi:
	Likewise.
	* tests/data/test-read-dwarf/test9-pr18818-clang.so.abi: Likewise.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
---
 src/abg-dwarf-reader.cc                       |  1377 +-
 src/abg-ir-priv.h                             |     9 +
 .../data/test-annotate/test14-pr18893.so.abi  |    52 +-
 .../data/test-annotate/test15-pr18892.so.abi  | 11386 ++++++++--------
 .../data/test-annotate/test17-pr19027.so.abi  |  1224 +-
 ...19-pr19023-libtcmalloc_and_profiler.so.abi | 11323 +++++++--------
 ...st20-pr19025-libvtkParallelCore-6.1.so.abi |  7194 +++++-----
 ...x86_64--2.24.2-30.fc30.x86_64-report-0.txt |     2 +-
 .../test-read-dwarf/PR22122-libftdc.so.abi    |   893 +-
 .../test-read-dwarf/test-libandroid.so.abi    |     8 +
 .../test-read-dwarf/test14-pr18893.so.abi     |    38 +-
 .../test-read-dwarf/test15-pr18892.so.abi     | 10901 ++++++++-------
 .../test-read-dwarf/test16-pr18904.so.abi     |   676 +-
 .../test-read-dwarf/test17-pr19027.so.abi     |  1216 +-
 ...19-pr19023-libtcmalloc_and_profiler.so.abi | 11153 +++++++--------
 ...st20-pr19025-libvtkParallelCore-6.1.so.abi |  7164 +++++-----
 .../test22-pr19097-libstdc++.so.6.0.17.so.abi |    32 +-
 .../test9-pr18818-clang.so.abi                |   127 +-
 18 files changed, 33405 insertions(+), 31370 deletions(-)

The patch is too big for the list so I am attaching it gzipped.


[-- Attachment #2: 0001-dwarf-reader-Revamp-the-canonical-type-DIE-propagati.patch.gz --]
[-- Type: application/gzip, Size: 815094 bytes --]

[-- Attachment #3: Type: text/plain, Size: 14 bytes --]



-- 
		Dodji

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 2/4, applied] Allow restricting analyzed decls to exported symbols
  2022-09-20 10:50 [PATCH 0/4, applied] Speed up type DIEs canonicalization Dodji Seketeli
  2022-09-20 11:27 ` [PATCH 1/4, applied] dwarf-reader: Revamp the canonical type DIE propagation algorithm Dodji Seketeli
@ 2022-09-20 11:29 ` Dodji Seketeli
  2022-09-20 11:30 ` [PATCH 3/4] Fix IR comparison result caching and canonical type propagation tracking Dodji Seketeli
  2022-09-20 11:37 ` [PATCH 4/4, applied] ir, writer: Go back to canonicalizing typedefs in the IR Dodji Seketeli
  3 siblings, 0 replies; 5+ messages in thread
From: Dodji Seketeli @ 2022-09-20 11:29 UTC (permalink / raw)
  To: libabigail; +Cc: dodji

Hello,

Profiling showed that the DWARF reader scans too much data.

Basically, in build_translation_unit_and_add_to_ir,
build_ir_node_from_die is called on every single DIE that is seen, for
a given translation unit.

There are interfaces (function and variable decls) that are not
associated with exported ELF symbols and that are analyzed by
build_ir_node_from_die nonetheless.  For instance, interfaces that are
visible outside of their translation units are analyzed and the types
that are reachable from those interfaces are analyzed as well.

Once that is done, an ABI corpus is built with the subset of
interfaces that have exported ELF symbol (strictly those that are part
of the ABI), but types that are not necessarily reachable from those
ABI interfaces can also be put into the ABI corpus.

Some tools make use of this "lose" behaviour of libabigail.  For
instance, abicompat precisely wants to analyze interfaces with
undefined symbols.  For an application, those interfaces represents
the interfaces that the application expects to be provided by some
shared library.

When analyzing the exported interface of the Linux Kernel (or any
other huge application) however, analyzing more types than necessary
appears to incur a huge time penalty.

So, this patch introduces an optional behaviour whereby
build_translation_unit_and_add_to_ir is restricted to analyzing
interfaces that have exported ELF symbols only.  So only the types
reachable from those interfaces are analyzed.  This more than halves
the time spent by "abidw --noout vmlinux".

Strictly speaking, this new behaviour is triggered by a new option named
--exported-interfaces-only, supported by the tools abidw, abidiff,
abipkgdiff and kmidiff.

When looking at the Linux Kernel however, this option is enabled by
default.

Note that an option --allow-non-exported-interfaces is also introduce
to function under the previous model of operations.  This option is
enabled by default on all the tools when they are not looking at the
Linux Kernel.

With this enabled, analyzing the Linux Kernel is back to taking less
than a minute on a reasonable machine.

	* doc/manuals/tools-use-libabigail.txt: New doc text.
	* doc/manuals/Makefile.am: Add the new tools-use-libabigail.rst
	tool to the source distribution.
	* doc/manuals/abidiff.rst: Include the new
	tools-use-libabigail.rst.  Document the --exported-interfaces-only
	and --allow-non-exported-interfaces.
	* doc/manuals/abidw.rst: Likewise.
	* doc/manuals/abipkgdiff.rst: Likewise.
	* doc/manuals/kmidiff.rst: Likewise.
	* include/abg-ir.h
	(environment::{user_set_analyze_exported_interfaces_only,
	analyze_exported_interfaces_only}): Declare new accessors.
	* src/abg-ir.cc
	(environment::{user_set_analyze_exported_interfaces_only,
	analyze_exported_interfaces_only}): Define new accessors.
	* src/abg-dwarf-reader.cc (die_is_variable_decl)
	(die_is_function_decl): Define new static functions.
	(read_context::is_decl_die_with_exported_symbol): Define new
	member function.
	(read_context::get_{function,variable}_address): Const-ify the
	Dwarf_Die* parameter.
	(build_translation_unit_and_add_to_ir): If the user asks to
	analyze exported interfaces only,  the analyze only interfaces
	that have exported ELF symbols.
	(read_debug_info_into_corpus): If we are looking at the Linux
	Kernel, then only analyze exported interfaces unless the user asks
	otherwise.
	* src/abg-ir-priv.h
	(environment::priv::analyze_exported_interfaces_only_): Define new
	data member.
	* tools/abidiff.cc (options::exported_interfaces_only): Define new
	data member.
	(display_usage): Add new help strings for
	--exported-interfaces-only and --allow-non-exported-interfaces.
	(parse_command_line): Parse the new options
	--exported-interfaces-only and --allow-non-exported-interfaces.
	(main): Pass the value of opts.exported_interfaces_only to the
	environment.
	* tools/abidw.cc (options::exported_interfaces_only): Define new
	data member.
	(display_usage): Add new help strings for
	--exported-interfaces-only and --allow-non-exported-interfaces.
	(parse_command_line): Parse the new options
	(load_corpus_and_write_abixml)
	(load_kernel_corpus_group_and_write_abixml): Pass the value of
	opts.exported_interfaces_only onto the environment.
	* tools/abipkgdiff.cc (options::exported_interfaces_only): Define new
	data member.
	(display_usage): Add new help strings for
	--exported-interfaces-only and --allow-non-exported-interfaces.
	(parse_command_line): Parse the new options
	(compare_task::perform, self_compare_task::perform): Pass the
	value of opts.exported_interfaces_only onto the environment.
	(compare_prepared_linux_kernel_packages): Likewise.
	* tools/kmidiff.cc(options::exported_interfaces_only): Define new
	data member.
	(display_usage): Add new help strings for
	--exported-interfaces-only and --allow-non-exported-interfaces.
	(parse_command_line): Parse the new options
	(main): Pass the value of opts.exported_interfaces_only onto the
	environment.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
---
 doc/manuals/Makefile.am              |   3 +-
 doc/manuals/abidiff.rst              |  52 ++++++++++++
 doc/manuals/abidw.rst                |  82 ++++++++++++++++---
 doc/manuals/abipkgdiff.rst           |  51 ++++++++++++
 doc/manuals/kmidiff.rst              |  52 +++++++++++-
 doc/manuals/tools-use-libabigail.txt |  16 ++++
 include/abg-ir.h                     |   9 +++
 src/abg-dwarf-reader.cc              | 113 ++++++++++++++++++++++++---
 src/abg-ir-priv.h                    |   2 +
 src/abg-ir.cc                        |  36 +++++++++
 tools/abidiff.cc                     |  12 +++
 tools/abidw.cc                       |  15 ++++
 tools/abipkgdiff.cc                  |  21 +++++
 tools/kmidiff.cc                     |  12 +++
 14 files changed, 449 insertions(+), 27 deletions(-)
 create mode 100644 doc/manuals/tools-use-libabigail.txt

diff --git a/doc/manuals/Makefile.am b/doc/manuals/Makefile.am
index 894b38f1..e2813785 100644
--- a/doc/manuals/Makefile.am
+++ b/doc/manuals/Makefile.am
@@ -14,7 +14,8 @@ libabigail-concepts.rst \
 libabigail-overview.rst \
 libabigail-tools.rst \
 fedabipkgdiff.rst \
-kmidiff.rst
+kmidiff.rst \
+tools-use-libabigail.txt
 
 # You can set these variables from the command line.
 SPHINXOPTS    =
diff --git a/doc/manuals/abidiff.rst b/doc/manuals/abidiff.rst
index a15515be..0c711d9e 100644
--- a/doc/manuals/abidiff.rst
+++ b/doc/manuals/abidiff.rst
@@ -18,6 +18,8 @@ be accompanied with their debug information in `DWARF`_ format.
 Otherwise, only `ELF`_ symbols that were added or removed are
 reported.
 
+.. include:: tools-use-libabigail.txt
+
 .. _abidiff_invocation_label:
 
 Invocation
@@ -197,6 +199,56 @@ Options
     consumption of the tool on binaries with a lot of publicly defined
     and exported types.
 
+  * ``--exported-interfaces-only``
+
+    By default, when looking at the debug information accompanying a
+    binary, this tool analyzes the descriptions of the types reachable
+    by the interfaces (functions and variables) that are visible
+    outside of their translation unit.  Once that analysis is done, an
+    ABI corpus is constructed by only considering the subset of types
+    reachable from interfaces associated to `ELF`_ symbols that are
+    defined and exported by the binary.  It's those final ABI Corpora
+    that are compared by this tool.
+
+    The problem with that approach however is that analyzing all the
+    interfaces that are visible from outside their translation unit
+    can amount to a lot of data, especially when those binaries are
+    applications, as opposed to shared libraries.  One example of such
+    applications is the `Linux Kernel`_.  Analyzing massive ABI
+    corpora like these can be extremely slow.
+
+    To mitigate that performance issue, this option allows libabigail
+    to only analyze types that are reachable from interfaces
+    associated with defined and exported `ELF`_ symbols.
+
+    Note that this option is turned on by default when analyzing the
+    `Linux Kernel`_.  Otherwise, it's turned off by default.
+
+  * ``--allow-non-exported-interfaces``
+
+    When looking at the debug information accompanying a binary, this
+    tool analyzes the descriptions of the types reachable by the
+    interfaces (functions and variables) that are visible outside of
+    their translation unit.  Once that analysis is done, an ABI corpus
+    is constructed by only considering the subset of types reachable
+    from interfaces associated to `ELF`_ symbols that are defined and
+    exported by the binary.  It's those final ABI Corpora that are
+    compared by this tool.
+
+    The problem with that approach however is that analyzing all the
+    interfaces that are visible from outside their translation unit
+    can amount to a lot of data, especially when those binaries are
+    applications, as opposed to shared libraries.  One example of such
+    applications is the `Linux Kernel`_.  Analyzing massive ABI
+    Corpora like these can be extremely slow.
+
+    In the presence of an "average sized" binary however one can
+    afford having libabigail analyze all interfaces that are visible
+    outside of their translation unit, using this option.
+
+    Note that this option is turned on by default, unless we are in
+    the presence of the `Linux Kernel`_.
+
   * ``--stat``
 
     Rather than displaying the detailed ABI differences between
diff --git a/doc/manuals/abidw.rst b/doc/manuals/abidw.rst
index bdd6204d..a3055c7e 100644
--- a/doc/manuals/abidw.rst
+++ b/doc/manuals/abidw.rst
@@ -12,14 +12,19 @@ defined ELF symbols of the file.  The input shared library must
 contain associated debug information in `DWARF`_ format.
 
 When given the ``--linux-tree`` option, this program can also handle a
-Linux kernel tree.  That is, a directory tree that contains both the
-vmlinux binary and Linux kernel modules.  It analyses those Linux
-kernel binaries and emits an XML representation of the interface
-between the kernel and its module, to standard output.  In this case,
-we don't call it an ABI, but a KMI (Kernel Module Interface).  The
-emitted KMI includes all the globally defined functions and variables,
-along with a complete representation of their types.  The input
-binaries must contain associated debug information in `DWARF`_ format.
+`Linux kernel`_ tree.  That is, a directory tree that contains both
+the vmlinux binary and `Linux Kernel`_ modules.  It analyses those
+`Linux Kernel`_ binaries and emits an XML representation of the
+interface between the kernel and its module, to standard output.  In
+this case, we don't call it an ABI, but a KMI (Kernel Module
+Interface).  The emitted KMI includes all the globally defined
+functions and variables, along with a complete representation of their
+types.  The input binaries must contain associated debug information
+in `DWARF`_ format.
+
+.. include:: tools-use-libabigail.txt
+
+.. _abidiff_invocation_label:
 
 Invocation
 ==========
@@ -92,7 +97,7 @@ Options
 
   * ``--kmi-whitelist | -kaw`` <*path-to-whitelist*>
 
-    When analyzing a Linux kernel binary, this option points to the
+    When analyzing a `Linux Kernel`_ binary, this option points to the
     white list of names of ELF symbols of functions and variables
     which ABI must be written out.  That white list is called a "
     Kernel Module Interface white list".  This is because for the
@@ -105,7 +110,7 @@ Options
 
     If this option is not provided -- thus if no white list is
     provided -- then the entire KMI, that is, all publicly defined and
-    exported functions and global variables by the Linux Kernel
+    exported functions and global variables by the `Linux Kernel`_
     binaries is emitted.
     
   * ``--linux-tree | --lt``
@@ -115,9 +120,10 @@ Options
     In that case, this program emits the representation of the Kernel
     Module Interface (KMI) on the standard output.
 
-    Below is an example of usage of ``abidw`` on a Linux Kernel tree.
+    Below is an example of usage of ``abidw`` on a `Linux Kernel`_
+    tree.
 
-    First, checkout a Linux kernel source tree and build it.  Then
+    First, checkout a `Linux Kernel`_ source tree and build it.  Then
     install the kernel modules in a directory somewhere.  Copy the
     vmlinux binary into that directory too.  And then serialize the
     KMI of that kernel to disk, using ``abidw``: ::
@@ -171,6 +177,56 @@ Options
     representation build by Libabigail to represent the ABI and will
     not end up in the abi XML file.
 
+  * ``--exported-interfaces-only``
+
+    By default, when looking at the debug information accompanying a
+    binary, this tool analyzes the descriptions of the types reachable
+    by the interfaces (functions and variables) that are visible
+    outside of their translation unit.  Once that analysis is done, an
+    ABI corpus is constructed by only considering the subset of types
+    reachable from interfaces associated to `ELF`_ symbols that are
+    defined and exported by the binary.  It's that final ABI corpus
+    which textual representation is saved as ``ABIXML``.
+
+    The problem with that approach however is that analyzing all the
+    interfaces that are visible from outside their translation unit
+    can amount to a lot of data, especially when those binaries are
+    applications, as opposed to shared libraries.  One example of such
+    applications is the `Linux Kernel`_.  Analyzing massive ABI
+    corpora like these can be extremely slow.
+
+    To mitigate that performance issue, this option allows libabigail
+    to only analyze types that are reachable from interfaces
+    associated with defined and exported `ELF`_ symbols.
+
+    Note that this option is turned on by default when analyzing the
+    `Linux Kernel`_.  Otherwise, it's turned off by default.
+
+  * ``--allow-non-exported-interfaces``
+
+    When looking at the debug information accompanying a binary, this
+    tool analyzes the descriptions of the types reachable by the
+    interfaces (functions and variables) that are visible outside of
+    their translation unit.  Once that analysis is done, an ABI corpus
+    is constructed by only considering the subset of types reachable
+    from interfaces associated to `ELF`_ symbols that are defined and
+    exported by the binary.  It's that final ABI corpus which textual
+    representation is saved as ``ABIXML``.
+
+    The problem with that approach however is that analyzing all the
+    interfaces that are visible from outside their translation unit
+    can amount to a lot of data, especially when those binaries are
+    applications, as opposed to shared libraries.  One example of such
+    applications is the `Linux Kernel`_.  Analyzing massive ABI
+    corpora like these can be extremely slow.
+
+    In the presence of an "average sized" binary however one can
+    afford having libabigail analyze all interfaces that are visible
+    outside of their translation unit, using this option.
+
+    Note that this option is turned on by default, unless we are in
+    the presence of the `Linux Kernel`_.
+
   * ``--no-linux-kernel-mode``
 
     Without this option, if abipkgiff detects that the binaries it is
@@ -308,4 +364,4 @@ standard `here
 .. _ELF: http://en.wikipedia.org/wiki/Executable_and_Linkable_Format
 .. _DWARF: http://www.dwarfstd.org
 .. _GNU: http://www.gnu.org
-
+.. _Linux Kernel: https://kernel.org/
diff --git a/doc/manuals/abipkgdiff.rst b/doc/manuals/abipkgdiff.rst
index 15ea9072..9114775a 100644
--- a/doc/manuals/abipkgdiff.rst
+++ b/doc/manuals/abipkgdiff.rst
@@ -19,6 +19,7 @@ information directly in a section of said binaries.  In those cases,
 obviously, no separate debug information package is needed as the tool
 will find the debug information inside the binaries.
 
+.. include:: tools-use-libabigail.txt
 
 .. _abipkgdiff_invocation_label:
 
@@ -277,6 +278,56 @@ Options
     global functions and variables are analyzed, so the tool detects
     and reports changes on these reachable types only.
 
+  * ``--exported-interfaces-only``
+
+    By default, when looking at the debug information accompanying a
+    binary, this tool analyzes the descriptions of the types reachable
+    by the interfaces (functions and variables) that are visible
+    outside of their translation unit.  Once that analysis is done, an
+    ABI corpus is constructed by only considering the subset of types
+    reachable from interfaces associated to `ELF`_ symbols that are
+    defined and exported by the binary.  It's those final ABI Corpora
+    that are compared by this tool.
+
+    The problem with that approach however is that analyzing all the
+    interfaces that are visible from outside their translation unit
+    can amount to a lot of data, especially when those binaries are
+    applications, as opposed to shared libraries.  One example of such
+    applications is the `Linux Kernel`_.  Analyzing massive ABI
+    corpora like these can be extremely slow.
+
+    To mitigate that performance issue, this option allows libabigail
+    to only analyze types that are reachable from interfaces
+    associated with defined and exported `ELF`_ symbols.
+
+    Note that this option is turned on by default when analyzing the
+    `Linux Kernel`_.  Otherwise, it's turned off by default.
+
+  * ``--allow-non-exported-interfaces``
+
+    When looking at the debug information accompanying a binary, this
+    tool analyzes the descriptions of the types reachable by the
+    interfaces (functions and variables) that are visible outside of
+    their translation unit.  Once that analysis is done, an ABI corpus
+    is constructed by only considering the subset of types reachable
+    from interfaces associated to `ELF`_ symbols that are defined and
+    exported by the binary.  It's those final ABI Corpora that are
+    compared by this tool.
+
+    The problem with that approach however is that analyzing all the
+    interfaces that are visible from outside their translation unit
+    can amount to a lot of data, especially when those binaries are
+    applications, as opposed to shared libraries.  One example of such
+    applications is the `Linux Kernel`_.  Analyzing massive ABI
+    Corpora like these can be extremely slow.
+
+    In the presence of an "average sized" binary however one can
+    afford having libabigail analyze all interfaces that are visible
+    outside of their translation unit, using this option.
+
+    Note that this option is turned on by default, unless we are in
+    the presence of the `Linux Kernel`_.
+
   *  ``--redundant``
 
     In the diff reports, do display redundant changes.  A redundant
diff --git a/doc/manuals/kmidiff.rst b/doc/manuals/kmidiff.rst
index ce8168ae..53010189 100644
--- a/doc/manuals/kmidiff.rst
+++ b/doc/manuals/kmidiff.rst
@@ -55,6 +55,10 @@ command line looks like: ::
 		       linux/v4.5/build/modules \
 		       linux/v4.6/build/modules
 
+
+.. include:: tools-use-libabigail.txt
+
+
 Invocation
 ==========
 
@@ -67,8 +71,8 @@ Environment
 
 By default, ``kmidiff`` compares all the interfaces (exported
 functions and variables) between the Kernel and its modules.  In
-practice, though, users want to compare a subset of the those
-interfaces.
+practice, though, some users might want to compare a subset of the
+those interfaces.
 
 Users can then define a "white list" of the interfaces to compare.
 Such a white list is a just a file in the "INI" format that looks
@@ -91,8 +95,11 @@ function or variable.  Only those interfaces along with the types
 reachable from their signatures are going to be compared by
 ``kmidiff`` recursively.
 
-Note that kmidiff compares the interfaces exported by the ``vmlinux``
-binary and by the all of the compiled modules.
+Note that by default kmidiff analyzes the types reachable from the
+interfaces associated with `ELF`_ symbols that are defined and
+exported by the `Linux Kernel`_ as being the union of the ``vmlinux``
+binary and all its compiled modules.  It then compares those
+interfaces (along with their types).
 
 Options
 =======
@@ -180,6 +187,38 @@ Options
     exported interfaces.  This is the default kind of report emitted
     by tools like ``abidiff`` or ``abipkgdiff``.
 
+  * ``--exported-interfaces-only``
+
+    When using this option, this tool analyzes the descriptions of the
+    types reachable by the interfaces (functions and variables)
+    associated with `ELF`_ symbols that are defined and exported by
+    the `Linux Kernel`_.
+
+    Otherwise, the tool also has the ability to analyze the
+    descriptions of the types reachable by the interfaces associated
+    with `ELF`_ symbols that are visible outside their translation
+    unit.  This later possibility is however much more resource
+    intensive and results in much slower operations.
+
+    That is why this option is enabled by default.
+
+
+  * ``--allow-non-exported-interfaces``
+
+    When using this option, this tool analyzes the descriptions of the
+    types reachable by the interfaces (functions and variables) that
+    are visible outside of their translation unit.  Once that analysis
+    is done, an ABI Corpus is constructed by only considering the
+    subset of types reachable from interfaces associated to `ELF`_
+    symbols that are defined and exported by the binary.  It's that
+    final ABI corpus which is compared against another one.
+
+    The problem with that approach however is that analyzing all the
+    interfaces that are visible from outside their translation unit
+    can amount to a lot of data, leading to very slow operations.
+
+    Note that this option is turned off by default.
+
   * ``--show-bytes``
 
     Show sizes and offsets in bytes, not bits.  This option is
@@ -198,3 +237,8 @@ Options
   * ``--show-dec``
 
     Show sizes and offsets in decimal base.
+
+
+.. _ELF: http://en.wikipedia.org/wiki/Executable_and_Linkable_Format
+.. _ksymtab: http://en.wikipedia.org/wiki/Executable_and_Linkable_Format
+.. _Linux Kernel: https://kernel.org
diff --git a/doc/manuals/tools-use-libabigail.txt b/doc/manuals/tools-use-libabigail.txt
new file mode 100644
index 00000000..43edf296
--- /dev/null
+++ b/doc/manuals/tools-use-libabigail.txt
@@ -0,0 +1,16 @@
+This tool uses the libabigail library to analyze the binary as well as its
+associated debug information.  Here is its general mode of operation.
+
+When instructed to do so, a binary and its associated debug
+information is read and analyzed.  To that effect, libabigail analyzes
+by default the descriptions of the types reachable by the interfaces
+(functions and variables) that are visible outside of their
+translation unit.  Once that analysis is done, an Application Binary
+Interface Corpus is constructed by only considering the subset of
+types reachable from interfaces associated to `ELF`_ symbols that are
+defined and exported by the binary.  It's that final ABI corpus which
+libabigail considers as representing the ABI of the analyzed binary.
+
+Libabigail then has capabilities to generate textual representations
+of ABI Corpora, compare them, analyze their changes and report about
+them.
diff --git a/include/abg-ir.h b/include/abg-ir.h
index a857d041..61338edb 100644
--- a/include/abg-ir.h
+++ b/include/abg-ir.h
@@ -197,6 +197,15 @@ public:
   const config&
   get_config() const;
 
+  bool
+  user_set_analyze_exported_interfaces_only() const;
+
+  void
+  analyze_exported_interfaces_only(bool f);
+
+  bool
+  analyze_exported_interfaces_only() const;
+
 #ifdef WITH_DEBUG_SELF_COMPARISON
   void
   set_self_comparison_debug_input(const corpus_sptr& corpus);
diff --git a/src/abg-dwarf-reader.cc b/src/abg-dwarf-reader.cc
index f1b1cf55..09e0704a 100644
--- a/src/abg-dwarf-reader.cc
+++ b/src/abg-dwarf-reader.cc
@@ -402,6 +402,12 @@ die_is_decl(const Dwarf_Die* die);
 static bool
 die_is_declaration_only(Dwarf_Die* die);
 
+static bool
+die_is_variable_decl(const Dwarf_Die *die);
+
+static bool
+die_is_function_decl(const Dwarf_Die *die);
+
 static bool
 die_has_size_attribute(const Dwarf_Die *die);
 
@@ -5303,6 +5309,44 @@ public:
     return symbol;
   }
 
+  /// Test if a DIE represents a decl (function or variable) that has
+  /// a symbol that is exported, whatever that means.  This is
+  /// supposed to work for Linux Kernel binaries as well.
+  ///
+  /// This is useful to limit the amount of DIEs taken into account to
+  /// the strict limit of what an ABI actually means.  Limiting the
+  /// volume of DIEs analyzed this way is an important optimization to
+  /// keep big binaries "manageable" by libabigail.
+  ///
+  /// @param DIE the die to consider.
+  bool
+  is_decl_die_with_exported_symbol(const Dwarf_Die *die)
+  {
+    if (!die || !die_is_decl(die))
+      return false;
+
+    bool result = false, address_found = false, symbol_is_exported = false;;
+    Dwarf_Addr decl_symbol_address = 0;
+
+    if (die_is_variable_decl(die))
+      {
+	if ((address_found = get_variable_address(die, decl_symbol_address)))
+	  symbol_is_exported =
+	    !!variable_symbol_is_exported(decl_symbol_address);
+      }
+    else if (die_is_function_decl(die))
+      {
+	if ((address_found = get_function_address(die, decl_symbol_address)))
+	  symbol_is_exported =
+	    !!function_symbol_is_exported(decl_symbol_address);
+      }
+
+    if (address_found)
+      result = symbol_is_exported;
+
+    return result;
+  }
+
   /// Getter for the symtab reader. Will load the symtab from the elf handle if
   /// not yet set.
   ///
@@ -5580,16 +5624,18 @@ public:
   ///
   /// @return true if the function address was found.
   bool
-  get_function_address(Dwarf_Die* function_die, Dwarf_Addr& address) const
+  get_function_address(const Dwarf_Die* function_die, Dwarf_Addr& address) const
   {
-    if (!die_address_attribute(function_die, DW_AT_low_pc, address))
+    if (!die_address_attribute(const_cast<Dwarf_Die*>(function_die),
+			       DW_AT_low_pc, address))
       // So no DW_AT_low_pc was found.  Let's see if the function DIE
       // has got a DW_AT_ranges attribute instead.  If it does, the
       // first address of the set of addresses represented by the
       // value of that DW_AT_ranges represents the function (symbol)
       // address we are looking for.
-      if (!get_first_exported_fn_address_from_DW_AT_ranges(function_die,
-							   address))
+      if (!get_first_exported_fn_address_from_DW_AT_ranges
+	  (const_cast<Dwarf_Die*>(function_die),
+	   address))
 	return false;
 
     address = maybe_adjust_fn_sym_address(address);
@@ -5611,11 +5657,12 @@ public:
   ///
   /// @return true if the variable address was found.
   bool
-  get_variable_address(Dwarf_Die*	variable_die,
+  get_variable_address(const Dwarf_Die* variable_die,
 		       Dwarf_Addr&	address) const
   {
     bool is_tls_address = false;
-    if (!die_location_address(variable_die, address, is_tls_address))
+    if (!die_location_address(const_cast<Dwarf_Die*>(variable_die),
+			      address, is_tls_address))
       return false;
     if (!is_tls_address)
       address = maybe_adjust_var_sym_address(address);
@@ -7155,6 +7202,40 @@ die_is_declaration_only(Dwarf_Die* die)
   return false;
 }
 
+/// Test if a DIE is for a function decl.
+///
+/// @param die the DIE to consider.
+///
+/// @return true iff @p die represents a function decl.
+static bool
+die_is_function_decl(const Dwarf_Die *die)
+{
+  if (!die)
+    return false;
+
+  int tag = dwarf_tag(const_cast<Dwarf_Die*>(die));
+  if (tag == DW_TAG_subprogram)
+    return true;
+  return false;
+}
+
+/// Test if a DIE is for a variable decl.
+///
+/// @param die the DIE to consider.
+///
+/// @return true iff @p die represents a variable decl.
+static bool
+die_is_variable_decl(const Dwarf_Die *die)
+{
+    if (!die)
+    return false;
+
+  int tag = dwarf_tag(const_cast<Dwarf_Die*>(die));
+  if (tag == DW_TAG_variable)
+    return true;
+  return false;
+}
+
 /// Test if a DIE has size attribute.
 ///
 /// @param die the DIE to consider.
@@ -12696,9 +12777,13 @@ build_translation_unit_and_add_to_ir(read_context&	ctxt,
   result->set_is_constructed(false);
 
   do
-    build_ir_node_from_die(ctxt, &child,
-			   die_is_public_decl(&child),
-			   dwarf_dieoffset(&child));
+    // Analyze all the DIEs we encounter unless we are asked to only
+    // analyze exported interfaces and the types reachables from them.
+    if (!ctxt.env()->analyze_exported_interfaces_only()
+	|| ctxt.is_decl_die_with_exported_symbol(&child))
+      build_ir_node_from_die(ctxt, &child,
+			     die_is_public_decl(&child),
+			     dwarf_dieoffset(&child));
   while (dwarf_siblingof(&child, &child) == 0);
 
   if (!ctxt.var_decls_to_re_add_to_tree().empty())
@@ -15705,6 +15790,16 @@ read_debug_info_into_corpus(read_context& ctxt)
     origin |= corpus::LINUX_KERNEL_BINARY_ORIGIN;
   ctxt.current_corpus()->set_origin(origin);
 
+  if (origin & corpus::LINUX_KERNEL_BINARY_ORIGIN
+      && !ctxt.env()->user_set_analyze_exported_interfaces_only())
+    // So we are looking at the Linux Kernel and the user has not set
+    // any particular option regarding the amount of types to analyse.
+    // In that case, we need to only analyze types that are reachable
+    // from exported interfaces otherwise we get such a massive amount
+    // of type DIEs to look at that things are just too slow down the
+    // road.
+    ctxt.env()->analyze_exported_interfaces_only(true);
+
   ctxt.current_corpus()->set_soname(ctxt.dt_soname());
   ctxt.current_corpus()->set_needed(ctxt.dt_needed());
   ctxt.current_corpus()->set_architecture_name(ctxt.elf_architecture());
diff --git a/src/abg-ir-priv.h b/src/abg-ir-priv.h
index 45b711b7..21734b25 100644
--- a/src/abg-ir-priv.h
+++ b/src/abg-ir-priv.h
@@ -26,6 +26,7 @@ namespace ir
 {
 
 using std::string;
+using abg_compat::optional;
 
 /// The result of structural comparison of type ABI artifacts.
 enum comparison_result
@@ -443,6 +444,7 @@ struct environment::priv
   bool					decl_only_class_equals_definition_;
   bool					use_enum_binary_only_equality_;
   bool					allow_type_comparison_results_caching_;
+  optional<bool>			analyze_exported_interfaces_only_;
 #ifdef WITH_DEBUG_SELF_COMPARISON
   bool					self_comparison_debug_on_;
 #endif
diff --git a/src/abg-ir.cc b/src/abg-ir.cc
index b3bec8fa..42442791 100644
--- a/src/abg-ir.cc
+++ b/src/abg-ir.cc
@@ -3674,6 +3674,42 @@ const config&
 environment::get_config() const
 {return priv_->config_;}
 
+/// Getter for a property that says if the user actually did set the
+/// analyze_exported_interfaces_only() property.  If not, it means
+/// the default behaviour prevails.
+///
+/// @return tru iff the user did set the
+/// analyze_exported_interfaces_only() property.
+bool
+environment::user_set_analyze_exported_interfaces_only() const
+{return priv_->analyze_exported_interfaces_only_.has_value();}
+
+/// Setter for the property that controls if we are to restrict the
+/// analysis to the types that are only reachable from the exported
+/// interfaces only, or if the set of types should be more broad than
+/// that.  Typically, we'd restrict the analysis to types reachable
+/// from exported interfaces only (stricto sensu, that would really be
+/// only the types that are part of the ABI of well designed
+/// libraries) for performance reasons.
+///
+/// @param f the value of the flag.
+void
+environment::analyze_exported_interfaces_only(bool f)
+{priv_->analyze_exported_interfaces_only_ = f;}
+
+/// Getter for the property that controls if we are to restrict the
+/// analysis to the types that are only reachable from the exported
+/// interfaces only, or if the set of types should be more broad than
+/// that.  Typically, we'd restrict the analysis to types reachable
+/// from exported interfaces only (stricto sensu, that would really be
+/// only the types that are part of the ABI of well designed
+/// libraries) for performance reasons.
+///
+/// @param f the value of the flag.
+bool
+environment::analyze_exported_interfaces_only() const
+{return priv_->analyze_exported_interfaces_only_.value_or(false);}
+
 #ifdef WITH_DEBUG_SELF_COMPARISON
 /// Setter of the corpus of the input corpus of the self comparison
 /// that takes place when doing "abidw --debug-abidiff <binary>".
diff --git a/tools/abidiff.cc b/tools/abidiff.cc
index 97b036cb..e0bb35ac 100644
--- a/tools/abidiff.cc
+++ b/tools/abidiff.cc
@@ -29,6 +29,7 @@ using std::ostream;
 using std::cout;
 using std::cerr;
 using std::shared_ptr;
+using abg_compat::optional;
 using abigail::ir::environment;
 using abigail::ir::environment_sptr;
 using abigail::translation_unit;
@@ -74,6 +75,7 @@ struct options
   vector<string>	headers_dirs2;
   vector<string>        header_files2;
   bool			drop_private_types;
+  optional<bool>	exported_interfaces_only;
   bool			linux_kernel_mode;
   bool			no_default_supprs;
   bool			no_arch;
@@ -197,6 +199,9 @@ display_usage(const string& prog_name, ostream& out)
     << " --header-file2|--hf2 <path>  the path to one header of file2\n"
     << " --drop-private-types  drop private types from "
     "internal representation\n"
+    << "  --exported-interfaces-only  analyze exported interfaces only\n"
+    << "  --allow-non-exported-interfaces  analyze interfaces that "
+    "might not be exported\n"
     << " --no-linux-kernel-mode  don't consider the input binaries as "
        "linux kernel binaries\n"
     << " --kmi-whitelist|-w  path to a "
@@ -403,6 +408,10 @@ parse_command_line(int argc, char* argv[], options& opts)
 	}
       else if (!strcmp(argv[i], "--drop-private-types"))
 	opts.drop_private_types = true;
+      else if (!strcmp(argv[i], "--exported-interfaces-only"))
+	opts.exported_interfaces_only = true;
+      else if (!strcmp(argv[i], "--allow-non-exported-interfaces"))
+	opts.exported_interfaces_only = false;
       else if (!strcmp(argv[i], "--no-default-suppression"))
 	opts.no_default_supprs = true;
       else if (!strcmp(argv[i], "--no-architecture"))
@@ -1130,6 +1139,9 @@ main(int argc, char* argv[])
       t2_type = guess_file_type(opts.file2);
 
       environment_sptr env(new environment);
+      if (opts.exported_interfaces_only.has_value())
+	env->analyze_exported_interfaces_only(*opts.exported_interfaces_only);
+
 #ifdef WITH_DEBUG_SELF_COMPARISON
 	    if (opts.do_debug)
 	      env->self_comparison_debug_is_on(true);
diff --git a/tools/abidw.cc b/tools/abidw.cc
index 9a27a029..f38d6048 100644
--- a/tools/abidw.cc
+++ b/tools/abidw.cc
@@ -40,6 +40,7 @@ using std::ostream;
 using std::ofstream;
 using std::vector;
 using std::shared_ptr;
+using abg_compat::optional;
 using abigail::tools_utils::emit_prefix;
 using abigail::tools_utils::temp_file;
 using abigail::tools_utils::temp_file_sptr;
@@ -114,6 +115,7 @@ struct options
   bool			do_log;
   bool			drop_private_types;
   bool			drop_undefined_syms;
+  optional<bool>	exported_interfaces_only;
   type_id_style_kind	type_id_style;
 #ifdef WITH_DEBUG_SELF_COMPARISON
   string		type_id_file_path;
@@ -187,6 +189,9 @@ display_usage(const string& prog_name, ostream& out)
     << "  --short-locs  only print filenames rather than paths\n"
     << "  --drop-private-types  drop private types from representation\n"
     << "  --drop-undefined-syms  drop undefined symbols from representation\n"
+    << "  --exported-interfaces-only  analyze exported interfaces only\n"
+    << "  --allow-non-exported-interfaces  analyze interfaces that "
+    "might not be exported\n"
     << "  --no-comp-dir-path  do not show compilation path information\n"
     << "  --no-elf-needed  do not show the DT_NEEDED information\n"
     << "  --no-write-default-sizes  do not emit pointer size when it equals"
@@ -368,6 +373,10 @@ parse_command_line(int argc, char* argv[], options& opts)
 	opts.drop_private_types = true;
       else if (!strcmp(argv[i], "--drop-undefined-syms"))
 	opts.drop_undefined_syms = true;
+      else if (!strcmp(argv[i], "--exported-interfaces-only"))
+	opts.exported_interfaces_only = true;
+      else if (!strcmp(argv[i], "--allow-non-exported-interfaces"))
+	opts.exported_interfaces_only = false;
       else if (!strcmp(argv[i], "--no-linux-kernel-mode"))
 	opts.linux_kernel_mode = false;
       else if (!strcmp(argv[i], "--abidiff"))
@@ -606,6 +615,9 @@ load_corpus_and_write_abixml(char* argv[],
             }
         }
 
+      if (opts.exported_interfaces_only.has_value())
+	env->analyze_exported_interfaces_only(*opts.exported_interfaces_only);
+
       t.start();
       corp = dwarf_reader::read_corpus_from_elf(ctxt, s);
       t.stop();
@@ -813,6 +825,9 @@ load_kernel_corpus_group_and_write_abixml(char* argv[],
   timer t, global_timer;
   suppressions_type supprs;
 
+  if (opts.exported_interfaces_only.has_value())
+    env->analyze_exported_interfaces_only(*opts.exported_interfaces_only);
+
   if (opts.do_log)
     emit_prefix(argv[0], cerr)
       << "going to build ABI representation of the Linux Kernel ...\n";
diff --git a/tools/abipkgdiff.cc b/tools/abipkgdiff.cc
index 551080b9..656d5882 100644
--- a/tools/abipkgdiff.cc
+++ b/tools/abipkgdiff.cc
@@ -106,6 +106,7 @@ using std::set;
 using std::ostringstream;
 using std::shared_ptr;
 using std::dynamic_pointer_cast;
+using abg_compat::optional;
 using abigail::workers::task;
 using abigail::workers::task_sptr;
 using abigail::workers::queue;
@@ -205,6 +206,7 @@ public:
   bool		fail_if_no_debug_info;
   bool		show_identical_binaries;
   bool		self_check;
+  optional<bool> exported_interfaces_only;
 #ifdef WITH_CTF
   bool		use_ctf;
 #endif
@@ -868,6 +870,9 @@ display_usage(const string& prog_name, ostream& out)
     "full impact analysis report rather than the default leaf changes reports\n"
     << " --non-reachable-types|-t  consider types non reachable"
     " from public interfaces\n"
+    << "  --exported-interfaces-only  analyze exported interfaces only\n"
+    << "  --allow-non-exported-interfaces  analyze interfaces that "
+    "might not be exported\n"
     << " --no-linkage-name		do not display linkage names of "
     "added/removed/changed\n"
     << " --redundant                    display redundant changes\n"
@@ -2076,6 +2081,10 @@ public:
     abigail::elf_reader::status detailed_status =
       abigail::elf_reader::STATUS_UNKNOWN;
 
+    if (args->opts.exported_interfaces_only.has_value())
+      env->analyze_exported_interfaces_only
+	(*args->opts.exported_interfaces_only);
+
     status |= compare(args->elf1, args->debug_dir1, args->private_types_suppr1,
 		      args->elf2, args->debug_dir2, args->private_types_suppr2,
 		      args->opts, env, diff, ctxt, &detailed_status);
@@ -2142,6 +2151,10 @@ public:
     diff_context_sptr ctxt;
     corpus_diff_sptr diff;
 
+    if (args->opts.exported_interfaces_only.has_value())
+      env->analyze_exported_interfaces_only
+	(*args->opts.exported_interfaces_only);
+
     abigail::elf_reader::status detailed_status =
       abigail::elf_reader::STATUS_UNKNOWN;
 
@@ -3024,6 +3037,10 @@ compare_prepared_linux_kernel_packages(package& first_package,
   string dist_root2 = second_package.extracted_dir_path();
 
   abigail::ir::environment_sptr env(new abigail::ir::environment);
+  if (opts.exported_interfaces_only.has_value())
+    env->analyze_exported_interfaces_only
+      (*opts.exported_interfaces_only);
+
   suppressions_type supprs;
   corpus_group_sptr corpus1, corpus2;
   corpus1 = build_corpus_group_from_kernel_dist_under(dist_root1,
@@ -3326,6 +3343,10 @@ parse_command_line(int argc, char* argv[], options& opts)
       else if (!strcmp(argv[i], "--full-impact")
 	       ||!strcmp(argv[i], "-f"))
 	opts.show_full_impact_report = true;
+      else if (!strcmp(argv[i], "--exported-interfaces-only"))
+	opts.exported_interfaces_only = true;
+      else if (!strcmp(argv[i], "--allow-non-exported-interfaces"))
+	opts.exported_interfaces_only = false;
       else if (!strcmp(argv[i], "--no-linkage-name"))
 	opts.show_linkage_names = false;
       else if (!strcmp(argv[i], "--redundant"))
diff --git a/tools/kmidiff.cc b/tools/kmidiff.cc
index 8fd3fed9..f3332765 100644
--- a/tools/kmidiff.cc
+++ b/tools/kmidiff.cc
@@ -29,6 +29,7 @@ using std::vector;
 using std::ostream;
 using std::cout;
 using std::cerr;
+using abg_compat::optional;
 
 using namespace abigail::tools_utils;
 using namespace abigail::dwarf_reader;
@@ -60,6 +61,7 @@ struct options
   bool			show_hexadecimal_values;
   bool			show_offsets_sizes_in_bits;
   bool			show_impacted_interfaces;
+  optional<bool>	exported_interfaces_only;
 #ifdef WITH_CTF
   bool			use_ctf;
 #endif
@@ -120,6 +122,9 @@ display_usage(const string& prog_name, ostream& out)
     << " --impacted-interfaces|-i  show interfaces impacted by ABI changes\n"
     << " --full-impact|-f  show the full impact of changes on top-most "
 	 "interfaces\n"
+    << "  --exported-interfaces-only  analyze exported interfaces only\n"
+    << "  --allow-non-exported-interfaces  analyze interfaces that "
+    "might not be exported\n"
     << " --show-bytes  show size and offsets in bytes\n"
     << " --show-bits  show size and offsets in bits\n"
     << " --show-hex  show size and offset in hexadecimal\n"
@@ -262,6 +267,10 @@ parse_command_line(int argc, char* argv[], options& opts)
       else if (!strcmp(argv[i], "--full-impact")
 	       || !strcmp(argv[i], "-f"))
 	opts.leaf_changes_only = false;
+      else if (!strcmp(argv[i], "--exported-interfaces-only"))
+	opts.exported_interfaces_only = true;
+      else if (!strcmp(argv[i], "--allow-non-exported-interfaces"))
+	opts.exported_interfaces_only = false;
       else if (!strcmp(argv[i], "--show-bytes"))
 	opts.show_offsets_sizes_in_bits = false;
       else if (!strcmp(argv[i], "--show-bits"))
@@ -408,6 +417,9 @@ main(int argc, char* argv[])
 
   environment_sptr env(new environment);
 
+  if (opts.exported_interfaces_only.has_value())
+    env->analyze_exported_interfaces_only(*opts.exported_interfaces_only);
+
   corpus_group_sptr group1, group2;
   string debug_info_root_dir;
   corpus::origin origin =
-- 
2.37.2



-- 
		Dodji


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 3/4] Fix IR comparison result caching and canonical type propagation tracking
  2022-09-20 10:50 [PATCH 0/4, applied] Speed up type DIEs canonicalization Dodji Seketeli
  2022-09-20 11:27 ` [PATCH 1/4, applied] dwarf-reader: Revamp the canonical type DIE propagation algorithm Dodji Seketeli
  2022-09-20 11:29 ` [PATCH 2/4, applied] Allow restricting analyzed decls to exported symbols Dodji Seketeli
@ 2022-09-20 11:30 ` Dodji Seketeli
  2022-09-20 11:37 ` [PATCH 4/4, applied] ir, writer: Go back to canonicalizing typedefs in the IR Dodji Seketeli
  3 siblings, 0 replies; 5+ messages in thread
From: Dodji Seketeli @ 2022-09-20 11:30 UTC (permalink / raw)
  To: libabigail; +Cc: dodji

Hello,

Caching the result of IR comparison cannot happen on IR nodes that are
being compared in the context of type canonicalization if one of the
nodes is the target of canonical type propagation.  This is especially
true if the recursive IR node which comparison "temporarily" did yield
true (to avoid an infinite loop) at least until the comparison of the
rest of the sub-tree of the recursive type is done.  In that case, we
should not cache the result the comparison as it might change later.

The patch adds a way to track recursive types so that we know when not
to cache their comparison result.

As we now have a facility to track recursive types during canonical
type propagation (rather than just types that depend on recursive
types) we can be a bit more precise when confirming or cancelling
types that have been subject to canonical type propagation.

Also the patch cleans up the detection of comparison cycle to make it
more typesafe so that the macro
RETURN_TRUE_IF_COMPARISON_CYCLE_DETECTED can be used for class_decl
and union_decl, not just class_or_union.  It makes the code more
readable/maintainable is the equals overload for class_decl and
union_decl all have their proper call to
RETURN_TRUE_IF_COMPARISON_CYCLE_DETECTED.  The same is true for
mark_types_as_being_compared.

Doing that cleans up the detection of recursive types as well as the
types that depends on those recursive types.

	* src/abg-ir-priv.h (environment::priv::recursive_types_): Define
	new data member.
	(environment::priv::cache_type_comparison_result): Cache results
	only non-recursive and not dependant types, or when the result is
	"false".  In all these cases, the result, once cached, will not
	change.
	(environment::priv::mark_dependant_types_compared_until): Mark
	types as recursive at the same time others are marked as
	dependant.
	(environment::priv::{is_recursive_type, set_is_not_recursive}):
	Define new member functions.
	(environment::priv::{confirm_ct_propagation,
	cancel_ct_propagation}): Confirming canonical type propagation
	should happen for recursive types as well as their dependant
	types.
	* src/abg-ir.cc (return_comparison_result): Keep up with the
	book-keeping at all time when type canonicalization process is
	on-going.  Whenever we expect types that depends on recursive
	types, expect recursive types too, obviously.
	(type_base::get_canonical_type_for): Do not erase the comparison
	result cache between the canonicalization of two different types.
	(is_comparison_cycle_detected)
	(mark_types_as_being_compared, unmark_types_as_being_compared):
	Define new overloads for class_decl;
	(equals): In the overloads for class_decl and union_decl, use
	RETURN_COMPARISON_RESULT and mark_types_as_being_compared without
	casting it to class_or_union.
	* tests/data/test-abidiff/test-PR18791-report0.txt: Adjust.
	* tests/data/test-diff-dwarf/PR25058-liblttng-ctl-report-1.txt:
	Adjust.
	* tests/data/test-diff-pkg/nss-3.23.0-1.0.fc23.x86_64-report-0.txt:
	Adjust.
	* tests/data/test-diff-pkg/spice-server-0.12.4-19.el7.x86_64-0.12.8-1.el7.x86_64-report-2.txt:
	Adjust.
	* tests/data/test-diff-pkg/tbb-4.1-9.20130314.fc22.x86_64--tbb-4.3-3.20141204.fc23.x86_64-report-0.txt:
	Adjust.
	* tests/data/test-diff-pkg/tbb-4.1-9.20130314.fc22.x86_64--tbb-4.3-3.20141204.fc23.x86_64-report-1.txt:
	Adjust.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
---
 src/abg-ir-priv.h                             |  46 +++-
 src/abg-ir.cc                                 |  96 ++++++--
 .../test-abidiff/test-PR18791-report0.txt     | 225 +++---------------
 .../PR25058-liblttng-ctl-report-1.txt         |   7 +-
 .../nss-3.23.0-1.0.fc23.x86_64-report-0.txt   |   6 +-
 ...l7.x86_64-0.12.8-1.el7.x86_64-report-2.txt |  82 ++++++-
 ...bb-4.3-3.20141204.fc23.x86_64-report-0.txt |  47 +---
 ...bb-4.3-3.20141204.fc23.x86_64-report-1.txt |  13 +-
 8 files changed, 251 insertions(+), 271 deletions(-)

diff --git a/src/abg-ir-priv.h b/src/abg-ir-priv.h
index 21734b25..b6f9354e 100644
--- a/src/abg-ir-priv.h
+++ b/src/abg-ir-priv.h
@@ -419,6 +419,7 @@ struct environment::priv
   // the canonical type propagation is cancelled, the canonical types
   // must be cleared.
   pointer_set		types_with_non_confirmed_propagated_ct_;
+  pointer_set		recursive_types_;
 #ifdef WITH_DEBUG_SELF_COMPARISON
   // This is used for debugging purposes.
   // When abidw is used with the option --debug-abidiff, some
@@ -518,11 +519,19 @@ struct environment::priv
   void
   cache_type_comparison_result(T& first, T& second, bool r)
   {
-    if (allow_type_comparison_results_caching())
-      type_comparison_results_cache_.emplace
-	(std::make_pair(reinterpret_cast<uint64_t>(&first),
-			reinterpret_cast<uint64_t>(&second)),
-	 r);
+    if (allow_type_comparison_results_caching()
+	&& (r == false
+	    ||
+	    (!is_recursive_type(&first)
+	     && !is_recursive_type(&second)
+	     && !is_type(&first)->priv_->depends_on_recursive_type()
+	     && !is_type(&second)->priv_->depends_on_recursive_type())))
+      {
+	type_comparison_results_cache_.emplace
+	  (std::make_pair(reinterpret_cast<uint64_t>(&first),
+			  reinterpret_cast<uint64_t>(&second)),
+	   r);
+      }
   }
 
   /// Retrieve the result of comparing two sub-types from the cache,
@@ -749,9 +758,30 @@ struct environment::priv
     result |=
       mark_dependant_types(right,
 			   right_type_comp_operands_);
+    recursive_types_.insert(reinterpret_cast<uintptr_t>(right));
     return result;
   }
 
+  /// Test if a type is a recursive one.
+  ///
+  /// @param t the type to consider.
+  ///
+  /// @return true iff @p t is recursive.
+  bool
+  is_recursive_type(const type_base* t)
+  {
+    return (recursive_types_.find(reinterpret_cast<uintptr_t>(t))
+	    != recursive_types_.end());
+  }
+
+
+  /// Unflag a type as being recursive
+  ///
+  /// @param t the type to unflag
+  void
+  set_is_not_recursive(const type_base* t)
+  {recursive_types_.erase(reinterpret_cast<uintptr_t>(t));}
+
   /// Propagate the canonical type of a type to another one.
   ///
   /// @param src the type to propagate the canonical type from.
@@ -786,7 +816,8 @@ struct environment::priv
     for (auto i : types_with_non_confirmed_propagated_ct_)
       {
 	type_base *t = reinterpret_cast<type_base*>(i);
-	ABG_ASSERT(t->priv_->depends_on_recursive_type());
+	ABG_ASSERT(t->get_environment()->priv_->is_recursive_type(t)
+		   || t->priv_->depends_on_recursive_type());
 	t->priv_->set_does_not_depend_on_recursive_type(dependant_type);
 	if (!t->priv_->depends_on_recursive_type())
 	  to_remove.insert(i);
@@ -858,7 +889,8 @@ struct environment::priv
     for (auto i : to_remove)
       {
 	type_base *t = reinterpret_cast<type_base*>(i);
-	ABG_ASSERT(t->priv_->depends_on_recursive_type());
+	ABG_ASSERT(t->get_environment()->priv_->is_recursive_type(t)
+		   || t->priv_->depends_on_recursive_type());
 	type_base_sptr canonical = t->priv_->canonical_type.lock();
 	if (canonical)
 	  {
diff --git a/src/abg-ir.cc b/src/abg-ir.cc
index 42442791..905d1b43 100644
--- a/src/abg-ir.cc
+++ b/src/abg-ir.cc
@@ -382,7 +382,9 @@ bool
 mark_dependant_types_compared_until(const type_base &r)
 {
   const environment * env = r.get_environment();
-  return env->priv_->mark_dependant_types_compared_until(&r);
+  if (env->do_on_the_fly_canonicalization())
+    return env->priv_->mark_dependant_types_compared_until(&r);
+  return false;
 }
 
 /// @brief the location of a token represented in its simplest form.
@@ -920,6 +922,22 @@ is_comparison_cycle_detected(T& l, T& r)
   return result ;
 }
 
+/// Detect if a recursive comparison cycle is detected while
+/// structurally comparing two @ref class_decl types.
+///
+/// @param l the left-hand-side operand of the current comparison.
+///
+/// @param r the right-hand-side operand of the current comparison.
+///
+/// @return true iff a comparison cycle is detected.
+template<>
+bool
+is_comparison_cycle_detected(const class_decl& l, const class_decl& r)
+{
+  return is_comparison_cycle_detected(static_cast<const class_or_union&>(l),
+				      static_cast<const class_or_union&>(r));
+}
+
 /// This macro is to be used while comparing composite types that
 /// might recursively refer to themselves.  Comparing two such types
 /// might get us into a cyle.
@@ -968,6 +986,22 @@ mark_types_as_being_compared(T& l, T&r)
   push_composite_type_comparison_operands(l, r);
 }
 
+/// Mark a pair of @ref class_decl types as being compared.
+///
+/// This is helpful to later detect recursive cycles in the comparison
+/// stack.
+///
+/// @param l the left-hand-side operand of the comparison.
+///
+/// @parm r the right-hand-side operand of the comparison.
+template<>
+void
+mark_types_as_being_compared(const class_decl& l, const class_decl &r)
+{
+  return mark_types_as_being_compared(static_cast<const class_or_union&>(l),
+				      static_cast<const class_or_union&>(r));
+}
+
 /// Mark a pair of types as being not compared anymore.
 ///
 /// This is helpful to later detect recursive cycles in the comparison
@@ -987,6 +1021,26 @@ unmark_types_as_being_compared(T& l, T&r)
   pop_composite_type_comparison_operands(l, r);
 }
 
+/// Mark a pair of @ref class_decl types as being not compared
+/// anymore.
+///
+/// This is helpful to later detect recursive cycles in the comparison
+/// stack.
+///
+/// Note that the types must have been passed to
+/// mark_types_as_being_compared prior to calling this function.
+///
+/// @param l the left-hand-side operand of the comparison.
+///
+/// @parm r the right-hand-side operand of the comparison.
+template<>
+void
+unmark_types_as_being_compared(const class_decl& l, const class_decl &r)
+{
+  return unmark_types_as_being_compared(static_cast<const class_or_union&>(l),
+					static_cast<const class_or_union&>(r));
+}
+
 /// Return the result of the comparison of two (sub) types.
 ///
 /// The function does the necessary book keeping before returning the
@@ -1020,7 +1074,7 @@ return_comparison_result(T& l, T& r, bool value,
   unmark_types_as_being_compared(l, r);
 
   const environment* env = l.get_environment();
-  if (propagate_canonical_type && env->do_on_the_fly_canonicalization())
+  if (env->do_on_the_fly_canonicalization())
     // We are instructed to perform the "canonical type propagation"
     // optimization, making 'r' to possibly get the canonical type of
     // 'l' if it has one.  This mostly means that we are currently
@@ -1028,8 +1082,8 @@ return_comparison_result(T& l, T& r, bool value,
     // the 'r' argument.
     {
       if (value == true
-	  && is_type(&r)->priv_->depends_on_recursive_type()
-	  && !env->priv_->right_type_comp_operands_.empty()
+	  && (is_type(&r)->priv_->depends_on_recursive_type()
+	      || env->priv_->is_recursive_type(&r))
 	  && is_type(&r)->priv_->canonical_type_propagated())
 	{
 	  // Track the object 'r' for which the propagated canonical
@@ -1048,10 +1102,12 @@ return_comparison_result(T& l, T& r, bool value,
 	  // sub-types that were compared during the comparison of
 	  // 'r'.
 	  env->priv_->confirm_ct_propagation(&r);
-	  if (is_type(&r)->priv_->depends_on_recursive_type())
+	  if (is_type(&r)->priv_->depends_on_recursive_type()
+	      || env->priv_->is_recursive_type(&r))
 	    {
 	      is_type(&r)->priv_->set_does_not_depend_on_recursive_type();
 	      env->priv_->remove_from_types_with_non_confirmed_propagated_ct(&r);
+	      env->priv_->set_is_not_recursive(&r);
 	    }
 	}
       else if (value == false)
@@ -1062,7 +1118,8 @@ return_comparison_result(T& l, T& r, bool value,
 	  // should see their tentatively propagated canonical type
 	  // cancelled.
 	  env->priv_->cancel_ct_propagation(&r);
-	  if (is_type(&r)->priv_->depends_on_recursive_type())
+	  if (is_type(&r)->priv_->depends_on_recursive_type()
+	      || env->priv_->is_recursive_type(&r))
 	    {
 	      // The right-hand-side operand cannot carry any tentative
 	      // canonical type at this point.
@@ -14051,10 +14108,10 @@ compare_types_during_canonicalization(const type_base_sptr& canonical_type,
   if (env->debug_type_canonicalization_is_on())
     {
       bool canonical_equality = false, structural_equality = false;
-      env->priv_->use_canonical_type_comparison_ = true;
-      canonical_equality = canonical_type == candidate_type;
       env->priv_->use_canonical_type_comparison_ = false;
       structural_equality = canonical_type == candidate_type;
+      env->priv_->use_canonical_type_comparison_ = true;
+      canonical_equality = canonical_type == candidate_type;
       if (canonical_equality != structural_equality)
 	{
 	  std::cerr << "structural & canonical equality different for type: "
@@ -14208,7 +14265,6 @@ type_base::get_canonical_type_for(type_base_sptr t)
 	  // the decl-only-class-being-equal-to-a-matching-definition
 	  // flags.
 	  env->priv_->allow_type_comparison_results_caching(false);
-	  env->priv_->clear_type_comparison_results_cache();
 	  env->do_on_the_fly_canonicalization(false);
 	  env->decl_only_class_equals_definition
 	    (saved_decl_only_class_equals_definition);
@@ -23626,8 +23682,7 @@ equals(const class_decl& l, const class_decl& r, change_kind* k)
 		      static_cast<const class_or_union&>(r),
 		      k));
 
-  RETURN_TRUE_IF_COMPARISON_CYCLE_DETECTED(static_cast<const class_or_union&>(l),
-					   static_cast<const class_or_union&>(r));
+  RETURN_TRUE_IF_COMPARISON_CYCLE_DETECTED(l, r);
 
   bool had_canonical_type = !!r.get_naked_canonical_type();
   bool result = true;
@@ -23640,13 +23695,9 @@ equals(const class_decl& l, const class_decl& r, change_kind* k)
 	ABG_RETURN(result);
     }
 
-  mark_types_as_being_compared(static_cast<const class_or_union&>(l),
-			       static_cast<const class_or_union&>(r));
+  mark_types_as_being_compared(l, r);
 
-#define RETURN(value)							 \
-  return return_comparison_result(static_cast<const class_or_union&>(l), \
-				  static_cast<const class_or_union&>(r), \
-				  value);
+#define RETURN(value) return return_comparison_result(l, r, value);
 
   // If comparing the class_or_union 'part' of the type led to
   // canonical type propagation, then cancel that because it's too
@@ -24768,10 +24819,19 @@ union_decl::~union_decl()
 bool
 equals(const union_decl& l, const union_decl& r, change_kind* k)
 {
+
+  RETURN_TRUE_IF_COMPARISON_CYCLE_DETECTED(l, r);
+
+#define RETURN(value)				\
+  return return_comparison_result(l, r, value);
+
   bool result = equals(static_cast<const class_or_union&>(l),
 		       static_cast<const class_or_union&>(r),
 		       k);
-  ABG_RETURN(result);
+
+  mark_types_as_being_compared(l, r);
+
+  RETURN(result);
 }
 
 /// Copy a method of a @ref union_decl into a new @ref
diff --git a/tests/data/test-abidiff/test-PR18791-report0.txt b/tests/data/test-abidiff/test-PR18791-report0.txt
index 367929c7..9cefdfc2 100644
--- a/tests/data/test-abidiff/test-PR18791-report0.txt
+++ b/tests/data/test-abidiff/test-PR18791-report0.txt
@@ -1,4 +1,4 @@
-Functions changes summary: 1 Removed, 60 Changed, 1 Added functions
+Functions changes summary: 1 Removed, 36 Changed, 1 Added functions
 Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
 
 1 Removed function:
@@ -9,113 +9,7 @@ Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
 
   [A] 'method void std::__cxx11::_List_base<sigc::slot_base, std::allocator<sigc::slot_base> >::_M_clear()'
 
-60 functions with some indirect sub-type change:
-
-  [C] 'method bool sigc::connection::block(bool)' has some indirect sub-type changes:
-    implicit parameter 0 of type 'sigc::connection*' has sub-type changes:
-      in pointed to type 'struct sigc::connection':
-        type size hasn't changed
-        1 data member change:
-          type of 'sigc::slot_base* slot_' changed:
-            in pointed to type 'class sigc::slot_base':
-              type size hasn't changed
-              1 data member change:
-                type of 'sigc::slot_base::rep_type* rep_' changed:
-                  in pointed to type 'typedef sigc::slot_base::rep_type':
-                    underlying type 'struct sigc::internal::slot_rep' changed:
-                      type size hasn't changed
-                      1 base class change:
-                        'struct sigc::trackable' changed:
-                          type size hasn't changed
-                          1 data member change:
-                            type of 'sigc::internal::trackable_callback_list* callback_list_' changed:
-                              in pointed to type 'struct sigc::internal::trackable_callback_list':
-                                type size changed from 192 to 256 (in bits)
-                                2 data member changes:
-                                  type of 'sigc::internal::trackable_callback_list::callback_list callbacks_' changed:
-                                    underlying type 'class std::list<sigc::internal::trackable_callback, std::allocator<sigc::internal::trackable_callback> >' changed:
-                                      type name changed from 'std::list<sigc::internal::trackable_callback, std::allocator<sigc::internal::trackable_callback> >' to 'std::__cxx11::list<sigc::internal::trackable_callback, std::allocator<sigc::internal::trackable_callback> >'
-                                      type size changed from 128 to 192 (in bits)
-                                      1 base class change:
-                                        'class std::_List_base<sigc::internal::trackable_callback, std::allocator<sigc::internal::trackable_callback> >' changed:
-                                          type name changed from 'std::_List_base<sigc::internal::trackable_callback, std::allocator<sigc::internal::trackable_callback> >' to 'std::__cxx11::_List_base<sigc::internal::trackable_callback, std::allocator<sigc::internal::trackable_callback> >'
-                                          type size changed from 128 to 192 (in bits)
-                                          1 data member change:
-                                            type of 'std::_List_base<sigc::internal::trackable_callback, std::allocator<sigc::internal::trackable_callback> >::_List_impl _M_impl' changed:
-                                              type name changed from 'std::_List_base<sigc::internal::trackable_callback, std::allocator<sigc::internal::trackable_callback> >::_List_impl' to 'std::__cxx11::_List_base<sigc::internal::trackable_callback, std::allocator<sigc::internal::trackable_callback> >::_List_impl'
-                                              type size changed from 128 to 192 (in bits)
-                                              1 data member change:
-                                                type of 'std::__detail::_List_node_base _M_node' changed:
-                                                  type name changed from 'std::__detail::_List_node_base' to 'std::_List_node<long unsigned int>'
-                                                  type size changed from 128 to 192 (in bits)
-                                                  1 base class insertion:
-                                                    struct std::__detail::_List_node_base
-                                                  2 data member deletions:
-                                                    'std::__detail::_List_node_base* _M_next', at offset 0 (in bits)
-                                                    'std::__detail::_List_node_base* _M_prev', at offset 64 (in bits)
-                                                  1 data member insertion:
-                                                    'unsigned long int _M_data', at offset 128 (in bits)
-                                                and name of 'std::_List_base<sigc::internal::trackable_callback, std::allocator<sigc::internal::trackable_callback> >::_List_impl::_M_node' changed to 'std::__cxx11::_List_base<sigc::internal::trackable_callback, std::allocator<sigc::internal::trackable_callback> >::_List_impl::_M_node'
-                                            and name of 'std::_List_base<sigc::internal::trackable_callback, std::allocator<sigc::internal::trackable_callback> >::_M_impl' changed to 'std::__cxx11::_List_base<sigc::internal::trackable_callback, std::allocator<sigc::internal::trackable_callback> >::_M_impl'
-                                  'bool clearing_' offset changed from 128 to 192 (in bits) (by +64 bits)
-
-  [C] 'method bool sigc::connection::blocked()' has some indirect sub-type changes:
-    implicit parameter 0 of type 'const sigc::connection*' has sub-type changes:
-      in pointed to type 'const sigc::connection':
-        unqualified underlying type 'struct sigc::connection' changed, as reported earlier
-
-  [C] 'method sigc::connection::connection(const sigc::connection&)' has some indirect sub-type changes:
-    implicit parameter 0 of type 'sigc::connection*' has sub-type changes:
-      pointed to type 'struct sigc::connection' changed, as reported earlier
-    parameter 1 of type 'const sigc::connection&' has sub-type changes:
-      in referenced type 'const sigc::connection':
-        unqualified underlying type 'struct sigc::connection' changed, as reported earlier
-
-  [C] 'method sigc::connection::connection()' has some indirect sub-type changes:
-    implicit parameter 0 of type 'sigc::connection*' has sub-type changes:
-      pointed to type 'struct sigc::connection' changed, as reported earlier
-
-  [C] 'method sigc::connection::connection(sigc::slot_base&)' has some indirect sub-type changes:
-    implicit parameter 0 of type 'sigc::connection*' has sub-type changes:
-      pointed to type 'struct sigc::connection' changed, as reported earlier
-    parameter 1 of type 'sigc::slot_base&' has sub-type changes:
-      referenced type 'class sigc::slot_base' changed, as reported earlier
-
-  [C] 'method void sigc::connection::disconnect()' has some indirect sub-type changes:
-    implicit parameter 0 of type 'sigc::connection*' has sub-type changes:
-      pointed to type 'struct sigc::connection' changed, as reported earlier
-
-  [C] 'method bool sigc::connection::empty()' has some indirect sub-type changes:
-    implicit parameter 0 of type 'const sigc::connection*' has sub-type changes:
-      in pointed to type 'const sigc::connection':
-        unqualified underlying type 'struct sigc::connection' changed, as reported earlier
-
-  [C] 'method bool sigc::connection::operator bool()' has some indirect sub-type changes:
-    implicit parameter 0 of type 'sigc::connection*' has sub-type changes:
-      pointed to type 'struct sigc::connection' changed, as reported earlier
-
-  [C] 'method sigc::connection& sigc::connection::operator=(const sigc::connection&)' has some indirect sub-type changes:
-    return type changed:
-      referenced type 'struct sigc::connection' changed, as reported earlier
-    implicit parameter 0 of type 'sigc::connection*' has sub-type changes:
-      pointed to type 'struct sigc::connection' changed, as reported earlier
-    parameter 1 of type 'const sigc::connection&' has sub-type changes:
-      in referenced type 'const sigc::connection':
-        unqualified underlying type 'struct sigc::connection' changed, as reported earlier
-
-  [C] 'method void sigc::connection::set_slot(sigc::slot_base*)' has some indirect sub-type changes:
-    implicit parameter 0 of type 'sigc::connection*' has sub-type changes:
-      pointed to type 'struct sigc::connection' changed, as reported earlier
-    parameter 1 of type 'sigc::slot_base*' has sub-type changes:
-      pointed to type 'class sigc::slot_base' changed, as reported earlier
-
-  [C] 'method bool sigc::connection::unblock()' has some indirect sub-type changes:
-    implicit parameter 0 of type 'sigc::connection*' has sub-type changes:
-      pointed to type 'struct sigc::connection' changed, as reported earlier
-
-  [C] 'method sigc::connection::~connection(int)' has some indirect sub-type changes:
-    implicit parameter 0 of type 'sigc::connection*' has sub-type changes:
-      pointed to type 'struct sigc::connection' changed, as reported earlier
+36 functions with some indirect sub-type change:
 
   [C] 'method void sigc::internal::signal_impl::block(bool)' has some indirect sub-type changes:
     implicit parameter 0 of type 'sigc::internal::signal_impl*' has sub-type changes:
@@ -134,7 +28,16 @@ Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
                     type name changed from 'std::_List_base<sigc::slot_base, std::allocator<sigc::slot_base> >::_List_impl' to 'std::__cxx11::_List_base<sigc::slot_base, std::allocator<sigc::slot_base> >::_List_impl'
                     type size changed from 128 to 192 (in bits)
                     1 data member change:
-                      type of 'std::__detail::_List_node_base _M_node' changed, as reported earlier
+                      type of 'std::__detail::_List_node_base _M_node' changed:
+                        type name changed from 'std::__detail::_List_node_base' to 'std::_List_node<long unsigned int>'
+                        type size changed from 128 to 192 (in bits)
+                        1 base class insertion:
+                          struct std::__detail::_List_node_base
+                        2 data member deletions:
+                          'std::__detail::_List_node_base* _M_next', at offset 0 (in bits)
+                          'std::__detail::_List_node_base* _M_prev', at offset 64 (in bits)
+                        1 data member insertion:
+                          'unsigned long int _M_data', at offset 128 (in bits)
                       and name of 'std::_List_base<sigc::slot_base, std::allocator<sigc::slot_base> >::_List_impl::_M_node' changed to 'std::__cxx11::_List_base<sigc::slot_base, std::allocator<sigc::slot_base> >::_List_impl::_M_node'
                   and name of 'std::_List_base<sigc::slot_base, std::allocator<sigc::slot_base> >::_M_impl' changed to 'std::__cxx11::_List_base<sigc::slot_base, std::allocator<sigc::slot_base> >::_M_impl'
 
@@ -150,9 +53,6 @@ Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
   [C] 'method sigc::internal::signal_impl::iterator_type sigc::internal::signal_impl::connect(const sigc::slot_base&)' has some indirect sub-type changes:
     implicit parameter 0 of type 'sigc::internal::signal_impl*' has sub-type changes:
       pointed to type 'struct sigc::internal::signal_impl' changed, as reported earlier
-    parameter 1 of type 'const sigc::slot_base&' has sub-type changes:
-      in referenced type 'const sigc::slot_base':
-        unqualified underlying type 'class sigc::slot_base' changed, as reported earlier
 
   [C] 'method sigc::internal::signal_impl::iterator_type sigc::internal::signal_impl::erase(sigc::internal::signal_impl::iterator_type)' has some indirect sub-type changes:
     implicit parameter 0 of type 'sigc::internal::signal_impl*' has sub-type changes:
@@ -161,9 +61,6 @@ Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
   [C] 'method sigc::internal::signal_impl::iterator_type sigc::internal::signal_impl::insert(sigc::internal::signal_impl::iterator_type, const sigc::slot_base&)' has some indirect sub-type changes:
     implicit parameter 0 of type 'sigc::internal::signal_impl*' has sub-type changes:
       pointed to type 'struct sigc::internal::signal_impl' changed, as reported earlier
-    parameter 2 of type 'const sigc::slot_base&' has sub-type changes:
-      in referenced type 'const sigc::slot_base':
-        unqualified underlying type 'class sigc::slot_base' changed, as reported earlier
 
   [C] 'method sigc::internal::signal_impl::signal_impl()' has some indirect sub-type changes:
     implicit parameter 0 of type 'sigc::internal::signal_impl*' has sub-type changes:
@@ -183,12 +80,31 @@ Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
       pointed to type 'struct sigc::internal::signal_impl' changed, as reported earlier
 
   [C] 'method void sigc::internal::slot_rep::disconnect()' has some indirect sub-type changes:
-    implicit parameter 0 of type 'sigc::internal::slot_rep*' has sub-type changes:
-      pointed to type 'struct sigc::internal::slot_rep' changed, as reported earlier
+    implicit parameter 0 of type 'sigc::internal::slot_rep*' changed:
+      in pointed to type 'struct sigc::internal::slot_rep':
 
   [C] 'method void sigc::internal::trackable_callback_list::add_callback(void*)' has some indirect sub-type changes:
     implicit parameter 0 of type 'sigc::internal::trackable_callback_list*' has sub-type changes:
-      pointed to type 'struct sigc::internal::trackable_callback_list' changed, as reported earlier
+      in pointed to type 'struct sigc::internal::trackable_callback_list':
+        type size changed from 192 to 256 (in bits)
+        2 data member changes:
+          type of 'sigc::internal::trackable_callback_list::callback_list callbacks_' changed:
+            underlying type 'class std::list<sigc::internal::trackable_callback, std::allocator<sigc::internal::trackable_callback> >' changed:
+              type name changed from 'std::list<sigc::internal::trackable_callback, std::allocator<sigc::internal::trackable_callback> >' to 'std::__cxx11::list<sigc::internal::trackable_callback, std::allocator<sigc::internal::trackable_callback> >'
+              type size changed from 128 to 192 (in bits)
+              1 base class change:
+                'class std::_List_base<sigc::internal::trackable_callback, std::allocator<sigc::internal::trackable_callback> >' changed:
+                  type name changed from 'std::_List_base<sigc::internal::trackable_callback, std::allocator<sigc::internal::trackable_callback> >' to 'std::__cxx11::_List_base<sigc::internal::trackable_callback, std::allocator<sigc::internal::trackable_callback> >'
+                  type size changed from 128 to 192 (in bits)
+                  1 data member change:
+                    type of 'std::_List_base<sigc::internal::trackable_callback, std::allocator<sigc::internal::trackable_callback> >::_List_impl _M_impl' changed:
+                      type name changed from 'std::_List_base<sigc::internal::trackable_callback, std::allocator<sigc::internal::trackable_callback> >::_List_impl' to 'std::__cxx11::_List_base<sigc::internal::trackable_callback, std::allocator<sigc::internal::trackable_callback> >::_List_impl'
+                      type size changed from 128 to 192 (in bits)
+                      1 data member change:
+                        type of 'std::__detail::_List_node_base _M_node' changed, as reported earlier
+                        and name of 'std::_List_base<sigc::internal::trackable_callback, std::allocator<sigc::internal::trackable_callback> >::_List_impl::_M_node' changed to 'std::__cxx11::_List_base<sigc::internal::trackable_callback, std::allocator<sigc::internal::trackable_callback> >::_List_impl::_M_node'
+                    and name of 'std::_List_base<sigc::internal::trackable_callback, std::allocator<sigc::internal::trackable_callback> >::_M_impl' changed to 'std::__cxx11::_List_base<sigc::internal::trackable_callback, std::allocator<sigc::internal::trackable_callback> >::_M_impl'
+          'bool clearing_' offset changed from 128 to 192 (in bits) (by +64 bits)
 
   [C] 'method void sigc::internal::trackable_callback_list::clear()' has some indirect sub-type changes:
     implicit parameter 0 of type 'sigc::internal::trackable_callback_list*' has sub-type changes:
@@ -208,7 +124,10 @@ Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
         type size hasn't changed
         1 base class change:
           'struct sigc::trackable' changed:
-            details were reported earlier
+            type size hasn't changed
+            1 data member change:
+              type of 'sigc::internal::trackable_callback_list* callback_list_' changed:
+                pointed to type 'struct sigc::internal::trackable_callback_list' changed, as reported earlier
         1 data member change:
           type of 'sigc::internal::signal_impl* impl_' changed:
             pointed to type 'struct sigc::internal::signal_impl' changed, as reported earlier
@@ -225,9 +144,6 @@ Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
   [C] 'method sigc::signal_base::iterator_type sigc::signal_base::connect(const sigc::slot_base&)' has some indirect sub-type changes:
     implicit parameter 0 of type 'sigc::signal_base*' has sub-type changes:
       pointed to type 'struct sigc::signal_base' changed, as reported earlier
-    parameter 1 of type 'const sigc::slot_base&' has sub-type changes:
-      in referenced type 'const sigc::slot_base':
-        unqualified underlying type 'class sigc::slot_base' changed, as reported earlier
 
   [C] 'method sigc::signal_base::iterator_type sigc::signal_base::erase(sigc::signal_base::iterator_type)' has some indirect sub-type changes:
     implicit parameter 0 of type 'sigc::signal_base*' has sub-type changes:
@@ -243,9 +159,6 @@ Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
   [C] 'method sigc::signal_base::iterator_type sigc::signal_base::insert(sigc::signal_base::iterator_type, const sigc::slot_base&)' has some indirect sub-type changes:
     implicit parameter 0 of type 'sigc::signal_base*' has sub-type changes:
       pointed to type 'struct sigc::signal_base' changed, as reported earlier
-    parameter 2 of type 'const sigc::slot_base&' has sub-type changes:
-      in referenced type 'const sigc::slot_base':
-        unqualified underlying type 'class sigc::slot_base' changed, as reported earlier
 
   [C] 'method sigc::signal_base& sigc::signal_base::operator=(const sigc::signal_base&)' has some indirect sub-type changes:
     return type changed:
@@ -280,68 +193,6 @@ Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
     implicit parameter 0 of type 'sigc::signal_base*' has sub-type changes:
       pointed to type 'struct sigc::signal_base' changed, as reported earlier
 
-  [C] 'method void sigc::slot_base::add_destroy_notify_callback(void*)' has some indirect sub-type changes:
-    implicit parameter 0 of type 'const sigc::slot_base*' has sub-type changes:
-      in pointed to type 'const sigc::slot_base':
-        unqualified underlying type 'class sigc::slot_base' changed, as reported earlier
-
-  [C] 'method bool sigc::slot_base::block(bool)' has some indirect sub-type changes:
-    implicit parameter 0 of type 'sigc::slot_base*' has sub-type changes:
-      pointed to type 'class sigc::slot_base' changed, as reported earlier
-
-  [C] 'method void sigc::slot_base::disconnect()' has some indirect sub-type changes:
-    implicit parameter 0 of type 'sigc::slot_base*' has sub-type changes:
-      pointed to type 'class sigc::slot_base' changed, as reported earlier
-
-  [C] 'method bool sigc::slot_base::operator bool()' has some indirect sub-type changes:
-    implicit parameter 0 of type 'const sigc::slot_base*' has sub-type changes:
-      in pointed to type 'const sigc::slot_base':
-        unqualified underlying type 'class sigc::slot_base' changed, as reported earlier
-
-  [C] 'method sigc::slot_base& sigc::slot_base::operator=(const sigc::slot_base&)' has some indirect sub-type changes:
-    return type changed:
-      referenced type 'class sigc::slot_base' changed, as reported earlier
-    implicit parameter 0 of type 'sigc::slot_base*' has sub-type changes:
-      pointed to type 'class sigc::slot_base' changed, as reported earlier
-    parameter 1 of type 'const sigc::slot_base&' has sub-type changes:
-      in referenced type 'const sigc::slot_base':
-        unqualified underlying type 'class sigc::slot_base' changed, as reported earlier
-
-  [C] 'method void sigc::slot_base::remove_destroy_notify_callback(void*)' has some indirect sub-type changes:
-    implicit parameter 0 of type 'const sigc::slot_base*' has sub-type changes:
-      in pointed to type 'const sigc::slot_base':
-        unqualified underlying type 'class sigc::slot_base' changed, as reported earlier
-
-  [C] 'method void sigc::slot_base::set_parent(void*)' has some indirect sub-type changes:
-    implicit parameter 0 of type 'const sigc::slot_base*' has sub-type changes:
-      in pointed to type 'const sigc::slot_base':
-        unqualified underlying type 'class sigc::slot_base' changed, as reported earlier
-
-  [C] 'method sigc::slot_base::slot_base(sigc::slot_base::rep_type*)' has some indirect sub-type changes:
-    implicit parameter 0 of type 'sigc::slot_base*' has sub-type changes:
-      pointed to type 'class sigc::slot_base' changed, as reported earlier
-    parameter 1 of type 'sigc::slot_base::rep_type*' has sub-type changes:
-      pointed to type 'typedef sigc::slot_base::rep_type' changed, as reported earlier
-
-  [C] 'method sigc::slot_base::slot_base()' has some indirect sub-type changes:
-    implicit parameter 0 of type 'sigc::slot_base*' has sub-type changes:
-      pointed to type 'class sigc::slot_base' changed, as reported earlier
-
-  [C] 'method sigc::slot_base::slot_base(const sigc::slot_base&)' has some indirect sub-type changes:
-    implicit parameter 0 of type 'sigc::slot_base*' has sub-type changes:
-      pointed to type 'class sigc::slot_base' changed, as reported earlier
-    parameter 1 of type 'const sigc::slot_base&' has sub-type changes:
-      in referenced type 'const sigc::slot_base':
-        unqualified underlying type 'class sigc::slot_base' changed, as reported earlier
-
-  [C] 'method bool sigc::slot_base::unblock()' has some indirect sub-type changes:
-    implicit parameter 0 of type 'sigc::slot_base*' has sub-type changes:
-      pointed to type 'class sigc::slot_base' changed, as reported earlier
-
-  [C] 'method sigc::slot_base::~slot_base(int)' has some indirect sub-type changes:
-    implicit parameter 0 of type 'sigc::slot_base*' has sub-type changes:
-      pointed to type 'class sigc::slot_base' changed, as reported earlier
-
   [C] 'method void sigc::trackable::add_destroy_notify_callback(void*)' has some indirect sub-type changes:
     implicit parameter 0 of type 'const sigc::trackable*' has sub-type changes:
       in pointed to type 'const sigc::trackable':
diff --git a/tests/data/test-diff-dwarf/PR25058-liblttng-ctl-report-1.txt b/tests/data/test-diff-dwarf/PR25058-liblttng-ctl-report-1.txt
index 02879fcb..328ce804 100644
--- a/tests/data/test-diff-dwarf/PR25058-liblttng-ctl-report-1.txt
+++ b/tests/data/test-diff-dwarf/PR25058-liblttng-ctl-report-1.txt
@@ -89,7 +89,12 @@ Variables changes summary: 0 Removed, 0 Changed, 3 Added variables
     parameter 1 of type 'lttng_action*' has sub-type changes:
       in pointed to type 'struct lttng_action':
         type size hasn't changed
-        2 data member changes:
+        3 data member changes:
+          type of 'action_validate_cb validate' changed:
+            underlying type 'bool (lttng_action*)*' changed:
+              in pointed to type 'function type bool (lttng_action*)':
+                parameter 1 of type 'lttng_action*' has sub-type changes:
+                  pointed to type 'struct lttng_action' changed, as being reported
           type of 'action_serialize_cb serialize' changed:
             underlying type 'typedef ssize_t (lttng_action*, char*)*' changed:
               in pointed to type 'function type typedef ssize_t (lttng_action*, char*)':
diff --git a/tests/data/test-diff-pkg/nss-3.23.0-1.0.fc23.x86_64-report-0.txt b/tests/data/test-diff-pkg/nss-3.23.0-1.0.fc23.x86_64-report-0.txt
index 546942fe..57b11a8a 100644
--- a/tests/data/test-diff-pkg/nss-3.23.0-1.0.fc23.x86_64-report-0.txt
+++ b/tests/data/test-diff-pkg/nss-3.23.0-1.0.fc23.x86_64-report-0.txt
@@ -68,7 +68,7 @@
 ================ end of changes of 'libssl3.so'===============
 
 ================ changes of 'libsmime3.so'===============
-  Functions changes summary: 0 Removed, 1 Changed (127 filtered out), 0 Added functions
+  Functions changes summary: 0 Removed, 1 Changed (146 filtered out), 0 Added functions
   Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
 
   1 function with some indirect sub-type change:
@@ -82,12 +82,12 @@
               type of 'NSSCMSContent content' changed:
                 underlying type 'union NSSCMSContentUnion' at cmst.h:118:1 changed:
                   type size hasn't changed
-                  1 data member changes (3 filtered):
+                  1 data member changes (4 filtered):
                     type of 'NSSCMSEncryptedData* encryptedData' changed:
                       in pointed to type 'typedef NSSCMSEncryptedData' at cmst.h:65:1:
                         underlying type 'struct NSSCMSEncryptedDataStr' at cmst.h:468:1 changed:
                           type size hasn't changed
-                          1 data member changes (1 filtered):
+                          1 data member changes (2 filtered):
                             type of 'NSSCMSAttribute** unprotectedAttr' changed:
                               in pointed to type 'NSSCMSAttribute*':
                                 in pointed to type 'typedef NSSCMSAttribute' at cmst.h:69:1:
diff --git a/tests/data/test-diff-pkg/spice-server-0.12.4-19.el7.x86_64-0.12.8-1.el7.x86_64-report-2.txt b/tests/data/test-diff-pkg/spice-server-0.12.4-19.el7.x86_64-0.12.8-1.el7.x86_64-report-2.txt
index 290ad869..c0e8a3e1 100644
--- a/tests/data/test-diff-pkg/spice-server-0.12.4-19.el7.x86_64-0.12.8-1.el7.x86_64-report-2.txt
+++ b/tests/data/test-diff-pkg/spice-server-0.12.4-19.el7.x86_64-0.12.8-1.el7.x86_64-report-2.txt
@@ -29,7 +29,87 @@
                 in pointed to type 'typedef QXLState' at spice-qxl.h:35:1:
                   underlying type 'struct QXLState' at reds.h:93:1 changed:
                     type size hasn't changed
-                    1 data member change:
+                    2 data member changes:
+                      type of 'QXLInterface* qif' changed:
+                        in pointed to type 'typedef QXLInterface' at spice-qxl.h:33:1:
+                          underlying type 'struct QXLInterface' at spice.h:230:1 changed:
+                            type size hasn't changed
+                            15 data member changes:
+                              type of 'void (QXLInstance*, QXLWorker*)* attache_worker' changed:
+                                in pointed to type 'function type void (QXLInstance*, QXLWorker*)':
+                                  parameter 1 of type 'QXLInstance*' has sub-type changes:
+                                    in pointed to type 'typedef QXLInstance' at spice-qxl.h:34:1:
+                                      underlying type 'struct QXLInstance' changed, as being reported
+                              type of 'void (QXLInstance*, int)* set_compression_level' changed:
+                                in pointed to type 'function type void (QXLInstance*, int)':
+                                  parameter 1 of type 'QXLInstance*' has sub-type changes:
+                                    in pointed to type 'typedef QXLInstance' at spice-qxl.h:34:1:
+                                      underlying type 'struct QXLInstance' changed, as being reported
+                              type of 'void (QXLInstance*, typedef uint32_t)* set_mm_time' changed:
+                                in pointed to type 'function type void (QXLInstance*, typedef uint32_t)':
+                                  parameter 1 of type 'QXLInstance*' has sub-type changes:
+                                    in pointed to type 'typedef QXLInstance' at spice-qxl.h:34:1:
+                                      underlying type 'struct QXLInstance' changed, as being reported
+                              type of 'void (QXLInstance*, QXLDevInitInfo*)* get_init_info' changed:
+                                in pointed to type 'function type void (QXLInstance*, QXLDevInitInfo*)':
+                                  parameter 1 of type 'QXLInstance*' has sub-type changes:
+                                    in pointed to type 'typedef QXLInstance' at spice-qxl.h:34:1:
+                                      underlying type 'struct QXLInstance' changed, as being reported
+                              type of 'int (QXLInstance*, QXLCommandExt*)* get_command' changed:
+                                in pointed to type 'function type int (QXLInstance*, QXLCommandExt*)':
+                                  parameter 1 of type 'QXLInstance*' has sub-type changes:
+                                    in pointed to type 'typedef QXLInstance' at spice-qxl.h:34:1:
+                                      underlying type 'struct QXLInstance' changed, as being reported
+                              type of 'int (QXLInstance*)* req_cmd_notification' changed:
+                                in pointed to type 'function type int (QXLInstance*)':
+                                  parameter 1 of type 'QXLInstance*' has sub-type changes:
+                                    in pointed to type 'typedef QXLInstance' at spice-qxl.h:34:1:
+                                      underlying type 'struct QXLInstance' changed, as being reported
+                              type of 'void (QXLInstance*, struct QXLReleaseInfoExt)* release_resource' changed:
+                                in pointed to type 'function type void (QXLInstance*, struct QXLReleaseInfoExt)':
+                                  parameter 1 of type 'QXLInstance*' has sub-type changes:
+                                    in pointed to type 'typedef QXLInstance' at spice-qxl.h:34:1:
+                                      underlying type 'struct QXLInstance' changed, as being reported
+                              type of 'int (QXLInstance*, QXLCommandExt*)* get_cursor_command' changed:
+                                in pointed to type 'function type int (QXLInstance*, QXLCommandExt*)':
+                                  parameter 1 of type 'QXLInstance*' has sub-type changes:
+                                    in pointed to type 'typedef QXLInstance' at spice-qxl.h:34:1:
+                                      underlying type 'struct QXLInstance' changed, as being reported
+                              type of 'int (QXLInstance*)* req_cursor_notification' changed:
+                                in pointed to type 'function type int (QXLInstance*)':
+                                  parameter 1 of type 'QXLInstance*' has sub-type changes:
+                                    in pointed to type 'typedef QXLInstance' at spice-qxl.h:34:1:
+                                      underlying type 'struct QXLInstance' changed, as being reported
+                              type of 'void (QXLInstance*, typedef uint32_t)* notify_update' changed:
+                                in pointed to type 'function type void (QXLInstance*, typedef uint32_t)':
+                                  parameter 1 of type 'QXLInstance*' has sub-type changes:
+                                    in pointed to type 'typedef QXLInstance' at spice-qxl.h:34:1:
+                                      underlying type 'struct QXLInstance' changed, as being reported
+                              type of 'int (QXLInstance*)* flush_resources' changed:
+                                in pointed to type 'function type int (QXLInstance*)':
+                                  parameter 1 of type 'QXLInstance*' has sub-type changes:
+                                    in pointed to type 'typedef QXLInstance' at spice-qxl.h:34:1:
+                                      underlying type 'struct QXLInstance' changed, as being reported
+                              type of 'void (QXLInstance*, typedef uint64_t)* async_complete' changed:
+                                in pointed to type 'function type void (QXLInstance*, typedef uint64_t)':
+                                  parameter 1 of type 'QXLInstance*' has sub-type changes:
+                                    in pointed to type 'typedef QXLInstance' at spice-qxl.h:34:1:
+                                      underlying type 'struct QXLInstance' changed, as being reported
+                              type of 'void (QXLInstance*, typedef uint32_t, QXLRect*, typedef uint32_t)* update_area_complete' changed:
+                                in pointed to type 'function type void (QXLInstance*, typedef uint32_t, QXLRect*, typedef uint32_t)':
+                                  parameter 1 of type 'QXLInstance*' has sub-type changes:
+                                    in pointed to type 'typedef QXLInstance' at spice-qxl.h:34:1:
+                                      underlying type 'struct QXLInstance' changed, as being reported
+                              type of 'void (QXLInstance*, typedef uint8_t, uint8_t*)* set_client_capabilities' changed:
+                                in pointed to type 'function type void (QXLInstance*, typedef uint8_t, uint8_t*)':
+                                  parameter 1 of type 'QXLInstance*' has sub-type changes:
+                                    in pointed to type 'typedef QXLInstance' at spice-qxl.h:34:1:
+                                      underlying type 'struct QXLInstance' changed, as being reported
+                              type of 'int (QXLInstance*, VDAgentMonitorsConfig*)* client_monitors_config' changed:
+                                in pointed to type 'function type int (QXLInstance*, VDAgentMonitorsConfig*)':
+                                  parameter 1 of type 'QXLInstance*' has sub-type changes:
+                                    in pointed to type 'typedef QXLInstance' at spice-qxl.h:34:1:
+                                      underlying type 'struct QXLInstance' changed, as being reported
                       type of 'RedDispatcher* dispatcher' changed:
                         in pointed to type 'struct RedDispatcher' at red_dispatcher.c:55:1:
                           type size changed from 3264 to 3328 (in bits)
diff --git a/tests/data/test-diff-pkg/tbb-4.1-9.20130314.fc22.x86_64--tbb-4.3-3.20141204.fc23.x86_64-report-0.txt b/tests/data/test-diff-pkg/tbb-4.1-9.20130314.fc22.x86_64--tbb-4.3-3.20141204.fc23.x86_64-report-0.txt
index 51341ddb..afe77511 100644
--- a/tests/data/test-diff-pkg/tbb-4.1-9.20130314.fc22.x86_64--tbb-4.3-3.20141204.fc23.x86_64-report-0.txt
+++ b/tests/data/test-diff-pkg/tbb-4.1-9.20130314.fc22.x86_64--tbb-4.3-3.20141204.fc23.x86_64-report-0.txt
@@ -1,5 +1,5 @@
 ================ changes of 'libtbb.so.2'===============
-  Functions changes summary: 0 Removed, 10 Changed (92 filtered out), 17 Added functions
+  Functions changes summary: 0 Removed, 8 Changed (67 filtered out), 17 Added functions
   Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
   Function symbols changes summary: 0 Removed, 0 Added function symbol not referenced by debug info
   Variable symbols changes summary: 3 Removed, 0 Added variable symbols not referenced by debug info
@@ -24,26 +24,7 @@
     [A] 'method void tbb::internal::concurrent_queue_base_v8::move_content(tbb::internal::concurrent_queue_base_v8&)'    {_ZN3tbb8internal24concurrent_queue_base_v812move_contentERS1_}
     [A] 'method void tbb::task_group_context::capture_fp_settings()'    {_ZN3tbb18task_group_context19capture_fp_settingsEv}
 
-  10 functions with some indirect sub-type change:
-
-    [C] 'method void tbb::filter::set_end_of_input()' at pipeline.cpp:700:1 has some indirect sub-type changes:
-      implicit parameter 0 of type 'tbb::filter*' has sub-type changes:
-        in pointed to type 'class tbb::filter' at pipeline.h:65:1:
-          type size hasn't changed
-          1 member function deletion:
-            'method virtual tbb::filter::~filter(int)' at pipeline.cpp:698:1
-          1 member function insertion:
-            'method virtual tbb::filter::~filter(int)' at pipeline.cpp:688:1
-          no member function changes (4 filtered);
-          1 data member changes (4 filtered):
-            type of 'tbb::internal::input_buffer* my_input_buffer' changed:
-              in pointed to type 'class tbb::internal::input_buffer' at pipeline.cpp:52:1:
-                type size hasn't changed
-                1 data member change:
-                  type of 'tbb::spin_mutex array_mutex' changed:
-                    type size hasn't changed
-                    1 base class insertion:
-                      class tbb::internal::mutex_copy_deprecated_and_disabled at tbb_stddef.h:334:1
+  8 functions with some indirect sub-type change:
 
     [C] 'method tbb::task& tbb::internal::allocate_root_with_context_proxy::allocate(std::size_t) const' at task.h:135:1 has some indirect sub-type changes:
       implicit parameter 0 of type 'const tbb::internal::allocate_root_with_context_proxy*' has sub-type changes:
@@ -70,16 +51,7 @@
                             2 data member insertions:
                               'volatile intptr_t* my_ref_top_priority', at offset 576 (in bits) at scheduler.h:96:1
                               'volatile uintptr_t* my_ref_reload_epoch', at offset 640 (in bits) at scheduler.h:99:1
-                            3 data member changes:
-                              type of 'tbb::internal::arena_slot* my_arena_slot' changed:
-                                in pointed to type 'struct tbb::internal::arena_slot' at scheduler_common.h:316:1:
-                                  type size hasn't changed
-                                  2 base class deletions:
-                                    struct tbb::internal::padded<tbb::internal::arena_slot_line1> at tbb_stddef.h:261:1
-                                    struct tbb::internal::padded<tbb::internal::arena_slot_line2> at tbb_stddef.h:261:1
-                                  2 base class insertions:
-                                    struct tbb::internal::padded<tbb::internal::arena_slot_line1, 128ul> at tbb_stddef.h:251:1
-                                    struct tbb::internal::padded<tbb::internal::arena_slot_line2, 128ul> at tbb_stddef.h:251:1
+                            2 data member changes (2 filtered):
                               type of 'tbb::internal::arena* my_arena' changed:
                                 in pointed to type 'class tbb::internal::arena' at arena.h:160:1:
                                   type size hasn't changed
@@ -87,7 +59,6 @@
                                     struct tbb::internal::padded<tbb::internal::arena_base> at tbb_stddef.h:261:1
                                   1 base class insertion:
                                     struct tbb::internal::padded<tbb::internal::arena_base, 128ul> at tbb_stddef.h:251:1
-                                  no data member change (1 filtered);
                               type of 'tbb::internal::mail_inbox my_inbox' changed:
                                 type size hasn't changed
                                 1 data member change:
@@ -180,7 +151,7 @@
             type of 'tbb::internal::concurrent_queue_rep* my_rep' changed:
               in pointed to type 'class tbb::internal::concurrent_queue_rep' at concurrent_queue.cpp:129:1:
                 type size hasn't changed
-                1 data member changes (2 filtered):
+                1 data member changes (1 filtered):
                   type of 'tbb::internal::concurrent_monitor items_avail' changed:
                     type size hasn't changed
                     1 data member change:
@@ -221,7 +192,6 @@
           type size hasn't changed
           1 base class insertion:
             class tbb::internal::mutex_copy_deprecated_and_disabled at tbb_stddef.h:334:1
-          no data member change (1 filtered);
 
     [C] 'method void tbb::queuing_rw_mutex::internal_construct()' at queuing_rw_mutex.h:146:1 has some indirect sub-type changes:
       implicit parameter 0 of type 'tbb::queuing_rw_mutex*' has sub-type changes:
@@ -229,7 +199,6 @@
           type size hasn't changed
           1 base class insertion:
             class tbb::internal::mutex_copy_deprecated_and_disabled at tbb_stddef.h:334:1
-          no data member change (1 filtered);
 
     [C] 'method void tbb::recursive_mutex::internal_construct()' at recursive_mutex.h:224:1 has some indirect sub-type changes:
       implicit parameter 0 of type 'tbb::recursive_mutex*' has sub-type changes:
@@ -238,14 +207,6 @@
           1 base class insertion:
             class tbb::internal::mutex_copy_deprecated_and_disabled at tbb_stddef.h:334:1
 
-    [C] 'method tbb::thread_bound_filter::result_type tbb::thread_bound_filter::process_item()' at pipeline.cpp:712:1 has some indirect sub-type changes:
-      implicit parameter 0 of type 'tbb::thread_bound_filter*' has sub-type changes:
-        in pointed to type 'class tbb::thread_bound_filter' at pipeline.h:197:1:
-          type size hasn't changed
-          1 base class change:
-            'class tbb::filter' at pipeline.h:74:1 changed:
-              details were reported earlier
-
   3 Removed variable symbols not referenced by debug info:
 
     [D] _ZTVN3rml16versioned_objectE
diff --git a/tests/data/test-diff-pkg/tbb-4.1-9.20130314.fc22.x86_64--tbb-4.3-3.20141204.fc23.x86_64-report-1.txt b/tests/data/test-diff-pkg/tbb-4.1-9.20130314.fc22.x86_64--tbb-4.3-3.20141204.fc23.x86_64-report-1.txt
index a2247eb8..a8611476 100644
--- a/tests/data/test-diff-pkg/tbb-4.1-9.20130314.fc22.x86_64--tbb-4.3-3.20141204.fc23.x86_64-report-1.txt
+++ b/tests/data/test-diff-pkg/tbb-4.1-9.20130314.fc22.x86_64--tbb-4.3-3.20141204.fc23.x86_64-report-1.txt
@@ -1,5 +1,5 @@
 ================ changes of 'libtbb.so.2'===============
-  Functions changes summary: 0 Removed, 8 Changed (94 filtered out), 17 Added functions
+  Functions changes summary: 0 Removed, 7 Changed (68 filtered out), 17 Added functions
   Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
   Function symbols changes summary: 0 Removed, 0 Added function symbol not referenced by debug info
   Variable symbols changes summary: 3 Removed, 0 Added variable symbols not referenced by debug info
@@ -24,7 +24,7 @@
     [A] 'method void tbb::internal::concurrent_queue_base_v8::move_content(tbb::internal::concurrent_queue_base_v8&)'    {_ZN3tbb8internal24concurrent_queue_base_v812move_contentERS1_}
     [A] 'method void tbb::task_group_context::capture_fp_settings()'    {_ZN3tbb18task_group_context19capture_fp_settingsEv}
 
-  8 functions with some indirect sub-type change:
+  7 functions with some indirect sub-type change:
 
     [C] 'method tbb::task& tbb::internal::allocate_root_with_context_proxy::allocate(std::size_t) const' at task.h:135:1 has some indirect sub-type changes:
       implicit parameter 0 of type 'const tbb::internal::allocate_root_with_context_proxy*' has sub-type changes:
@@ -64,7 +64,6 @@
           type size hasn't changed
           1 base class insertion:
             class tbb::internal::mutex_copy_deprecated_and_disabled at tbb_stddef.h:334:1
-          no data member change (1 filtered);
 
     [C] 'method void tbb::queuing_rw_mutex::internal_construct()' at queuing_rw_mutex.h:146:1 has some indirect sub-type changes:
       implicit parameter 0 of type 'tbb::queuing_rw_mutex*' has sub-type changes:
@@ -72,7 +71,6 @@
           type size hasn't changed
           1 base class insertion:
             class tbb::internal::mutex_copy_deprecated_and_disabled at tbb_stddef.h:334:1
-          no data member change (1 filtered);
 
     [C] 'method void tbb::recursive_mutex::internal_construct()' at recursive_mutex.h:224:1 has some indirect sub-type changes:
       implicit parameter 0 of type 'tbb::recursive_mutex*' has sub-type changes:
@@ -81,13 +79,6 @@
           1 base class insertion:
             class tbb::internal::mutex_copy_deprecated_and_disabled at tbb_stddef.h:334:1
 
-    [C] 'method void tbb::spin_mutex::internal_construct()' at spin_mutex.h:138:1 has some indirect sub-type changes:
-      implicit parameter 0 of type 'tbb::spin_mutex*' has sub-type changes:
-        in pointed to type 'class tbb::spin_mutex' at spin_mutex.h:40:1:
-          type size hasn't changed
-          1 base class insertion:
-            class tbb::internal::mutex_copy_deprecated_and_disabled at tbb_stddef.h:334:1
-
     [C] 'method void tbb::spin_rw_mutex_v3::internal_acquire_reader()' at spin_rw_mutex.h:53:1 has some indirect sub-type changes:
       implicit parameter 0 of type 'tbb::spin_rw_mutex_v3*' has sub-type changes:
         in pointed to type 'class tbb::spin_rw_mutex_v3' at spin_rw_mutex.h:42:1:
-- 
2.37.2



-- 
		Dodji


^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 4/4, applied] ir, writer: Go back to canonicalizing typedefs in the IR
  2022-09-20 10:50 [PATCH 0/4, applied] Speed up type DIEs canonicalization Dodji Seketeli
                   ` (2 preceding siblings ...)
  2022-09-20 11:30 ` [PATCH 3/4] Fix IR comparison result caching and canonical type propagation tracking Dodji Seketeli
@ 2022-09-20 11:37 ` Dodji Seketeli
  3 siblings, 0 replies; 5+ messages in thread
From: Dodji Seketeli @ 2022-09-20 11:37 UTC (permalink / raw)
  To: libabigail; +Cc: dodji

[-- Attachment #1: Type: text/plain, Size: 15969 bytes --]

Hello,

Now that DIE and IR canonicalizing has progressed in precision,
especially due to fixing the canonical type propagation it seems like
we can go back to canonicalizing typedefs again.  This makes the
writer not needing to handle non-canonicalized types because only a
short number and types of types are still non-canonicalized.

Then I ran the test suite and hell broke lose.  Things would work on a
given platform but won't on others, etc.

So a great deal of the patch actually fixes issues uncovered by the
fact that we are back to canonicalizing typedefs.

The symptoms of many of the issues are related to emitting abixml in a
stable manner across toolchains.  But in reality those issues are
real, deeper ones.

This patch is thus ALSO an attempt at fixing all the issues at once
because they can't be separated.  Either the abixml is stable and
"make check" passes on all platforms, or it is not and testing fails
on some platforms.

I believe that at the core of the problems lies the observation that a
given typedef (of a given name) could appear several times (the number
of times not being quite constant) in a given class or namespace.
That would lead to some instabilities in the sorting and ordering of
type-ids depending on the toolchain being used, for instance.

To fix this add_or_update_class_type in the DWARF reader is taught to
avoid adding a typedef to a class if a member type of the same name
already exists at that class scope.  This handles the fact that a
given class can be built piece-wise in the DWARF.  Also, when handling
a typedef type, build_ir_node_from_die is taught to avoid creating a
typedef IR for a given scope (namespace, union or class) if said scope
already contains a type of the same name.  To be able to do that, the
handling of member types is moved from the ir::class_or_union type to
the ir::scope_decl type.  That way, all scopes (not just classes or
unions) can handle looking up for the (member) types they contain.

Another issue that was uncovered is that when emitting decl-only class
named A (in a namespace for instance) there can be some instability as
to which decl-only class A is emitted.  This is due to the fact that
libabigail considers all decl-only classes named A to be equal.  The
problem however is that a decl-only class A might have member types.
And a decl-only A declared in a translation unit somewhere might have
some member types that are different from the same decl-only A
declared in another translation unit.  This doesn't necessarily
violate the ODR, but rather, can be the result of compiler
optimization.  For instance, in a given translation unit, only a given
member type of the decl-only A is used by the code, whereas in another
one, another member type of the same decl-only A is used.  So only
partial views of A are emitted in the translation unit depending on
what is used.  Anyway, to handle that case, when comes the time to
emit the decl-only A in the ABIXML writer, write_class_decl now emits
all the member types of all the instances A known to the system.  This
hopefully removes the instability I was seeing.  To do that,
maybe_update_types_lookup_map<class_decl> has been taught to also keep
track of decl-only classes.  A new lookup_decl_only_class_types has
been defined to gather all the instances of a given decl-only class
known to the system.

Last but not least, the topological type sorting facilities carried by
the types {decl,type}_topo_comp has been fixed to ensure a more stable
sorting and also to ensure that "abilint foo.abi" emits the decls and
types in the same order as the one defined in foo.abi.  This fixes
another abixml instability I was seeing in the test suite across
different toolchains.  While doing that, I noticed that the ABIXML
reader was forgetting to properly mark the corpus as originating from
ABIXML.  So I fixed that too.

	* include/abg-fwd.h (lookup_decl_only_class_types): Declare new
	function.
	* src/abg-ir-priv.h (class_or_union::priv::member_types_): Move
	this data member into scope_decl::priv.
	(class_or_union::priv::priv): Do not take member types.
	* include/abg-ir.h (sort_type): Likewise.
	(namespaces_type): Add new typedefs.
	(scope_decl::insert_member_decl): Make this be non-virtual.
	(scope_decl::{insert_member_type, add_member_type,
	remove_member_type, get_member_types, get_sorted_member_types,
	find_member_type}): Move these methods here from ...
	(class_or_union::{insert_member_type, add_member_type,
	remove_member_type, get_member_types, get_sorted_member_types,
	find_member_type}): ... here.
	(class_or_union::insert_member_decl): Make this be non-virtual.
	(class_decl::insert_member_decl): Make this be non-virtual.
	(class_or_union::get_sorted_member_types): Declare ...
	* src/abg-dwarf-reader.cc (add_or_update_class_type): Do not add a
	member type to the class type being built if it already exists
	there.
	(build_ir_node_from_die): If a scope (namespace, union or class)
	already has a typedef, do not create a new one to add it there
	again.
	* src/abg-ir.cc (equals): In the overload for typedefs take into
	account the name of typedefs again.
	(lookup_decl_only_class_types): Define new function.
	(compare_using_locations): Define new static function that has
	been factorized out of ...
	(decl_topo_comp::operator()): ... here.  Also, two decls originate
	from an abixml corpus, compare them only using their artificial
	locations, meaning make them keep the same order as the order of
	the original abixml file.
	(type_top_comp::operator()): Likewise.
	(sort_types): Define new function.
	(scope_decl::priv::member_types_): Move this here from
	class_or_union::priv.
	(scope_decl::priv::sorted_member_types_): Define new data member.
	(scope_decl::{get_member_types, find_member_type,
	insert_member_type, add_member_type, remove_member_type}): Move
	these methods here from class_or_union.
	(scope_decl::get_sorted_member_types): Define new method.
	(is_non_canonicalized_type): Canonicalize typedefs.
	(lookup_type_in_map): Allow this to look through decl-only types
	to get their definition.  Otherwise, if only a decl-only was
	found, return it.
	(maybe_update_types_lookup_map<class_decl>): Allow adding
	decl-only class types to the map of classes held per TU and per
	corpus.
	(class_or_union::{class_or_union, add_member_decl,
	has_no_member}): Adjust.
	(class_or_union::{insert_member_type, add_member_type,
	add_member_type, remove_member_type, get_member_types,
	get_sorted_member_types, find_member_type}): Move these methods to
	scope_decl.
	({class_decl, class_or_union}::insert_member_decl): Remove the
	"before" parameter as it was not used anymore due to the re-use of
	the scope_decl::add_member that handles member types.
	* src/abg-reader.cc (read_corpus_group_from_input)
	(create_native_xml_read_context): Set the corpus origin.
	* src/abg-writer.cc (write_context::{m_nc_type_id_map,
	m_emitted_non_canonicalized_type_set,
	m_referenced_non_canonicalized_types_set}): Remove these maps that
	hold stuff for non-canonicalized types.
	(write_context::{type_has_existing_id, get_id_for_type,
	clear_type_id_map, has_non_emitted_referenced_types,
	record_type_as_referenced, type_is_referenced,
	record_type_as_emitted, type_is_emitted, clear_referenced_types,
	write_referenced_types, write_canonical_type_ids}): Adjust these
	as the maps for non-canonicalized types are gone.
	(write_context::{get_referenced_non_canonicalized_types,
	get_emitted_non_canonicalized_type_set}): Remove these methods.
	(write_decl_in_scope)
	(write_class_decl_opening_tag, write_union_decl_opening_tag)
	(write_union_decl): Adjust comments.
	(write_class_decl): Sort member types.  Also, When emitting a
	decl-only class, get all of the decl-only classes of the same name
	declared in several other TUs and emit all their member types into
	this decl-only class just once.  This reduces redundancies and
	sorting instabilities because for libabigail, all decl-only
	classes of a given name are equal, even if they have member types.
	* tests/data/test-abidiff/test-PR18791-report0.txt: Adjust.
	* tests/data/test-annotate/libtest23.so.abi: Likewise.
	* tests/data/test-annotate/libtest24-drop-fns-2.so.abi: Likewise.
	* tests/data/test-annotate/libtest24-drop-fns.so.abi: Likewise.
	* tests/data/test-annotate/test-anonymous-members-0.o.abi:
	Likewise.
	* tests/data/test-annotate/test0.abi: Likewise.
	* tests/data/test-annotate/test1.abi: Likewise.
	* tests/data/test-annotate/test13-pr18894.so.abi: Likewise.
	* tests/data/test-annotate/test14-pr18893.so.abi: Likewise.
	* tests/data/test-annotate/test15-pr18892.so.abi: Likewise.
	* tests/data/test-annotate/test17-pr19027.so.abi: Likewise.
	* tests/data/test-annotate/test18-pr19037-libvtkRenderingLIC-6.1.so.abi:
	Likewise.
	* tests/data/test-annotate/test19-pr19023-libtcmalloc_and_profiler.so.abi:
	Likewise.
	* tests/data/test-annotate/test2.so.abi: Likewise.
	* tests/data/test-annotate/test20-pr19025-libvtkParallelCore-6.1.so.abi:
	Likewise.
	* tests/data/test-annotate/test21-pr19092.so.abi: Likewise.
	* tests/data/test-diff-dwarf/test42-PR21296-clanggcc-report0.txt:
	Likewise.
	* tests/data/test-diff-filter/test31-pr18535-libstdc++-report-0.txt:
	Likewise.
	* tests/data/test-diff-filter/test31-pr18535-libstdc++-report-1.txt:
	Likewise.
	* tests/data/test-diff-filter/test41-report-0.txt: Likewise.
	* tests/data/test-diff-pkg/PR24690/PR24690-report-0.txt: Likewise.
	* tests/data/test-diff-pkg/spice-server-0.12.4-19.el7.x86_64-0.12.8-1.el7.x86_64-report-2.txt:
	Likewise.
	* tests/data/test-read-dwarf/PR22015-libboost_iostreams.so.abi:
	Likewise.
	* tests/data/test-read-dwarf/PR22122-libftdc.so.abi: Likewise.
	* tests/data/test-read-dwarf/PR25007-sdhci.ko.abi: Likewise.
	* tests/data/test-read-dwarf/PR25042-libgdbm-clang-dwarf5.so.6.0.0.abi:
	Likewise.
	* tests/data/test-read-dwarf/PR26261/PR26261-exe.abi: Likewise.
	* tests/data/test-read-dwarf/PR27700/test-PR27700.abi: Likewise.
	* tests/data/test-read-dwarf/libtest23.so.abi: Likewise.
	* tests/data/test-read-dwarf/libtest24-drop-fns-2.so.abi:
	Likewise.
	* tests/data/test-read-dwarf/libtest24-drop-fns.so.abi: Likewise.
	* tests/data/test-read-dwarf/test-PR26568-1.o.abi: Likewise.
	* tests/data/test-read-dwarf/test-PR26568-2.o.abi: Likewise.
	* tests/data/test-read-dwarf/test-libaaudio.so.abi: Likewise.
	* tests/data/test-read-dwarf/test-libandroid.so.abi: Likewise.
	* tests/data/test-read-dwarf/test0.abi: Likewise.
	* tests/data/test-read-dwarf/test0.hash.abi: Likewise.
	* tests/data/test-read-dwarf/test1.abi: Likewise.
	* tests/data/test-read-dwarf/test1.hash.abi: Likewise.
	* tests/data/test-read-dwarf/test10-pr18818-gcc.so.abi: Likewise.
	* tests/data/test-read-dwarf/test11-pr18828.so.abi: Likewise.
	* tests/data/test-read-dwarf/test12-pr18844.so.abi: Likewise.
	* tests/data/test-read-dwarf/test13-pr18894.so.abi: Likewise.
	* tests/data/test-read-dwarf/test14-pr18893.so.abi: Likewise.
	* tests/data/test-read-dwarf/test15-pr18892.so.abi: Likewise.
	* tests/data/test-read-dwarf/test16-pr18904.so.abi: Likewise.
	* tests/data/test-read-dwarf/test17-pr19027.so.abi: Likewise.
	* tests/data/test-read-dwarf/test18-pr19037-libvtkRenderingLIC-6.1.so.abi:
	Likewise.
	* tests/data/test-read-dwarf/test19-pr19023-libtcmalloc_and_profiler.so.abi:
	Likewise.
	* tests/data/test-read-dwarf/test2.so.abi: Likewise.
	* tests/data/test-read-dwarf/test2.so.hash.abi: Likewise.
	* tests/data/test-read-dwarf/test20-pr19025-libvtkParallelCore-6.1.so.abi:
	Likewise.
	* tests/data/test-read-dwarf/test21-pr19092.so.abi: Likewise.
	* tests/data/test-read-dwarf/test22-pr19097-libstdc++.so.6.0.17.so.abi:
	Likewise.
	* tests/data/test-read-dwarf/test9-pr18818-clang.so.abi: Likewise.
	* tests/data/test-read-write/test18.xml: Likewise.
	* tests/data/test-read-write/test28-without-std-fns-ref.xml:
	Likewise.
	* tests/data/test-read-write/test28-without-std-vars-ref.xml:
	Likewise.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
---
 include/abg-fwd.h                             |     5 +
 include/abg-ir.h                              |    58 +-
 src/abg-dwarf-reader.cc                       |    17 +-
 src/abg-ir-priv.h                             |     7 +-
 src/abg-ir.cc                                 |   420 +-
 src/abg-reader.cc                             |     3 +
 src/abg-writer.cc                             |   166 +-
 .../test-abidiff/test-PR18791-report0.txt     |    13 +
 tests/data/test-annotate/libtest23.so.abi     |   724 +-
 .../test-annotate/libtest24-drop-fns-2.so.abi |   804 +-
 .../test-annotate/libtest24-drop-fns.so.abi   |   804 +-
 .../test-anonymous-members-0.o.abi            |    96 +-
 tests/data/test-annotate/test0.abi            |     4 +-
 tests/data/test-annotate/test1.abi            |    32 +-
 .../data/test-annotate/test13-pr18894.so.abi  |  2322 +-
 .../data/test-annotate/test14-pr18893.so.abi  | 18402 ++---
 .../data/test-annotate/test15-pr18892.so.abi  | 64330 ++++++++--------
 .../data/test-annotate/test17-pr19027.so.abi  | 36303 +++++----
 ...st18-pr19037-libvtkRenderingLIC-6.1.so.abi | 18052 +++--
 ...19-pr19023-libtcmalloc_and_profiler.so.abi | 49963 ++++++------
 tests/data/test-annotate/test2.so.abi         |    38 +-
 ...st20-pr19025-libvtkParallelCore-6.1.so.abi | 28890 +++----
 .../data/test-annotate/test21-pr19092.so.abi  |  7608 +-
 .../test42-PR21296-clanggcc-report0.txt       |     6 +
 .../test31-pr18535-libstdc++-report-0.txt     |    31 +-
 .../test31-pr18535-libstdc++-report-1.txt     |    31 +-
 .../data/test-diff-filter/test41-report-0.txt |    30 +-
 .../PR24690/PR24690-report-0.txt              |     2 +-
 ...l7.x86_64-0.12.8-1.el7.x86_64-report-2.txt |    31 +-
 .../PR22015-libboost_iostreams.so.abi         |  2858 +-
 .../test-read-dwarf/PR22122-libftdc.so.abi    | 16990 +++-
 .../data/test-read-dwarf/PR25007-sdhci.ko.abi | 14620 ++--
 .../PR25042-libgdbm-clang-dwarf5.so.6.0.0.abi |   642 +-
 .../test-read-dwarf/PR26261/PR26261-exe.abi   |    10 +-
 .../test-read-dwarf/PR27700/test-PR27700.abi  |     2 +-
 tests/data/test-read-dwarf/libtest23.so.abi   |   626 +-
 .../libtest24-drop-fns-2.so.abi               |   702 +-
 .../test-read-dwarf/libtest24-drop-fns.so.abi |   702 +-
 .../data/test-read-dwarf/test-PR26568-1.o.abi |    16 +-
 .../data/test-read-dwarf/test-PR26568-2.o.abi |    22 +-
 .../test-read-dwarf/test-libaaudio.so.abi     |   238 +-
 .../test-read-dwarf/test-libandroid.so.abi    | 56370 +++++++-------
 tests/data/test-read-dwarf/test0.abi          |     2 +-
 tests/data/test-read-dwarf/test0.hash.abi     |     2 +-
 tests/data/test-read-dwarf/test1.abi          |    26 +-
 tests/data/test-read-dwarf/test1.hash.abi     |     6 +-
 .../test-read-dwarf/test10-pr18818-gcc.so.abi |  7990 +-
 .../test-read-dwarf/test11-pr18828.so.abi     | 23566 +++---
 .../test-read-dwarf/test12-pr18844.so.abi     | 33361 ++++----
 .../test-read-dwarf/test13-pr18894.so.abi     |  2090 +-
 .../test-read-dwarf/test14-pr18893.so.abi     | 15144 ++--
 .../test-read-dwarf/test15-pr18892.so.abi     | 26815 ++++---
 .../test-read-dwarf/test16-pr18904.so.abi     | 40951 +++++-----
 .../test-read-dwarf/test17-pr19027.so.abi     | 24080 +++---
 ...st18-pr19037-libvtkRenderingLIC-6.1.so.abi | 14733 ++--
 ...19-pr19023-libtcmalloc_and_profiler.so.abi | 23754 +++---
 tests/data/test-read-dwarf/test2.so.abi       |    34 +-
 tests/data/test-read-dwarf/test2.so.hash.abi  |     4 +-
 ...st20-pr19025-libvtkParallelCore-6.1.so.abi | 21807 +++---
 .../test-read-dwarf/test21-pr19092.so.abi     |  6494 +-
 .../test22-pr19097-libstdc++.so.6.0.17.so.abi | 59148 +++++++-------
 .../test9-pr18818-clang.so.abi                |  4896 +-
 tests/data/test-read-write/test18.xml         |     2 +-
 .../test28-without-std-fns-ref.xml            |   860 +-
 .../test28-without-std-vars-ref.xml           |   780 +-
 65 files changed, 318361 insertions(+), 311174 deletions(-)

The patch is too big for the list so I am attaching it here.


[-- Attachment #2: 0004-ir-writer-Go-back-to-canonicalizing-typedefs-in-the-.patch.gz --]
[-- Type: application/gzip, Size: 4002840 bytes --]

[-- Attachment #3: Type: text/plain, Size: 14 bytes --]


-- 
		Dodji


^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2022-09-20 11:40 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-09-20 10:50 [PATCH 0/4, applied] Speed up type DIEs canonicalization Dodji Seketeli
2022-09-20 11:27 ` [PATCH 1/4, applied] dwarf-reader: Revamp the canonical type DIE propagation algorithm Dodji Seketeli
2022-09-20 11:29 ` [PATCH 2/4, applied] Allow restricting analyzed decls to exported symbols Dodji Seketeli
2022-09-20 11:30 ` [PATCH 3/4] Fix IR comparison result caching and canonical type propagation tracking Dodji Seketeli
2022-09-20 11:37 ` [PATCH 4/4, applied] ir, writer: Go back to canonicalizing typedefs in the IR Dodji Seketeli

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).