[applied] abilint: add the --show-type-use option

Message ID 871qznl4qb.fsf@redhat.com
State New
Headers
Series [applied] abilint: add the --show-type-use option |

Commit Message

Dodji Seketeli Feb. 28, 2022, 9:28 a.m. UTC
  Hello,

"abilint --show-type-use <type-id> <abixml-file>" is a facility that
shows how a type defined in an abixml file is used.  That is, it emits
a textual representation of how the use a type is used up until the
function or global variable that constitutes an entry point in the API
corpus.  Here is an example of its use:

    test-read-write$ abilint --noout --show-type-use type-id-5 test17.xml
    Type ID 'type-id-5' is for type 'enum E'
    The usage graph for that type is:
    | -> enum E -> E S::m2 -> class S -> S* -> method void S::S()
    | -> enum E -> E S::m2 -> class S -> S* -> method void S::__base_ctor ()
    | -> enum E -> E S::m2 -> class S -> S* -> method void S::__comp_ctor ()
    | -> enum E -> E S::m2 -> class S -> S* -> method void S::S(S&)
    | -> enum E -> E S::m2 -> class S -> S* -> S& -> method void S::S(S&)
    | -> enum E -> E S::m2 -> class S -> S* -> S& -> S var
    | -> enum E -> E S::m2 -> class S -> S* -> S& -> E S::m2 -> class S -> S* -> method void S::S()
    | -> enum E -> E S::m2 -> class S -> S* -> S& -> E S::m2 -> class S -> S* -> method void S::__base_ctor ()
    | -> enum E -> E S::m2 -> class S -> S* -> S& -> E S::m2 -> class S -> S* -> method void S::__comp_ctor ()
    | -> enum E -> E S::m2 -> class S -> S* -> S& -> E S::m2 -> class S -> S* -> method void S::S(S&)
    | -> enum E -> E S::m2 -> class S -> S* -> S& -> E S::m2 -> class S -> S* -> S& -> method void S::S(S&)
    | -> enum E -> E S::m2 -> class S -> S* -> S& -> E S::m2 -> class S -> S* -> S& -> S var
    $

The screenshot above should be self explanatory.

This facility is useful to analyse type usage and find potential
issues in how libabigail represents some types.

To activate this feature, one needs to configure the package with the
configure option "--enable-show-type-use-in-abilint".

	* configure.ac: Define the --enable-show-type-use-in-abilint
	configure option.  It defines the WITH_SHOW_TYPE_USE_IN_ABILINT
	macro.
	* include/abg-reader.h (read_translation_unit): Add an overload
	that takes the read context.
	(get_types_from_type_id, get_artifact_used_by_relation_map):
	Declare new functions.
	* src/abg-reader.cc (get_types_from_type_id)
	(get_artifact_used_by_relation_map): Declare these functions as
	friend of the read_context type.
	(read_context::m_artifact_used_by_map):
	(read_context::key_type_decl): Replace the shared_ptr<type_base>
	type of the first parm by the equivalent type_base_sptr type.
	(read_context::{record_artifact_as_used_by,
	record_artifacts_as_used_in_fn_decl,
	record_artifacts_as_used_in_fn_type}): Add new methods guarded by
	the WITH_SHOW_TYPE_USE_IN_ABILINT macro.
	(get_types_from_type_id, get_artifact_used_by_relation_map): Define
	new functions guarded by the WITH_SHOW_TYPE_USE_IN_ABILINT macro.
	(read_translation_unit): Define new overload.
	(RECORD_ARTIFACT_AS_USED_BY, RECORD_ARTIFACTS_AS_USED_IN_FN_DECL)
	(RECORD_ARTIFACTS_AS_USED_IN_FN_TYPE): Define new macros.
	(build_function_decl, build_var_decl, build_qualified_type_decl)
	(build_pointer_type_def, build_reference_type_def)
	(build_function_type, build_array_type_def, build_enum_type_decl)
	(build_typedef_decl, build_class_decl, build_union_decl): Use the
	macros above to mark the relevant sub-types as used by the
	artifact being built.
	* tools/abilint.cc (struct artifact_use_relation_tree): Define new
	type, guarded by the WITH_SHOW_TYPE_USE_IN_ABILINT macro.
	(fill_artifact_use_tree, build_type_use_tree, emit_trace)
	(emit_artifact_use_trace, emit_artifact_use_trace)
	(show_how_type_is_used): Define static functions guarded by the
	WITH_SHOW_TYPE_USE_IN_ABILINT macro.
	(display_usage): Add doc string for the --show-type-use option,
	guarded by the WITH_SHOW_TYPE_USE_IN_ABILINT macro.
	(parse_command_line): Parse the --show-type-use option, guarded by
	the WITH_SHOW_TYPE_USE_IN_ABILINT macro.
	(main): Slight re-organisation to make the abixml file reading use
	a read_context.  That read context is then used to analyze how a
	given type is used whenever the --show-type-use option is used.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
---
 configure.ac         |  17 ++
 include/abg-reader.h |  13 +-
 src/abg-reader.cc    | 221 ++++++++++++++++++++--
 tools/abilint.cc     | 436 ++++++++++++++++++++++++++++++++++++++++++-
 4 files changed, 664 insertions(+), 23 deletions(-)
  

Patch

diff --git a/configure.ac b/configure.ac
index fc35a31a..97b3469f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -83,6 +83,12 @@  AC_ARG_ENABLE(debug-type-canonicalization,
 	      ENABLE_DEBUG_TYPE_CANONICALIZATION=$enableval,
 	      ENABLE_DEBUG_TYPE_CANONICALIZATION=no)
 
+AC_ARG_ENABLE(show-type-use-in-abilint,
+	      AS_HELP_STRING([--enable-show-type-use-in-abilint=yes|no],
+			     ['enable abilint --show-type-use'(default is no)]),
+	      ENABLE_SHOW_TYPE_USE_IN_ABILINT=$enableval,
+	      ENABLE_SHOW_TYPE_USE_IN_ABILINT=no)
+
 AC_ARG_ENABLE(deb,
 	      AS_HELP_STRING([--enable-deb=yes|no|auto],
 			     [enable the support of deb in abipkgdiff (default is auto)]),
@@ -373,6 +379,16 @@  fi
 
 AM_CONDITIONAL(ENABLE_DEBUG_SELF_COMPARISON, test x$ENABLE_DEBUG_SELF_COMPARISON = xyes)
 
+dnl enable support of abilint --show-type-use <type-id>
+if test x$ENABLE_SHOW_TYPE_USE_IN_ABILINT = xyes; then
+  AC_DEFINE([WITH_SHOW_TYPE_USE_IN_ABILINT], 1, [compile support of abilint --show-type-use])
+  AC_MSG_NOTICE([support of abilint --show-type-use <type-id> is enabled])
+else
+  AC_MSG_NOTICE([support of abilint --show-type-use <type-id> is disabled])
+fi
+
+AM_CONDITIONAL(ENABLE_SHOW_TYPE_USE_IN_ABILINT, test x$ENABLE_SHOW_TYPE_USE_IN_ABILINT = 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],
@@ -1011,6 +1027,7 @@  AC_MSG_NOTICE([
     libdw has the dwarf_getalt function            : ${FOUND_DWARF_GETALT_IN_LIBDW}
     Enable rpm support in abipkgdiff               : ${ENABLE_RPM}
     Enable rpm/zstd in abipkgdiff testing          : ${ENABLE_RPM_ZSTD}
+    Enable abilint --show-type-use <type-id>       : ${ENABLE_SHOW_TYPE_USE_IN_ABILINT}
     Enable self comparison debugging               : ${ENABLE_DEBUG_SELF_COMPARISON}
     Enable type canonicalization debugging         : ${ENABLE_DEBUG_TYPE_CANONICALIZATION}
     Enable deb support in abipkgdiff               : ${ENABLE_DEB}
diff --git a/include/abg-reader.h b/include/abg-reader.h
index a3aa0f85..78771c56 100644
--- a/include/abg-reader.h
+++ b/include/abg-reader.h
@@ -26,6 +26,8 @@  namespace xml_reader
 
 using namespace abigail::ir;
 
+class read_context;
+
 translation_unit_sptr
 read_translation_unit_from_file(const std::string&	file_path,
 				environment*		env);
@@ -38,7 +40,8 @@  translation_unit_sptr
 read_translation_unit_from_istream(std::istream*	in,
 				   environment*	env);
 
-class read_context;
+translation_unit_sptr
+read_translation_unit(read_context&);
 
 /// A convenience typedef for a shared pointer to read_context.
 typedef shared_ptr<read_context> read_context_sptr;
@@ -81,6 +84,14 @@  add_read_context_suppressions(read_context& ctxt,
 void
 consider_types_not_reachable_from_public_interfaces(read_context& ctxt,
 						    bool flag);
+
+#ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
+vector<type_base_sptr>*
+get_types_from_type_id(read_context&, const string&);
+
+unordered_map<type_or_decl_base*, vector<type_or_decl_base*>>*
+get_artifact_used_by_relation_map(read_context&);
+#endif
 }//end xml_reader
 
 #ifdef WITH_DEBUG_SELF_COMPARISON
diff --git a/src/abg-reader.cc b/src/abg-reader.cc
index 541ca179..7070fa9f 100644
--- a/src/abg-reader.cc
+++ b/src/abg-reader.cc
@@ -102,6 +102,12 @@  public:
 
   typedef unordered_map<xmlNodePtr, decl_base_sptr> xml_node_decl_base_sptr_map;
 
+  friend vector<type_base_sptr>* get_types_from_type_id(read_context&,
+							const string&);
+
+  friend unordered_map<type_or_decl_base*, vector<type_or_decl_base*>>*
+	 get_artifact_used_by_relation_map(read_context& ctxt);
+
 private:
   string						m_path;
   environment*						m_env;
@@ -120,6 +126,10 @@  private:
   suppr::suppressions_type				m_supprs;
   bool							m_tracking_non_reachable_types;
   bool							m_drop_undefined_syms;
+#ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
+  unordered_map<type_or_decl_base*,
+		vector<type_or_decl_base*>>		m_artifact_used_by_map;
+#endif
 
   read_context();
 
@@ -551,7 +561,7 @@  public:
   ///
   /// @return true upon successful completion.
   bool
-  key_type_decl(shared_ptr<type_base> type, const string& id)
+  key_type_decl(const type_base_sptr& type, const string& id)
   {
     if (!type)
       return false;
@@ -584,7 +594,7 @@  public:
     return true;
   }
 
-    /// Associate an ID to a class template.
+  /// Associate an ID to a class template.
   ///
   /// @param class_tmpl_decl the class template to consider.
   ///
@@ -607,6 +617,94 @@  public:
     return true;
   }
 
+#ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
+  /// Record that an artifact is used by another one.
+  ///
+  /// If a type is "used" by another one (as in the type is a sub-type
+  /// of another one), this function records that relation.
+  ///
+  /// @param used the type that is used.
+  ///
+  /// @param user the type that uses @p used.
+  void
+  record_artifact_as_used_by(type_or_decl_base* used,
+			     type_or_decl_base* user)
+  {
+    if (m_artifact_used_by_map.find(used) == m_artifact_used_by_map.end())
+      {
+	vector<type_or_decl_base*> v;
+	m_artifact_used_by_map[used] = v;
+      }
+    m_artifact_used_by_map[used].push_back(user);
+  }
+
+  /// Record that an artifact is used by another one.
+  ///
+  /// If a type is "used" by another one (as in the type is a sub-type
+  /// of another one), this function records that relation.
+  ///
+  /// @param used the type that is used.
+  ///
+  /// @param user the type that uses @p used.
+  void
+  record_artifact_as_used_by(const type_or_decl_base_sptr& used,
+			     const type_or_decl_base_sptr& user)
+  {record_artifact_as_used_by(used.get(), user.get());}
+
+  /// Record the sub-types of a fn-decl as being used by the fn-decl.
+  ///
+  /// @param fn the function decl to consider.
+  void
+  record_artifacts_as_used_in_fn_decl(const function_decl *fn)
+  {
+    if (!fn)
+      return;
+
+    type_base_sptr t = fn->get_return_type();
+    record_artifact_as_used_by(t.get(), const_cast<function_decl*>(fn));
+
+    for (auto pit : fn->get_parameters())
+      {
+	type_base_sptr t = pit->get_type();
+	record_artifact_as_used_by(t.get(), const_cast<function_decl*>(fn));
+      }
+  }
+
+  /// Record the sub-types of a function decl as being used by it.
+  ///
+  /// @param fn the function decl to consider.
+  void
+  record_artifacts_as_used_in_fn_decl(const function_decl_sptr& fn)
+  {record_artifacts_as_used_in_fn_decl(fn.get());}
+
+  /// Record the sub-types of a function type as being used by it.
+  ///
+  /// @param fn_type the function decl to consider.
+  void
+  record_artifacts_as_used_in_fn_type(const function_type *fn_type)
+  {
+    if (!fn_type)
+      return;
+
+    type_base_sptr t = fn_type->get_return_type();
+    record_artifact_as_used_by(t.get(), const_cast<function_type*>(fn_type));
+
+    for (auto pit : fn_type->get_parameters())
+      {
+	type_base_sptr t = pit->get_type();
+	record_artifact_as_used_by(t.get(),
+				   const_cast<function_type*>(fn_type));
+      }
+  }
+
+  /// Record the sub-types of a function type as being used by it.
+  ///
+  /// @param fn_type the function decl to consider.
+  void
+  record_artifacts_as_used_in_fn_type(const function_type_sptr& fn_type)
+  {record_artifacts_as_used_in_fn_type(fn_type.get());}
+#endif
+
   /// This function must be called on each declaration that is created during
   /// the parsing.  It adds the declaration to the current scope, and updates
   /// the state of the parsing context accordingly.
@@ -1138,10 +1236,8 @@  public:
 };// end class read_context
 
 static int	advance_cursor(read_context&);
-static bool
-read_translation_unit(read_context&, translation_unit&, xmlNodePtr);
-static translation_unit_sptr
-get_or_read_and_add_translation_unit(read_context&, xmlNodePtr);
+static bool read_translation_unit(read_context&, translation_unit&, xmlNodePtr);
+static translation_unit_sptr get_or_read_and_add_translation_unit(read_context&, xmlNodePtr);
 static translation_unit_sptr read_translation_unit_from_input(read_context&);
 static bool	read_symbol_db_from_input(read_context&,
 					  string_elf_symbols_map_sptr&,
@@ -1300,6 +1396,19 @@  static decl_base_sptr	handle_union_decl(read_context&, xmlNodePtr, bool);
 static decl_base_sptr	handle_function_tdecl(read_context&, xmlNodePtr, bool);
 static decl_base_sptr	handle_class_tdecl(read_context&, xmlNodePtr, bool);
 
+#ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
+#define RECORD_ARTIFACT_AS_USED_BY(ctxt, used, user) \
+  ctxt.record_artifact_as_used_by(used,user)
+#define RECORD_ARTIFACTS_AS_USED_IN_FN_DECL(ctxt, fn) \
+  ctxt.record_artifacts_as_used_in_fn_decl(fn)
+#define RECORD_ARTIFACTS_AS_USED_IN_FN_TYPE(ctxt, fn_type)\
+  ctxt.record_artifacts_as_used_in_fn_type(fn_type)
+#else
+#define RECORD_ARTIFACT_AS_USED_BY(ctxt, used, user)
+#define RECORD_ARTIFACTS_AS_USED_IN_FN_DECL(ctxt, fn)
+#define RECORD_ARTIFACTS_AS_USED_IN_FN_TYPE(ctxt, fn_type)
+#endif
+
 /// Get the IR node representing the scope for a given XML node.
 ///
 /// This function might trigger the building of a full sub-tree of IR.
@@ -1846,6 +1955,35 @@  consider_types_not_reachable_from_public_interfaces(read_context& ctxt,
 						    bool flag)
 {ctxt.tracking_non_reachable_types(flag);}
 
+#ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
+/// Get the vector of types that have a given type-id.
+///
+/// This function is available only if the project has been configured
+/// with --enable-show-type-use-in-abilint.
+///
+/// @param ctxt the abixml text reader context to use.
+///
+/// @param type_id the type-id to consider.
+vector<type_base_sptr>*
+get_types_from_type_id(read_context& ctxt, const string& type_id)
+{
+  auto it = ctxt.m_types_map.find(type_id);
+  if (it == ctxt.m_types_map.end())
+    return nullptr;
+  return &it->second;
+}
+
+/// Get the map that associates an artififact to its users.
+///
+/// This function is available only if the project has been configured
+/// with --enable-show-type-use-in-abilint.
+///
+/// @param ctxt the abixml text reader context to use.
+unordered_map<type_or_decl_base*, vector<type_or_decl_base*>>*
+get_artifact_used_by_relation_map(read_context& ctxt)
+{return &ctxt.m_artifact_used_by_map;}
+#endif
+
 /// Read the "version" attribute from the current XML element which is
 /// supposed to be a corpus or a corpus group and set the format
 /// version to the corpus object accordingly.
@@ -2219,6 +2357,23 @@  read_translation_unit_from_buffer(const string&	buffer,
   return tu;
 }
 
+/// Parse a translation unit from an abixml input from a given
+/// context.
+///
+/// @param ctxt the @ref read_context to consider.
+///
+/// @return the constructed @ref translation_unit from the content of
+/// the input abixml.
+translation_unit_sptr
+read_translation_unit(read_context& ctxt)
+{
+  translation_unit_sptr tu = read_translation_unit_from_input(ctxt);
+  ctxt.get_environment()->canonicalization_is_done(false);
+  ctxt.perform_late_type_canonicalizing();
+  ctxt.get_environment()->canonicalization_is_done(true);
+  return tu;
+}
+
 /// This function is called by @ref read_translation_unit_from_input.
 /// It handles the current xml element node of the reading context.
 /// The result of the "handling" is to build the representation of the
@@ -3407,6 +3562,7 @@  build_function_decl(read_context&	ctxt,
 
   maybe_set_artificial_location(ctxt, node, fn_decl);
   ctxt.push_decl_to_current_scope(fn_decl, add_to_current_scope);
+  RECORD_ARTIFACTS_AS_USED_IN_FN_DECL(ctxt, fn_decl);
 
   elf_symbol_sptr sym = build_elf_symbol_from_reference(ctxt, node);
   if (sym)
@@ -3645,6 +3801,10 @@  build_var_decl(read_context&	ctxt,
     decl->set_symbol(sym);
 
   ctxt.push_decl_to_current_scope(decl, add_to_current_scope);
+  if (add_to_current_scope)
+    // This variable is really being kept in the IR, so let's record
+    // that it's using its type.
+    RECORD_ARTIFACT_AS_USED_BY(ctxt, underlying_type, decl);
 
   if (decl->get_symbol() && decl->get_symbol()->is_public())
     decl->set_is_in_public_symbol_table(true);
@@ -3813,6 +3973,7 @@  build_qualified_type_decl(read_context&	ctxt,
       decl.reset(new qualified_type_def(underlying_type, cv, loc));
       maybe_set_artificial_location(ctxt, node, decl);
       ctxt.push_and_key_type_decl(decl, id, add_to_current_scope);
+      RECORD_ARTIFACT_AS_USED_BY(ctxt, underlying_type, decl);
     }
 
   ctxt.map_xml_node_to_decl(node, decl);
@@ -3891,7 +4052,7 @@  build_pointer_type_def(read_context&	ctxt,
   ABG_ASSERT(pointed_to_type);
 
   t->set_pointed_to_type(pointed_to_type);
-
+  RECORD_ARTIFACT_AS_USED_BY(ctxt, pointed_to_type, t);
   return t;
 }
 
@@ -3970,6 +4131,7 @@  build_reference_type_def(read_context&		ctxt,
     ctxt.build_or_get_type_decl(type_id,/*add_to_current_scope=*/ true);
   ABG_ASSERT(pointed_to_type);
   t->set_pointed_to_type(pointed_to_type);
+  RECORD_ARTIFACT_AS_USED_BY(ctxt, pointed_to_type, t);
 
   return t;
 }
@@ -4034,6 +4196,7 @@  build_function_type(read_context&	ctxt,
 
   ctxt.get_translation_unit()->bind_function_type_life_time(fn_type);
   ctxt.key_type_decl(fn_type, id);
+  RECORD_ARTIFACTS_AS_USED_IN_FN_TYPE(ctxt, fn_type);
 
   for (xmlNodePtr n = xmlFirstElementChild(node);
        n;
@@ -4291,6 +4454,7 @@  build_array_type_def(read_context&	ctxt,
   maybe_set_artificial_location(ctxt, node, ar_type);
   if (ctxt.push_and_key_type_decl(ar_type, id, add_to_current_scope))
     ctxt.map_xml_node_to_decl(node, ar_type);
+  RECORD_ARTIFACT_AS_USED_BY(ctxt, type, ar_type);
 
   if (dimensions != ar_type->get_dimension_count()
       || (alignment_in_bits
@@ -4472,6 +4636,7 @@  build_enum_type_decl(read_context&	ctxt,
     {
       maybe_set_naming_typedef(ctxt, node, t);
       ctxt.map_xml_node_to_decl(node, t);
+      RECORD_ARTIFACT_AS_USED_BY(ctxt, underlying_type, t);
       return t;
     }
 
@@ -4534,6 +4699,7 @@  build_typedef_decl(read_context&	ctxt,
   maybe_set_artificial_location(ctxt, node, t);
   ctxt.push_and_key_type_decl(t, id, add_to_current_scope);
   ctxt.map_xml_node_to_decl(node, t);
+  RECORD_ARTIFACT_AS_USED_BY(ctxt, underlying_type, t);
 
   return t;
 }
@@ -4897,6 +5063,21 @@  build_class_decl(read_context&		ctxt,
 					    offset_in_bits);
 		      if (is_static)
 			ctxt.maybe_add_var_to_exported_decls(v.get());
+		      // Now let's record the fact that the data
+		      // member uses its type and that the class being
+		      // built uses the data member.
+		      if (is_anonymous_data_member(v))
+			// This data member is anonymous so recording
+			// that it uses its type is useless because we
+			// can't name it.  Rather, let's record that
+			// the class being built uses the type of the
+			// (anonymous) data member.
+			RECORD_ARTIFACT_AS_USED_BY(ctxt, v->get_type(), decl);
+		      else
+			{
+			  RECORD_ARTIFACT_AS_USED_BY(ctxt, v->get_type(), v);
+			  RECORD_ARTIFACT_AS_USED_BY(ctxt, v, decl);
+			}
 		    }
 		}
 	    }
@@ -5243,10 +5424,27 @@  build_union_decl(read_context& ctxt,
 		    }
 		  if (!is_static
 		      || !variable_is_suppressed(ctxt, decl.get(), *v))
-		    decl->add_data_member(v, access,
-					  is_laid_out,
-					  is_static,
-					  offset_in_bits);
+		    {
+		      decl->add_data_member(v, access,
+					    is_laid_out,
+					    is_static,
+					    offset_in_bits);
+		      // Now let's record the fact that the data
+		      // member uses its type and that the union being
+		      // built uses the data member.
+		      if (is_anonymous_data_member(v))
+			// This data member is anonymous so recording
+			// that it uses its type is useless because we
+			// can't name it.  Rather, let's record that
+			// the class being built uses the type of the
+			// (anonymous) data member.
+			RECORD_ARTIFACT_AS_USED_BY(ctxt, v->get_type(), decl);
+		      else
+			{
+			  RECORD_ARTIFACT_AS_USED_BY(ctxt, v->get_type(), v);
+			  RECORD_ARTIFACT_AS_USED_BY(ctxt, v, decl);
+			}
+		    }
 		}
 	    }
 	}
@@ -5526,7 +5724,6 @@  build_type_tparameter(read_context&		ctxt,
   return result;
 }
 
-
 /// Build a tmpl_parm_type_composition from a
 /// "template-parameter-type-composition" xml element node.
 ///
diff --git a/tools/abilint.cc b/tools/abilint.cc
index efddd77b..ba2de634 100644
--- a/tools/abilint.cc
+++ b/tools/abilint.cc
@@ -41,6 +41,8 @@  using std::cout;
 using std::ostream;
 using std::ofstream;
 using std::vector;
+using std::unordered_set;
+using std::unique_ptr;
 using abigail::tools_utils::emit_prefix;
 using abigail::tools_utils::check_file;
 using abigail::tools_utils::file_type;
@@ -48,6 +50,10 @@  using abigail::tools_utils::guess_file_type;
 using abigail::suppr::suppression_sptr;
 using abigail::suppr::suppressions_type;
 using abigail::suppr::read_suppressions;
+using abigail::type_base;
+using abigail::type_or_decl_base;
+using abigail::type_base_sptr;
+using abigail::type_or_decl_base_sptr;
 using abigail::corpus;
 using abigail::corpus_sptr;
 using abigail::xml_reader::read_translation_unit_from_file;
@@ -55,6 +61,10 @@  using abigail::xml_reader::read_translation_unit_from_istream;
 using abigail::xml_reader::read_corpus_from_native_xml;
 using abigail::xml_reader::read_corpus_from_native_xml_file;
 using abigail::xml_reader::read_corpus_group_from_input;
+#ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
+using abigail::xml_reader::get_types_from_type_id;
+using abigail::xml_reader::get_artifact_used_by_relation_map;
+#endif
 using abigail::dwarf_reader::read_corpus_from_elf;
 using abigail::xml_writer::write_translation_unit;
 using abigail::xml_writer::write_context_sptr;
@@ -78,6 +88,9 @@  struct options
   vector<string>		suppression_paths;
   string			headers_dir;
   vector<string>		header_files;
+#if WITH_SHOW_TYPE_USE_IN_ABILINT
+  string			type_id_to_show;
+#endif
 
   options()
     : display_version(false),
@@ -92,6 +105,374 @@  struct options
   {}
 };//end struct options;
 
+#ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
+/// A tree node representing the "use" relation between an artifact A
+/// (e.g, a type) and a set of artifacts {A'} that use "A" as in "A"
+/// is a sub-type of A'.
+///
+/// So the node contains the artifact A and a vector children nodes
+/// that contain the A' artifacts that use A.
+struct artifact_use_relation_tree
+{
+  artifact_use_relation_tree *root_node = nullptr;
+  /// The parent node of this one.  Is nullptr if this node is the root
+  /// node.
+  artifact_use_relation_tree *parent = nullptr;
+  /// The artifact contained in this node.
+  type_or_decl_base* artifact = nullptr;
+  /// The vector of children nodes that carry the artifacts that
+  /// actually use the 'artifact' above.  In other words, the
+  /// 'artifact" data member above is a sub-type of each artifact
+  /// contained in this vector.
+  vector<unique_ptr<artifact_use_relation_tree>> artifact_users;
+  /// This is the set of artifacts that have been added to the tree.
+  /// This is useful to ensure that all artifacts are added just once
+  /// in the tree to prevent infinite loops.
+  unordered_set<type_or_decl_base *> artifacts;
+
+  /// The constructor of the tree node.
+  ///
+  /// @param the artifact to consider.
+  artifact_use_relation_tree(type_or_decl_base* t)
+    : artifact (t)
+  {
+    ABG_ASSERT(t && !artifact_in_tree(t));
+    record_artifact(t);
+  }
+
+  /// Add a user artifact node for the artifact carried by this node.
+  ///
+  /// The artifact carried by the current node is a sub-type of the
+  /// artifact carried by the 'user' node being added.
+  ///
+  /// @param user a tree node that carries an artifact that uses the
+  /// artifact carried by the current node.
+  void
+  add_artifact_user(artifact_use_relation_tree *user)
+  {
+    ABG_ASSERT(user && !artifact_in_tree(user->artifact ));
+    artifact_users.push_back(unique_ptr<artifact_use_relation_tree>(user));
+    user->parent = this;
+    record_artifact(user->artifact);
+  }
+
+  /// Move constructor.
+  ///
+  /// @param o the source of the move.
+  artifact_use_relation_tree(artifact_use_relation_tree &&o)
+  {
+    parent = o.parent;
+    artifact = o.artifact;
+    artifact_users = std::move(o.artifact_users);
+    artifacts = std::move(o.artifacts);
+  }
+
+  /// Move assignment operator.
+  ///
+  /// @param o the source of the assignment.
+  artifact_use_relation_tree& operator=(artifact_use_relation_tree&& o)
+  {
+    parent = o.parent;
+    artifact = o.artifact;
+    artifact_users = std::move(o.artifact_users);
+    artifacts = std::move(o.artifacts);
+    return *this;
+  }
+
+  /// Test if the current node is a leaf node.
+  ///
+  /// @return true if the artifact carried by the current node has no
+  /// user artifacts.
+  bool
+  is_leaf() const
+  {return artifact_users.empty();}
+
+  /// Test if the current node is a root node.
+  ///
+  /// @return true if the current artifact uses no other artifact.
+  bool
+  is_root() const
+  {return parent == nullptr;}
+
+  /// Test wether a given artifact has been added to the tree.
+  ///
+  /// Here, the tree means the tree that the current tree node is part
+  /// of.
+  ///
+  /// An artifact is considered as having been added to the tree if
+  /// artifact_use_relation_tree::record_artifact has been invoked on
+  /// it.
+  ///
+  /// @param artifact the artifact to consider.
+  ///
+  /// @return true iff @p artifact is present in the tree.
+  bool
+  artifact_in_tree(type_or_decl_base *artifact)
+  {
+    artifact_use_relation_tree *root_node = get_root_node();
+    ABG_ASSERT(root_node);
+    return root_node->artifacts.find(artifact) != root_node->artifacts.end();
+  }
+
+  /// Record an artifact as being added to the current tree.
+  ///
+  /// Note that this function assumes the artifact is not already
+  /// present in the tree containing the current tree node.
+  ///
+  /// @param artifact the artifact to consider.
+  void
+  record_artifact(type_or_decl_base *artifact)
+  {
+    ABG_ASSERT(!artifact_in_tree(artifact));
+    artifact_use_relation_tree *root_node = get_root_node();
+    ABG_ASSERT(root_node);
+    root_node->artifacts.insert(artifact);
+  }
+
+  /// Get the root node of the current tree.
+  ///
+  /// @return the root node of the current tree.
+  artifact_use_relation_tree*
+  get_root_node()
+  {
+    if (root_node)
+      return root_node;
+
+    if (parent == nullptr)
+      return this;
+
+    root_node = parent->get_root_node();
+    return root_node;
+  }
+
+  artifact_use_relation_tree(const artifact_use_relation_tree&) = delete;
+  artifact_use_relation_tree& operator=(const artifact_use_relation_tree&) = delete;
+}; // end struct artifact_use_relation_tree
+
+/// Fill an "artifact use" tree from a map that associates a type T
+/// (or artifact) to artifacts that use T as a sub-type.
+///
+/// @param artifact_use_rel the map that establishes the relation
+/// between a type T and the artifacts that use T as a sub-type.
+///
+/// @parm tree output parameter.  This function will fill up this tree
+/// from the information carried in @p artifact_use_rel.  Each node of
+/// the tree contains an artifact A and its children nodes contain the
+/// artifacts A' that use A as a sub-type.
+static void
+fill_artifact_use_tree(const std::unordered_map<type_or_decl_base*,
+						vector<type_or_decl_base*>>& artifact_use_rel,
+		       artifact_use_relation_tree& tree)
+{
+  auto r = artifact_use_rel.find(tree.artifact);
+  if (r == artifact_use_rel.end())
+    return;
+
+  // Walk the users of "artifact", create a tree node for each one of
+  // them, and add them as children node of the current tree node
+  // named 'tree'.
+  for (auto user : r->second)
+    {
+      if (tree.artifact_in_tree(user))
+	// The artifact has already been added to the tree, so skip it
+	// otherwise we can loop for ever.
+	continue;
+
+      artifact_use_relation_tree *user_tree =
+	new artifact_use_relation_tree(user);
+
+      // Now add the new user node as a child of the current tree
+      // node.
+      tree.add_artifact_user(user_tree);
+
+      // Recursively fill the newly created tree node.
+      fill_artifact_use_tree(artifact_use_rel, *user_tree);
+    }
+}
+
+/// construct an "artifact use tree" for a type designated by a "type-id".
+/// (or artifact) to artifacts that use T as a sub-type.
+///
+/// Each node of the "artifact use tree" contains a type T and its
+/// children nodes contain the artifacts A' that use T as a sub-type.
+/// The root node is the type designed by a given type-id.
+///
+/// @param ctxt the abixml read context to consider.
+///
+/// @param type_id the type-id of the type to construct the "use tree"
+/// for.
+static unique_ptr<artifact_use_relation_tree>
+build_type_use_tree(abigail::xml_reader::read_context &ctxt,
+		    const string& type_id)
+{
+  unique_ptr<artifact_use_relation_tree> result;
+  vector<type_base_sptr>* types = get_types_from_type_id(ctxt, type_id);
+  if (!types)
+    return result;
+
+  std::unordered_map<type_or_decl_base*, vector<type_or_decl_base*>>*
+    artifact_use_rel = get_artifact_used_by_relation_map(ctxt);
+  if (!artifact_use_rel)
+    return result;
+
+  type_or_decl_base_sptr type = types->front();
+  unique_ptr<artifact_use_relation_tree> use_tree
+    (new artifact_use_relation_tree(type.get()));
+
+  fill_artifact_use_tree(*artifact_use_rel, *use_tree);
+
+  result = std::move(use_tree);
+  return result;
+}
+
+/// Emit a visual representation of a "type use trace".
+///
+/// The trace is vector of strings.  Each string is the textual
+/// representation of a type.  The next element in the vector is a
+/// type using the previous element, as in, the "previous element is a
+/// sub-type of the next element".
+///
+/// This is a sub-routine of emit_artifact_use_trace.
+///
+/// @param the trace vector to emit.
+///
+/// @param out the output stream to emit the trace to.
+static void
+emit_trace(const vector<string>& trace, ostream& out)
+{
+  if (trace.empty())
+    return;
+
+  if (!trace.empty())
+    // Make the beginning of the trace line of the usage of a given
+    // type be easily recognizeable by a "pattern".
+    out << "===";
+
+  for (auto element : trace)
+    out << "-> " << element << " ";
+
+  if (!trace.empty())
+    // Make the end of the trace line of the usage of a given type be
+    // easily recognizeable by another "pattern".
+    out << " <-~~~";
+
+  out << "\n";
+}
+
+/// Walk a @ref artifact_use_relation_tree to emit a "type-is-used-by"
+/// trace.
+///
+/// The tree carries the information about how a given type is used by
+/// other types.  This function walks the tree by visiting a node
+/// carrying a given type T, and then the nodes for which T is a
+/// sub-type.  The function accumulates a trace made of the textual
+/// representation of the visited nodes and then emits that trace on
+/// an output stream.
+///
+/// @param artifact_use_tree the tree to walk.
+///
+/// @param trace the accumulated vector of the textual representations
+/// of the types carried by the visited nodes.
+///
+/// @param out the output stream to emit the trace to.
+static void
+emit_artifact_use_trace(const artifact_use_relation_tree& artifact_use_tree,
+			vector<string>& trace, ostream& out)
+{
+  type_or_decl_base* artifact = artifact_use_tree.artifact;
+  if (!artifact)
+    return;
+
+  string repr = artifact->get_pretty_representation();
+  trace.push_back(repr);
+
+  if (artifact_use_tree.artifact_users.empty())
+    {
+      // We reached a leaf node.  This means that no other artifact
+      // uses the artifact carried by this leaf node.  So, we want to
+      // emit the trace accumulated to this point.
+
+      // But we only want to emit the usage traces that end up with a
+      // function of variable that have an associated ELF symbol.
+      bool do_emit_trace = false;
+      if (is_decl(artifact))
+	{
+	  if (abigail::ir::var_decl* v = is_var_decl(artifact))
+	    if (v->get_symbol())
+	      do_emit_trace = true;
+	  if (abigail::ir::function_decl* f = is_function_decl(artifact))
+	    if (f->get_symbol())
+	      do_emit_trace = true;
+	}
+
+      // OK now, really emit the trace.
+      if (do_emit_trace)
+	emit_trace(trace, out);
+
+      trace.pop_back();
+      return;
+    }
+
+  for (const auto &user : artifact_use_tree.artifact_users)
+    emit_artifact_use_trace(*user, trace, out);
+
+  trace.pop_back();
+}
+
+/// Walk a @ref artifact_use_relation_tree to emit a "type-is-used-by"
+/// trace.
+///
+/// The tree carries the information about how a given type is used by
+/// other types.  This function walks the tree by visiting a node
+/// carrying a given type T, and then the nodes for which T is a
+/// sub-type.  The function then emits a trace of how the root type is
+/// used.
+///
+/// @param artifact_use_tree the tree to walk.
+///
+/// @param out the output stream to emit the trace to.
+static void
+emit_artifact_use_trace(const artifact_use_relation_tree& artifact_use_tree,
+			ostream& out)
+{
+  vector<string> trace;
+  emit_artifact_use_trace(artifact_use_tree, trace, out);
+}
+
+/// Show how a type is used.
+///
+/// The type to consider is designated by a type-id string that is
+/// carried by the options data structure.
+///
+/// @param ctxt the abixml read context to consider.
+///
+/// @param the type_id of the type which usage to analyse.
+static bool
+show_how_type_is_used(abigail::xml_reader::read_context &ctxt,
+		      const string& type_id)
+{
+  if (type_id.empty())
+    return false;
+
+  unique_ptr<artifact_use_relation_tree> use_tree =
+    build_type_use_tree(ctxt, type_id);
+  if (!use_tree)
+    return false;
+
+  // Now walk the use_tree to emit the type use trace
+  if (use_tree->artifact)
+    {
+      std::cout << "Type ID '"
+		<< type_id << "' is for type '"
+		<< use_tree->artifact->get_pretty_representation()
+		<< "'\n"
+		<< "The usage graph for that type is:\n";
+      emit_artifact_use_trace(*use_tree, std::cout);
+    }
+  return true;
+}
+#endif // WITH_SHOW_TYPE_USE_IN_ABILINT
+
 static void
 display_usage(const string& prog_name, ostream& out)
 {
@@ -112,6 +493,9 @@  display_usage(const string& prog_name, ostream& out)
     << "  --tu  expect a single translation unit file\n"
 #ifdef WITH_CTF
     << "  --ctf use CTF instead of DWARF in ELF files\n"
+#endif
+#ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
+    << "  --show-type-use <type-id>  show how a type is used from the abixml file\n"
 #endif
     ;
 }
@@ -195,6 +579,15 @@  parse_command_line(int argc, char* argv[], options& opts)
 	  opts.diff = true;
 	else if (!strcmp(argv[i], "--noout"))
 	  opts.noout = true;
+#ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
+      else if (!strcmp(argv[i], "--show-type-use"))
+	{
+	  ++i;
+	  if (i >= argc || argv[i][0] == '-')
+	    return false;
+	  opts.type_id_to_show = argv[i];
+	}
+#endif
 	else
 	  {
 	    if (strlen(argv[i]) >= 2 && argv[i][0] == '-' && argv[i][1] == '-')
@@ -203,8 +596,17 @@  parse_command_line(int argc, char* argv[], options& opts)
 	  }
       }
 
-    if (opts.file_path.empty())
+#ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
+    if (!opts.type_id_to_show.empty()
+	&& opts.file_path.empty())
+      emit_prefix(argv[0], cout)
+	<< "WARNING: --show-type-use <type-id> "
+	"must be accompanied with an abixml file\n";
+
+    if (opts.file_path.empty()
+	&& opts.type_id_to_show.empty())
       opts.read_from_stdin = true;
+#endif
 
     if (opts.read_from_stdin && !opts.file_path.empty())
     {
@@ -349,6 +751,7 @@  main(int argc, char* argv[])
       abigail::elf_reader::status s = abigail::elf_reader::STATUS_OK;
       char* di_root_path = 0;
       file_type type = guess_file_type(opts.file_path);
+      abigail::xml_reader::read_context_sptr abixml_read_ctxt;
 
       switch (type)
 	{
@@ -358,7 +761,12 @@  main(int argc, char* argv[])
 	    << "\n";
 	  return 1;
 	case abigail::tools_utils::FILE_TYPE_NATIVE_BI:
-	  tu = read_translation_unit_from_file(opts.file_path, env.get());
+	  {
+	    abixml_read_ctxt =
+	      abigail::xml_reader::create_native_xml_read_context(opts.file_path,
+								  env.get());
+	    tu = read_translation_unit(*abixml_read_ctxt);
+	  }
 	  break;
 	case abigail::tools_utils::FILE_TYPE_ELF:
 	case abigail::tools_utils::FILE_TYPE_AR:
@@ -391,22 +799,22 @@  main(int argc, char* argv[])
 	  break;
 	case abigail::tools_utils::FILE_TYPE_XML_CORPUS:
 	  {
-	    abigail::xml_reader::read_context_sptr ctxt =
+	    abixml_read_ctxt =
 	      abigail::xml_reader::create_native_xml_read_context(opts.file_path,
 								  env.get());
-	    assert(ctxt);
-	    set_suppressions(*ctxt, opts);
-	    corp = read_corpus_from_input(*ctxt);
+	    assert(abixml_read_ctxt);
+	    set_suppressions(*abixml_read_ctxt, opts);
+	    corp = read_corpus_from_input(*abixml_read_ctxt);
 	    break;
 	  }
 	case abigail::tools_utils::FILE_TYPE_XML_CORPUS_GROUP:
 	  {
-	    abigail::xml_reader::read_context_sptr ctxt =
+	    abixml_read_ctxt =
 	      abigail::xml_reader::create_native_xml_read_context(opts.file_path,
 								  env.get());
-	    assert(ctxt);
-	    set_suppressions(*ctxt, opts);
-	    group = read_corpus_group_from_input(*ctxt);
+	    assert(abixml_read_ctxt);
+	    set_suppressions(*abixml_read_ctxt, opts);
+	    group = read_corpus_group_from_input(*abixml_read_ctxt);
 	  }
 	  break;
 	case abigail::tools_utils::FILE_TYPE_RPM:
@@ -517,6 +925,14 @@  main(int argc, char* argv[])
 	    is_ok = false;
 	}
 
+#ifdef WITH_SHOW_TYPE_USE_IN_ABILINT
+      if (is_ok
+	  && !opts.type_id_to_show.empty())
+	{
+	  ABG_ASSERT(abixml_read_ctxt);
+	  show_how_type_is_used(*abixml_read_ctxt, opts.type_id_to_show);
+	}
+#endif
       return is_ok ? 0 : 1;
     }