Hello,
This configure option adds the possibility to debug the type
canonicalization process specifically.
When this new configure option is turned on, in
ir::get_canonical_type_for, when the type T, candidate for
canonicalization is compared to a given canonical type C, the
comparison is done twice; once using structural equality and once
using canonical equality whenever it's possible. For all the
sub-types of T and C, structural equality and canonical equality must
yield the same result. Otherwise, an error message is emitted and the
process aborts.
This all happens when using the abidw program with the --enable-tc or
--enable-type-canonicalization option.
This has proven to be very helpful to detect type canonicalization issues.
For instance, here is a trace of canonicalization issue that was
detected thanks to this patch:
$ build/tools/abidw --debug-tc /usr/lib64/libwiretap.so.11.0.8
structural & canonical equality different for type: function type void (wtap*)
in compare_types_during_canonicalization at: /home/dodji/git/libabigail/PR28364/src/abg-ir.cc:13575: execution should not have reached this point!
Abandon (core dumped)
This means that right after canonicalizing the type "void (wtap*)",
structural and canonical equality yield different results. So it
means there is a problem with that type specifically that makes its
canonicalization "go wrong". This requires further debugging to
understand, but at least, we are super close to the root cause of the
canonicalization problem.
* configure.ac: Support the new
--enable-debug-type-canonicalization option. Define macro
WITH_DEBUG_TYPE_CANONICALIZATION accordingly.
* doc/manuals/abidw.rst: Update documentation.
* include/abg-ir.h
(environment::debug_type_canonicalization_is_on): Declare new
member function if WITH_DEBUG_TYPE_CANONICALIZATION is defined.
* src/abg-ir-priv.h
(environment::priv::{use_canonical_type_comparison_,
debug_type_canonicalization_}): Define new data members if
WITH_DEBUG_TYPE_CANONICALIZATION is defined.
(environment::priv::priv): Initialize them.
* src/abg-ir.cc (try_canonical_compare): When
WITH_DEBUG_TYPE_CANONICALIZATION is defined, perform comparison
using either structural or canonical equality depending on the
environment::priv::use_canonical_type_comparison_ flag.
(environment::debug_type_canonicalization_is_on): Define member
function when WITH_DEBUG_TYPE_CANONICALIZATION is defined.
(compare_types_during_canonicalization): Define new function.
(type_base::get_canonical_type_for): Use the new function
compare_types_during_canonicalization.
* tools/abidw.cc (options::debug_type_canonicalization): Define
new data member.
(option::option): Initialize it.
(display_usage): Add help string for --debug-tc.
(parse_command_line): Support new option --debug-tc or
--debug-type-canonicalization.
(load_corpus_and_write_abixml): Turn type canonicalization
debugging on if --enable-tc is provided.
Signed-off-by: Dodji Seketeli <dodji@redhat.com>
Applied to master.
---
configure.ac | 19 ++++++++
doc/manuals/abidw.rst | 13 +++++
include/abg-ir.h | 8 +++
src/abg-ir-priv.h | 20 ++++++++
src/abg-ir.cc | 110 +++++++++++++++++++++++++++++++++++++++++-
tools/abidw.cc | 19 ++++++++
6 files changed, 187 insertions(+), 2 deletions(-)
@@ -72,6 +72,12 @@ AC_ARG_ENABLE(debug-self-comparison,
ENABLE_DEBUG_SELF_COMPARISON=$enableval,
ENABLE_DEBUG_SELF_COMPARISON=no)
+AC_ARG_ENABLE(debug-type-canonicalization,
+ AS_HELP_STRING([--enable-debug-type-canonicalization=yes|no],
+ [enable debugging of type canonicalization 'abidw --debug-tc'(default is no)]),
+ ENABLE_DEBUG_TYPE_CANONICALIZATION=$enableval,
+ ENABLE_DEBUG_TYPE_CANONICALIZATION=no)
+
AC_ARG_ENABLE(deb,
AS_HELP_STRING([--enable-deb=yes|no|auto],
[enable the support of deb in abipkgdiff (default is auto)]),
@@ -313,6 +319,18 @@ fi
AM_CONDITIONAL(ENABLE_DEBUG_SELF_COMPARISON, test x$ENABLE_DEBUG_SELF_COMPARISON = xyes)
+dnl enable the debugging of type canonicalization when doing abidw --debug-tc <binary>
+if test x$ENABLE_DEBUG_TYPE_CANONICALIZATION = xyes; then
+ AC_DEFINE([WITH_DEBUG_TYPE_CANONICALIZATION],
+ 1,
+ [compile support of debugging type canonicalization while using abidw --debug-tc])
+ AC_MSG_NOTICE([support of debugging type canonicalization is enabled])
+else
+ AC_MSG_NOTICE([support of debugging type canonicalization is disabled])
+fi
+
+AM_CONDITIONAL(ENABLE_DEBUG_TYPE_CANONICALIZATION, test x$ENABLE_DEBUG_TYPE_CANONICALIZATION = xyes)
+
dnl Check for the dpkg program
if test x$ENABLE_DEB = xauto -o x$ENABLE_DEB = xyes; then
AC_CHECK_PROG(HAS_DPKG, dpkg, yes, no)
@@ -931,6 +949,7 @@ AC_MSG_NOTICE([
Enable rpm support in abipkgdiff : ${ENABLE_RPM}
Enable rpm 4.15 support in abipkgdiff tests : ${ENABLE_RPM415}
Enable self comparison debugging : ${ENABLE_DEBUG_SELF_COMPARISON}
+ Enable type canonicalization debugging : ${ENABLE_DEBUG_TYPE_CANONICALIZATION}
Enable deb support in abipkgdiff : ${ENABLE_DEB}
Enable GNU tar archive support in abipkgdiff : ${ENABLE_TAR}
Enable bash completion : ${ENABLE_BASH_COMPLETION}
@@ -250,6 +250,19 @@ Options
it the libabigail package needs to be configured with
the --enable-debug-self-comparison option.
+ * ``--debug-type-canonicalization | --debug-tc``
+
+ Debug the type canonicalization process. This is done by using
+ structural and canonical equality when canonicalizing every single
+ type. Structural and canonical equality should yield the same
+ result. If they don't yield the same result for a given type,
+ then it means that the canonicalization of that type went wrong.
+ In that case, an error message is emitted and the execution of the
+ program is aborted.
+
+ This option is available only if the package was configured with
+ the --enable-debug-type-canonicalization option.
+
* ``--annotate``
@@ -217,6 +217,14 @@ public:
self_comparison_debug_is_on() const;
#endif
+#ifdef WITH_DEBUG_TYPE_CANONICALIZATION
+ void
+ debug_type_canonicalization_is_on(bool flag);
+
+ bool
+ debug_type_canonicalization_is_on() const;
+#endif
+
vector<type_base_sptr>* get_canonical_types(const char* name);
type_base* get_canonical_type(const char* name, unsigned index);
@@ -389,6 +389,21 @@ struct environment::priv
#ifdef WITH_DEBUG_SELF_COMPARISON
bool self_comparison_debug_on_;
#endif
+#ifdef WITH_DEBUG_TYPE_CANONICALIZATION
+ // This controls whether to use canonical type comparison during
+ // type comparison or not. This is only used for debugging, when we
+ // want to ensure that comparing types using canonical or structural
+ // comparison yields the same result.
+ bool use_canonical_type_comparison_;
+ // Whether we are debugging type canonicalization or not. When
+ // debugging type canonicalization, a type is compared to its
+ // potential canonical type twice: The first time with canonical
+ // comparison activated, and the second time with structural
+ // comparison activated. The two comparison should yield the same
+ // result, otherwise, canonicalization is "broken" for that
+ // particular type.
+ bool debug_type_canonicalization_;
+#endif
priv()
: canonicalization_is_done_(),
@@ -398,6 +413,11 @@ struct environment::priv
#ifdef WITH_DEBUG_SELF_COMPARISON
,
self_comparison_debug_on_(false)
+#endif
+#ifdef WITH_DEBUG_TYPE_CANONICALIZATION
+ ,
+ use_canonical_type_comparison_(true),
+ debug_type_canonicalization_(false)
#endif
{}
@@ -864,10 +864,41 @@ template<typename T>
bool
try_canonical_compare(const T *l, const T *r)
{
+#if WITH_DEBUG_TYPE_CANONICALIZATION
+ // We are debugging the canonicalization of a type down the stack.
+ // 'l' is a subtype of a canonical type and 'r' is a subtype of the
+ // type being canonicalized. We are at a point where we can compare
+ // 'l' and 'r' either using canonical comparison (if 'l' and 'r'
+ // have canonical types) or structural comparison.
+ //
+ // Because we are debugging the process of type canonicalization, we
+ // want to compare 'l' and 'r' canonically *AND* structurally. Both
+ // kinds of comparison should yield the same result, otherwise type
+ // canonicalization just failed for the subtype 'r' of the type
+ // being canonicalized.
+ //
+ // In concrete terms, this function is going to be called twice with
+ // the same pair {'l', 'r'} to compare: The first time with
+ // environment::priv_->use_canonical_type_comparison_ set to true,
+ // instructing us to compare them canonically, and the second time
+ // with that boolean set to false, instructing us to compare them
+ // structurally.
+ const environment *env = l->get_environment();
+ if (env->priv_->use_canonical_type_comparison_)
+ {
+ if (const type_base *lc = l->get_naked_canonical_type())
+ if (const type_base *rc = r->get_naked_canonical_type())
+ ABG_RETURN_EQUAL(lc, rc);
+ }
+ return equals(*l, *r, 0);
+#else
if (const type_base *lc = l->get_naked_canonical_type())
if (const type_base *rc = r->get_naked_canonical_type())
ABG_RETURN_EQUAL(lc, rc);
return equals(*l, *r, 0);
+#endif
+
+
}
/// Detect if a recursive comparison cycle is detected while
@@ -3667,6 +3698,26 @@ environment::self_comparison_debug_is_on() const
{return priv_->self_comparison_debug_on_;}
#endif
+#ifdef WITH_DEBUG_TYPE_CANONICALIZATION
+/// Set the "type canonicalization debugging" mode, triggered by using
+/// the command: "abidw --debug-tc".
+///
+/// @param flag if true then the type canonicalization debugging mode
+/// is enabled.
+void
+environment::debug_type_canonicalization_is_on(bool flag)
+{priv_->debug_type_canonicalization_ = flag;}
+
+/// Getter of the "type canonicalization debugging" mode, triggered by
+/// using the command: "abidw --debug-tc".
+///
+/// @return true iff the type canonicalization debugging mode is
+/// enabled.
+bool
+environment::debug_type_canonicalization_is_on() const
+{return priv_->debug_type_canonicalization_;}
+#endif // WITH_DEBUG_TYPE_CANONICALIZATION
+
/// Get the vector of canonical types which have a given "string
/// representation".
///
@@ -13535,6 +13586,61 @@ types_defined_same_linux_kernel_corpus_public(const type_base& t1,
return false;
}
+
+/// Compare a type T against a canonical type.
+///
+/// This function is called during the canonicalization process of the
+/// type T. T is called the "candidate type" because it's in the
+/// process of being canonicalized. Meaning, it's going to be
+/// compared to a canonical type C. If T equals C, then the canonical
+/// type of T is C.
+///
+/// The purpose of this function is to allow the debugging of the
+/// canonicalization of T, if that debugging is activated by
+/// configuring the libabigail package with
+/// --enable-debug-type-canonicalization and by running "abidw
+/// --debug-tc". In that case, T is going to be compared to C twice:
+/// once with canonical equality and once with structural equality.
+/// The two comparisons must be equal. Otherwise, the
+/// canonicalization process is said to be faulty and this function
+/// aborts.
+///
+/// This is a sub-routine of type_base::get_canonical_type_for.
+///
+/// @param canonical_type the canonical type to compare the candidate
+/// type against.
+///
+/// @param candidate_type the candidate type to compare against the
+/// canonical type.
+///
+/// @return true iff @p canonical_type equals @p candidate_type.
+///
+static bool
+compare_types_during_canonicalization(const type_base_sptr& canonical_type,
+ const type_base_sptr& candidate_type)
+{
+#ifdef WITH_DEBUG_TYPE_CANONICALIZATION
+ environment *env = canonical_type->get_environment();
+ 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;
+ if (canonical_equality != structural_equality)
+ {
+ std::cerr << "structural & canonical equality different for type: "
+ << canonical_type->get_pretty_representation(true, true)
+ << std::endl;
+ ABG_ASSERT_NOT_REACHED;
+ }
+ return structural_equality;
+ }
+#endif //end WITH_DEBUG_TYPE_CANONICALIZATION
+ return canonical_type == candidate_type;
+}
+
/// Compute the canonical type for a given instance of @ref type_base.
///
/// Consider two types T and T'. The canonical type of T, denoted
@@ -13665,8 +13771,8 @@ type_base::get_canonical_type_for(type_base_sptr t)
// Compare types by considering that decl-only classes don't
// equal their definition.
env->decl_only_class_equals_definition(false);
- bool equal = types_defined_same_linux_kernel_corpus_public(**it, *t)
- || *it == t;
+ bool equal = (types_defined_same_linux_kernel_corpus_public(**it, *t)
+ || compare_types_during_canonicalization(*it, t));
// Restore the state of the on-the-fly-canonicalization and
// the decl-only-class-being-equal-to-a-matching-definition
// flags.
@@ -102,6 +102,9 @@ struct options
bool abidiff;
#ifdef WITH_DEBUG_SELF_COMPARISON
bool debug_abidiff;
+#endif
+#ifdef WITH_DEBUG_TYPE_CANONICALIZATION
+ bool debug_type_canonicalization;
#endif
bool annotate;
bool do_log;
@@ -132,6 +135,9 @@ struct options
abidiff(),
#ifdef WITH_DEBUG_SELF_COMPARISON
debug_abidiff(),
+#endif
+#ifdef WITH_DEBUG_TYPE_CANONICALIZATION
+ debug_type_canonicalization(),
#endif
annotate(),
do_log(),
@@ -195,6 +201,9 @@ display_usage(const string& prog_name, ostream& out)
<< " --abidiff compare the loaded ABI against itself\n"
#ifdef WITH_DEBUG_SELF_COMPARISON
<< " --debug-abidiff debug the process of comparing the loaded ABI against itself\n"
+#endif
+#ifdef WITH_DEBUG_TYPE_CANONICALIZATION
+ << " --debug-tc debug the type canonicalization process\n"
#endif
<< " --annotate annotate the ABI artifacts emitted in the output\n"
<< " --stats show statistics about various internal stuff\n"
@@ -348,6 +357,11 @@ parse_command_line(int argc, char* argv[], options& opts)
opts.abidiff = true;
opts.debug_abidiff = true;
}
+#endif
+#ifdef WITH_DEBUG_TYPE_CANONICALIZATION
+ else if (!strcmp(argv[i], "--debug-tc")
+ || !strcmp(argv[i], "debug-type-canonicalization"))
+ opts.debug_type_canonicalization = true;
#endif
else if (!strcmp(argv[i], "--annotate"))
opts.annotate = true;
@@ -498,6 +512,11 @@ load_corpus_and_write_abixml(char* argv[],
env->self_comparison_debug_is_on(true);
#endif
+#ifdef WITH_DEBUG_TYPE_CANONICALIZATION
+ if (opts.debug_type_canonicalization)
+ env->debug_type_canonicalization_is_on(true);
+#endif
+
read_context& ctxt = *context;
corpus_sptr corp;
dwarf_reader::status s = dwarf_reader::STATUS_UNKNOWN;