@@ -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}
@@ -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
@@ -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.
///
@@ -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;
}