From patchwork Mon Feb 28 09:28:44 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dodji Seketeli X-Patchwork-Id: 51433 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 11CAB385843D for ; Mon, 28 Feb 2022 09:28:58 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 11CAB385843D DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1646040538; bh=NAXjDoyg2gh++XjpCeGvPE/stvvhyA7SdQibpNaw8zc=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Help: List-Subscribe:From:Reply-To:From; b=apmzqdd1iqmPhcrhQbsF5hvMWfNqmAy3CCnggBEw6RKxlu1vS3PKuu7XKeBruxFAm xBZS/fJUGh7olUF8TjPI61FPco2j8cmM+n7RyJYgaQ++T1KJw+uc+ZUQrKU0WjzaQd uhwsj0HSlkBBBC7i03Rnk/BzHHZEh9WDmWkLgTUI= X-Original-To: libabigail@sourceware.org Delivered-To: libabigail@sourceware.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.129.124]) by sourceware.org (Postfix) with ESMTPS id 7A9C73858C20 for ; Mon, 28 Feb 2022 09:28:50 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 7A9C73858C20 Received: from mail-wr1-f72.google.com (mail-wr1-f72.google.com [209.85.221.72]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-317-tIJYdMMMPgCOj042tCuLyg-1; Mon, 28 Feb 2022 04:28:48 -0500 X-MC-Unique: tIJYdMMMPgCOj042tCuLyg-1 Received: by mail-wr1-f72.google.com with SMTP id f14-20020adfc98e000000b001e8593b40b0so1812057wrh.14 for ; Mon, 28 Feb 2022 01:28:48 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:subject:organization:date:message-id :user-agent:mime-version; bh=NAXjDoyg2gh++XjpCeGvPE/stvvhyA7SdQibpNaw8zc=; b=mwocZAgENr+hyt72h4lgtlBzqGfnl6uLzGLRaNhWSH+s5RZRSrsQF0T+yWQtDsFLd2 uwfyEboJNj/YaikQ1i+LlVB2JlorhjVmok21UTpHtkGQDRuSWTYDejkPirCNRpW3sPHB 5Mg95vaES8ESgmG7Ck8c3AkU2ytH07qiE1EhJ3OQ/sxvn9nloV6QxEEIcn4ets6SXCjz 3E88ygtXHUXVKvCIQLqGiTrhUTXW8iYscqMJoAprGi8oX9rR11/p+L7h8jnMNbWTsZ+X jMTShNEgnFIYXYiD3vicLYilZdHcYKnw/t+tAHIeBd1zSgV/DBA1lMaMOyeHKKHCtHIF IZTw== X-Gm-Message-State: AOAM530kZ6K0qDLuzy8BG6Q5J1KmhJQaR48vytpc1O9uvNdYbA1QyLms myxmPCbBB268J8nkuAii0Dupkh/ZsJCu/hzGHtDr5Endmuk3U1Sc15l6oQmpIVR2pcrAL29qOh0 zpcUiY8jpFHse653gjO7vSTOka024UCh5qAW/MX0BvQGmKObetJaZXaxoBIV/SKNvACVZ X-Received: by 2002:a05:6000:184c:b0:1ef:f8a2:3283 with SMTP id c12-20020a056000184c00b001eff8a23283mr280379wri.467.1646040526068; Mon, 28 Feb 2022 01:28:46 -0800 (PST) X-Google-Smtp-Source: ABdhPJxo7g+KGn893mCxhYSNQW30lMLmiJz6mgY0aNSdAW7tiwxKVJBs4uubT+iftk4X3tQNcwEVPA== X-Received: by 2002:a05:6000:184c:b0:1ef:f8a2:3283 with SMTP id c12-20020a056000184c00b001eff8a23283mr280341wri.467.1646040525274; Mon, 28 Feb 2022 01:28:45 -0800 (PST) Received: from localhost ([88.120.130.27]) by smtp.gmail.com with ESMTPSA id x16-20020adff0d0000000b001ea81265a5fsm13380989wro.99.2022.02.28.01.28.44 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 28 Feb 2022 01:28:44 -0800 (PST) Received: by localhost (Postfix, from userid 1000) id 7A3B45802B4; Mon, 28 Feb 2022 10:28:44 +0100 (CET) To: libabigail@sourceware.org Subject: [PATCH, applied] abilint: add the --show-type-use option Organization: Red Hat / France X-Operating-System: Fedora 36 X-URL: http://www.redhat.com Date: Mon, 28 Feb 2022 10:28:44 +0100 Message-ID: <871qznl4qb.fsf@redhat.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (gnu/linux) MIME-Version: 1.0 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com X-Spam-Status: No, score=-12.2 required=5.0 tests=BAYES_00, DKIMWL_WL_HIGH, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H5, RCVD_IN_MSPIKE_WL, SPF_HELO_NONE, SPF_NONE, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: libabigail@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Mailing list of the Libabigail project List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-Patchwork-Original-From: Dodji Seketeli via Libabigail From: Dodji Seketeli Reply-To: Dodji Seketeli Errors-To: libabigail-bounces+patchwork=sourceware.org@sourceware.org Sender: "Libabigail" Hello, "abilint --show-type-use " 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 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 --- configure.ac | 17 ++ include/abg-reader.h | 13 +- src/abg-reader.cc | 221 ++++++++++++++++++++-- tools/abilint.cc | 436 ++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 664 insertions(+), 23 deletions(-) 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 +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 is enabled]) +else + AC_MSG_NOTICE([support of abilint --show-type-use 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 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 : ${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_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* +get_types_from_type_id(read_context&, const string&); + +unordered_map>* +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 xml_node_decl_base_sptr_map; + friend vector* get_types_from_type_id(read_context&, + const string&); + + friend unordered_map>* + 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> 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, 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 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(fn)); + + for (auto pit : fn->get_parameters()) + { + type_base_sptr t = pit->get_type(); + record_artifact_as_used_by(t.get(), const_cast(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(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(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* +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>* +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 suppression_paths; string headers_dir; vector 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> 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 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(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>& 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 +build_type_use_tree(abigail::xml_reader::read_context &ctxt, + const string& type_id) +{ + unique_ptr result; + vector* types = get_types_from_type_id(ctxt, type_id); + if (!types) + return result; + + std::unordered_map>* + 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 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& 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& 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 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 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 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 " + "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; }