From: Dodji Seketeli Date: Wed, 13 Nov 2019 10:04:18 +0000 (+0100) Subject: Bug 24690 - Support comparing non-reachable types of a binary X-Git-Tag: upstream/1.7~34 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=c32b8ec9f3566c5abbfcf17c2185a7f83aba4653;p=platform%2Fupstream%2Flibabigail.git Bug 24690 - Support comparing non-reachable types of a binary This patch adds the ability to compare all types of a binary, including those types that are not reachable from global functions and variables. This implies that for types that are not reachable from public interfaces, we want compare them against each others directly, without first comparing global functions/variables and walking the graph of reachable types from there. The patch adds the --non-reachable-types option to abidiff and abipkgdiff, instructing them to also compare types that are non-reachable from global variables and functions. Using that option, for instance, here is what the summary of abipkgdiff now looks like, in the test case attached added by this patch: ================ changes of 'libflatpak.so.0.10204.0'=============== Functions changes summary: 0 Removed, 0 Changed (16 filtered out), 16 Added functions Variables changes summary: 0 Removed, 0 Changed, 0 Added variable Unreachable types summary: 3 removed (2 filtered out), 1 changed (15 filtered out), 3 added (1 filtered out) types You can see that there is a new summary line which starts with the string: "Unreachable types summary:" Then in the body of the report, those unreachable types are reported separately. In practise, we want to limit the unreachable types to compare somehow, otherwise we'll end up comparing all the types of the types of the binary and that can be huge. So we want to limit the unreachable type analysis to types that are defined in public headers. So, for abipkgdiff, one can limit the analysis of non-reachable types to those defined in public headers by supplying the --devel{1,2} options that specifies the development packages that contain said public headers. For abidiff however, you'll want to use the --headers-dir{1,2} options for that. The patch comes with appropriate regression tests. * include/abg-comparison.h (string_type_base_sptr_map): Define new typedef. (diff_context::show_unreachable_types): Declare new member functions. (corpus_diff::{deleted_unreachable_types, deleted_unreachable_types_sorted, added_unreachable_types, added_unreachable_types_sorted, changed_unreachable_types, changed_unreachable_types_sorted}): Likewise. (maybe_report_unreachable_type_changes): Declare this function a friend of class corpus_diff. (corpus_diff::diff_stats::{num_added_unreachable_types, num_added_unreachable_types_filtered_out, net_num_added_unreachable_types, num_removed_unreachable_types, num_removed_unreachable_types_filtered_out, net_num_removed_unreachable_types, num_changed_unreachable_types, num_changed_unreachable_types_filtered_out, net_num_changed_unreachable_types}): Likewise. * src/abg-comparison-priv.h (diff_context::priv::show_unreachable_types_): Define new data member. (diff_context::priv::priv): Initialize the new data member. (diff_comp::operator()): Use pretty representation of diff subjects to sort them, rather than just their name. Also, add comment to the other member functions of diff_comp. (corpus_diff::{unreachable_types_edit_script_, deleted_unreachable_types_, deleted_unreachable_types_sorted_, suppressed_deleted_unreachable_types_, added_unreachable_types_, added_unreachable_types_sorted_, suppressed_added_unreachable_types_, changed_unreachable_types_, changed_unreachable_types_sorted_}): Define new data members. (corpus_diff::priv::apply_supprs_to_added_removed_fns_vars_unreachable_types): Changed the name of corpus_diff::priv::apply_suppressions_to_added_removed_fns_vars into this. (corpus_diff::priv::{added_unreachable_type_is_suppressed, deleted_unreachable_type_is_suppressed, changed_unreachable_types_sorted, count_unreachable_types}): Declare new member functions. (corpus_diff::diff_stats::priv::{num_added_unreachable_types, num_added_unreachable_types_filtered_out, num_removed_unreachable_types, num_removed_unreachable_types_filtered_out, num_changed_unreachable_types, num_changed_unreachable_types_filtered_out}): Define new data members. (sort_string_type_base_sptr_map): Declare new function. * src/abg-comparison.cc (sort_string_type_base_sptr_map) (diff_context::show_unreachable_types): Define new functions. (corpus_diff::diff_stats::{num_added_unreachable_types, num_added_unreachable_types_filtered_out, net_num_added_unreachable_types, net_num_removed_unreachable_types, num_removed_unreachable_types_filtered_out, num_removed_unreachable_types}): Define new member functions. (diff_maps::insert_diff_node): Do not update the map "diff -> impacted interfaces" if the current impacted interface is nil. This happens if we are looking at a diff node for a change on a type that is not reachable from any interfaces. (corpus_diff::priv::ensure_lookup_tables_populated): Handle the edit script for unreachable types. (corpus_diff::priv::apply_supprs_to_added_removed_fns_vars_unreachable_types): Rename corpus_diff::priv::apply_suppressions_to_added_removed_fns_vars into this. Apply suppression specifications to added and removed unreachable types as well. (corpus_diff::priv::{added,deleted}_unreachable_type_is_suppressed): Define new member functions. (corpus_diff::priv::{count_unreachable_types, changed_unreachable_types_sorted}): Likewise. (corpus_diff::priv::apply_filters_and_compute_diff_stats): Update statistics (including walking changed unreachable types to apply categorization and redundancy filters to them) related to unreachable types. (corpus_diff::priv::emit_diff_stats): Emit diff stats related to unreachable types. (corpus_diff::priv::maybe_dump_diff_tree): Dump diff tree nodes related to unreachable types. (corpus_diff::{deleted_unreachable_types, deleted_unreachable_types_sorted, added_unreachable_types, added_unreachable_types_sorted, changed_unreachable_types, changed_unreachable_types_sorted): Define new member functions. (corpus_diff::has_changes): Take deleted/added/changed unreachable types into account. (corpus_diff::has_incompatible_changes): Take net removed/changed unreachable types into account. (corpus_diff::has_net_subtype_changes): Take net removed and changed unreachable types into account. (corpus_diff::has_net_changes): Take net removed/added/changed unreachable types into account. (corpus_diff::traverse): When traversing the components of a corpus_diff node, make sure to traverse the changed unreachable types of the corpus. (leaf_diff_node_marker_visitor::visit_begin): Arrange for the fact that the current topmost interface can be nil if we are looking at types not reachable from global functions/variables. Also, make sure that only leaf nodes that are reachable from a global function/variable are recorded as leaf nodes. (compute_diff): In the overload for corpus_sptr, compute the changes between types not reachable from global functions and variables, if the user wishes that we do so. Also, add more comments. (apply_suppressions): Update for the name change of the function apply_suppressions_to_added_removed_fns_vars to apply_supprs_to_added_removed_fns_vars_unreachable_types. * include/abg-corpus.h (corpus::{record_type_as_reachable_from_public_interfaces, type_is_reachable_from_public_interfaces, get_types_not_reachable_from_public_interfaces}): Declare new member functions. (corpus::recording_types_reachable_from_public_interface_supported): Declare new virtual member function. (corpus_group::get_public_types_pretty_representations): Declare new member functons. (corpus_group::recording_types_reachable_from_public_interface_supported): Declare new virtual member function. * src/abg-corpus-priv.h (corpus::priv::{types_not_reachable_from_pub_ifaces_, pub_type_pretty_reprs_}): Define new data members. (corpus::priv::priv): Initialize the pub_type_pretty_reprs_ data member because it's a pointer. (corpus::priv::get_public_types_pretty_representations): Declare new member function. (corpus::priv::~priv): Declare a destructor. * src/abg-corpus.cc (corpus::priv::get_public_types_pretty_representations): Define new member function. (corpus::priv::~priv): Define new destructor to delete the new pub_type_pretty_reprs_ member pointer. (corpus::{record_type_as_reachable_from_public_interfaces, type_is_reachable_from_public_interfaces, get_types_not_reachable_from_public_interfaces, recording_types_reachable_from_public_interface_supported}): Define new member functions (corpus_group::get_public_types_pretty_representations): Likewise. * include/abg-diff-utils.h (struct deep_ptr_eq_functor): Document the equality operator. Also, add an overload to the equality operator, for weak_ptr. The existing equality operator overload was just for shared_ptr. * include/abg-fwd.h (is_user_defined_type): Declare function. * include/abg-ir.h (operator!=(const decl_base_sptr&, const decl_base_sptr&)): Declare new operator. (type_maps::get_types_sorted_by_name): Declare new member function. (decl_base::{g,s}et_is_artificial): Declare new member function. (function_decl::parameter::{g,s}et_artificial): Remove these member functions. * src/abg-ir.cc (operator!=(const decl_base_sptr&, const decl_base_sptr&)): Define new operator. (decl_base::priv::is_artificial_): Define new data member. (type_maps::priv::sorted_types_): Define new data member. (struct type_name_comp): Define new comparison functor to sort types based on their pretty representations. (decl_base::priv::priv): Initialize it. (decl_base::{g,s}et_is_artificial): Define new member functions. (type_maps::get_types_sorted_by_name): Define new member function. (is_user_defined_type): Define new function overloads. (strip_typedef, function_type::{function_type, set_parameters}): Adjust using decl_base::get_is_artificial rather than function_decl::parameter::get_artificial. (function_decl::parameter::priv::artificial_): Remove this data member. (function_decl::parameter::priv::priv): Adjust to the removal of function_decl::parameter::priv::artificial_. This constructor does not take an "is_artificial" flag anymore. (function_decl::parameter::parameter): Adjust to the removal of the is_artificial flag from the arguments of the constructor of function_decl::parameter::parameter::priv. (function_decl::parameter::get_artificial): Remove this member function. * src/abg-reporter-priv.h (maybe_report_unreachable_type_changes): Declare new function. * src/abg-reporter-priv.cc (maybe_report_unreachable_type_changes): Define new function. * src/abg-default-reporter.cc (default_reporter::report): In the overload for corpus_diff&, report added/removed/changed types that are not reachable from global functions and variables using the new function maybe_report_unreachable_type_changes. * src/abg-leaf-reporter.cc (leaf_reporter::report): In the overload for corpus_diff, report changes to types unreachable from global functions or variables, using the new function maybe_report_unreachable_type_changes. * src/abg-dwarf-reader.cc (build_ir_node_from_die): When the user requests that all types be loaded, record relevant types as reachable from global functions and variables. (build_enum_type, add_or_update_class_type) (add_or_update_union_type): Read the 'is-artificial' DWARF attribute and set the corresponding decl_base property accordingly. (finish_member_function_reading, strip_typedef) (function_type::function_type): Adjust using decl_base::get_is_artificial, rather than function_decl::parameter::get_artificial. * include/abg-reader.h (consider_types_not_reachable_from_public_interfaces): Declare new function. * src/abg-reader.cc (read_context::m_tracking_non_reachable_types): Add new data member. (read_context::read_context): Initialize it. (read_context::tracking_non_reachable_types): Define accessors for the new data member above. (read_is_declaration_only): Re-indent. (read_is_artificial): Define new helper function. (build_function_parameter): Use the new read_is_artificial function here, rather than open-coding it. (build_enum_type_decl, build_class_decl, build_union_decl): Support reading the 'is-artificial' property by using the new read_is_artificial function. (read_corpus_from_input): If the user wants us to take non-reachable types into account, then make sure we do so. (read_tracking_non_reachable_types, read_is_non_reachable_type): Define new static functions. (handle_element_node, build_type): Read the "is-non-reachable" attribute on type element nodes if the user wants us to track non-reachable types. (consider_types_not_reachable_from_public_interfaces): Define new function. * src/abg-writer.cc (write_is_artificial): Define new static helper function. (annotate): Adjust using decl_base::get_is_artificial rather than function_decl::parameter::get_artificial. (write_enum_type_decl, write_class_decl_opening_tag) (write_union_decl_opening_tag): Support writing the "is-artificial" property, using the new write_is_artificial function. (write_function_type): Adjust this to use the new write_is_artificial rather than open-coding writing the 'is-artificial' attribute. (write_is_non_reachable) (write_tracking_non_reachable_types): Define new static functions. (write_enum_type_decl, write_class_decl_opening_tag) (write_union_decl_opening_tag): Write the 'is-no-reachable' attribute when applicable. (write_corpus, write_corpus_group): Write the 'tracking-non-reachable-types' attribute when applicable. * tools/abidiff.cc (options::options): Initialize ... (options::show_all_types): ... new data member. (display_usage): Add help string from the new --non-reachable-types option. (parse_command_line): Parse the new --non-reachable-types option. (set_diff_context_from_opts): Set the dwarf_reader::read_context::show_unreachable_types property. (set_native_xml_reader_options): Define new static function. (main): Load all types when analyzing the DWARF or the ABIXML files, if the user wants us to do so. * tools/abipkgdiff.cc (options::show_all_types): Define new data member. (options::options): Initialize it. (parse_command_line): Parse the --non-reachable-types option to set the options::show_all_types data member. (display_usage): Add a help string for the new --non-reachable-types option. (set_diff_context_from_opts): Set the dwarf_reader::read_context::show_unreachable_types property based on the options::show_all_type data member. (compare): Configure the read context to load all types while analyzing the DWARF info, depending on the options::show_all_type data member. * doc/manuals/abidiff.rst: Document the new --non-reachable-types option added to abidiff above. * doc/manuals/abipkgdiff.rst: Add documentation for the --non-reachable-types option. * tests/data/test-diff-suppr/test47-non-reachable-types-v{0,1}.c: Source code files of test binary input. * tests/data/test-diff-suppr/test47-non-reachable-types-suppr-{1,2,3,4,5}.txt: New test input files. * tests/data/test-diff-suppr/test47-non-reachable-types-report-{1,2,3,4,5,6,7,8,9,10}.txt: New test reference output files. * tests/data/test-diff-suppr/test47-non-reachable-types-v{0,1}.o.alltypes.abixml: New test input abixml. * tests/data/Makefile.am: Add the new test material to source distribution. * tests/test-diff-suppr.cc (in_out_specs): Add the new tests above to this test harness. * tests/data/test-abidiff/test-struct1-report.txt: Adjust. * tests/data/test-diff-pkg/PR24690/flatpak-debuginfo-1.2.4-3.fc30.x86_64.rpm: New input binary RPM. * tests/data/test-diff-pkg/PR24690/flatpak-debuginfo-1.4.0-1.fc30.x86_64.rpm: Likewise. * tests/data/test-diff-pkg/PR24690/flatpak-devel-1.2.4-3.fc30.x86_64.rpm: Likewise. * tests/data/test-diff-pkg/PR24690/flatpak-devel-1.4.0-1.fc30.x86_64.rpm: Likewise. * tests/data/test-diff-pkg/PR24690/flatpak-libs-1.2.4-3.fc30.x86_64.rpm: Likewise. * tests/data/test-diff-pkg/PR24690/flatpak-libs-1.4.0-1.fc30.x86_64.rpm: Likewise. * tests/data/test-diff-pkg/PR24690/flatpak-libs-debuginfo-1.2.4-3.fc30.x86_64.rpm: Likewise. * tests/data/test-diff-pkg/PR24690/flatpak-libs-debuginfo-1.4.0-1.fc30.x86_64.rpm: Likewise. * tests/data/test-diff-pkg/PR24690/PR24690-report-0.txt: New test reference output. * tests/data/Makefile.am: Add the new test material above to source distribution. * tests/test-diff-pkg.cc (in_out_specs): Add the new test material above to this test harness. Signed-off-by: Dodji Seketeli --- diff --git a/doc/manuals/abidiff.rst b/doc/manuals/abidiff.rst index 0c334b44..052f7d56 100644 --- a/doc/manuals/abidiff.rst +++ b/doc/manuals/abidiff.rst @@ -210,6 +210,27 @@ Options the global variables that were added (defined) to *second-shared-library*. + * ``--non-reachable-types|-t`` + + Analyze and emit change reports for all the types of the binary, + including those that are not reachable from global functions and + variables. + + This option might incur some serious performance degradation as + the number of types analyzed can be huge. However, if paired with + the ``--headers-dir{1,2}`` options, the additional non-reachable + types analyzed are restricted to those defined in public headers + files, thus hopefully making the performance hit acceptable. + + Also, using this option alongside suppression specifications (by + also using the ``--suppressions`` option) might help keep the number of + analyzed types (and the potential performance degradation) in + control. + + Note that without this option, only types that are reachable from + global functions and variables are analyzed, so the tool detects + and reports changes on these reachable types only. + * ``--no-added-syms`` In the resulting report about the differences between diff --git a/doc/manuals/abipkgdiff.rst b/doc/manuals/abipkgdiff.rst index 0ba72846..34503a05 100644 --- a/doc/manuals/abipkgdiff.rst +++ b/doc/manuals/abipkgdiff.rst @@ -251,6 +251,28 @@ Options $ + * ``--non-reachable-types|-t`` + + Analyze and emit change reports for all the types of the binary, + including those that are not reachable from global functions and + variables. + + This option might incur some serious performance degradation as + the number of types analyzed can be huge. However, if paired with + the ``--devel-pkg{1,2}`` options, the additional non-reachable + types analyzed are restricted to those defined in the public + headers files carried by the referenced development packages, thus + hopefully making the performance hit acceptable. + + Also, using this option alongside suppression specifications (by + also using the ``--suppressions`` option) might help keep the number of + analyzed types (and the potential performance degradation) in + control. + + Note that without this option, only types that are reachable from + global functions and variables are analyzed, so the tool detects + and reports changes on these reachable types only. + * ``--redundant`` In the diff reports, do display redundant changes. A redundant diff --git a/include/abg-comparison.h b/include/abg-comparison.h index 8537a435..a4354912 100644 --- a/include/abg-comparison.h +++ b/include/abg-comparison.h @@ -144,6 +144,10 @@ typedef unordered_map pointer_map; /// value is a @ref decl_base_sptr. typedef unordered_map string_decl_base_sptr_map; +/// Convenience typedef for a map which key is a string and which +/// value is a @ref type_base_sptr. +typedef unordered_map string_type_base_sptr_map; + /// Convenience typedef for a map which key is an unsigned integer and /// which value is a @ref decl_base_sptr typedef unordered_map unsigned_decl_base_sptr_map; @@ -830,6 +834,10 @@ public: void show_added_symbols_unreferenced_by_debug_info(bool f); + void show_unreachable_types(bool f); + + bool show_unreachable_types(); + bool show_impacted_interfaces() const; @@ -2345,6 +2353,24 @@ public: const string_elf_symbol_map& added_unrefed_variable_symbols() const; + const string_type_base_sptr_map& + deleted_unreachable_types() const; + + const vector& + deleted_unreachable_types_sorted() const; + + const string_type_base_sptr_map& + added_unreachable_types() const; + + const vector& + added_unreachable_types_sorted() const; + + const string_diff_sptr_map& + changed_unreachable_types() const; + + const vector& + changed_unreachable_types_sorted() const; + const diff_context_sptr context() const; @@ -2392,6 +2418,12 @@ public: friend void apply_suppressions(const corpus_diff* diff_tree); + friend void + maybe_report_unreachable_type_changes(const corpus_diff& d, + const corpus_diff::diff_stats &s, + const string& indent, + ostream& out); + friend class default_reporter; friend class leaf_reporter; }; // end class corpus_diff @@ -2533,6 +2565,28 @@ public: size_t num_leaf_var_changes_filtered_out() const; void num_leaf_var_changes_filtered_out(size_t); size_t net_num_leaf_var_changes() const; + + size_t num_added_unreachable_types() const; + void num_added_unreachable_types(size_t); + + size_t num_added_unreachable_types_filtered_out() const; + void num_added_unreachable_types_filtered_out(size_t); + size_t net_num_added_unreachable_types() const; + + size_t num_removed_unreachable_types() const; + void num_removed_unreachable_types(size_t); + + size_t num_removed_unreachable_types_filtered_out() const; + void num_removed_unreachable_types_filtered_out(size_t); + size_t net_num_removed_unreachable_types() const; + + size_t num_changed_unreachable_types() const; + void num_changed_unreachable_types(size_t); + + size_t num_changed_unreachable_types_filtered_out() const; + void num_changed_unreachable_types_filtered_out(size_t); + size_t net_num_changed_unreachable_types() const; + }; // end class corpus_diff::diff_stats /// The base class for the node visitors. These are the types used to diff --git a/include/abg-corpus.h b/include/abg-corpus.h index ac2d78b1..c2adfa86 100644 --- a/include/abg-corpus.h +++ b/include/abg-corpus.h @@ -115,6 +115,18 @@ public: const type_maps& get_type_per_loc_map() const; + virtual bool + recording_types_reachable_from_public_interface_supported(); + + void + record_type_as_reachable_from_public_interfaces(const type_base&); + + bool + type_is_reachable_from_public_interfaces(const type_base&) const; + + const vector& + get_types_not_reachable_from_public_interfaces() const; + const corpus_group* get_group() const; @@ -406,6 +418,12 @@ public: virtual const elf_symbols& get_unreferenced_variable_symbols() const; + unordered_set* + get_public_types_pretty_representations(); + + virtual bool + recording_types_reachable_from_public_interface_supported(); + bool operator==(const corpus_group&) const; }; // end class corpus_group diff --git a/include/abg-diff-utils.h b/include/abg-diff-utils.h index 219cab69..4006483c 100644 --- a/include/abg-diff-utils.h +++ b/include/abg-diff-utils.h @@ -772,12 +772,36 @@ struct deep_ptr_eq_functor return *first == *second; } + /// This equality operator compares pointers by comparing the + /// pointed-to objects. + /// + /// @param first the first comparison argument. + /// + /// @param second the second comparison argument. + /// + /// @return true if the objects pointed to by the pointers are + /// equal, false otherwise. template bool operator()(const shared_ptr first, const shared_ptr second) const {return operator()(first.get(), second.get());} -}; + + /// This equality operator compares pointers by comparing the + /// pointed-to objects. + /// + /// @param first the first comparison argument. + /// + /// @param second the second comparison argument. + /// + /// @return true if the objects pointed to by the pointers are + /// equal, false otherwise. + template + bool + operator()(const weak_ptr first, + const weak_ptr second) const + {return operator()(shared_ptr(first), shared_ptr(second));} +}; // end struct deep_ptr_eq_functor /// Find the end of the furthest reaching d-path on diagonal k, for /// two sequences. In the paper This is referred to as "the basic diff --git a/include/abg-fwd.h b/include/abg-fwd.h index 823f0b96..cbba6342 100644 --- a/include/abg-fwd.h +++ b/include/abg-fwd.h @@ -572,6 +572,12 @@ is_scope_decl(const decl_base_sptr&); bool is_member_type(const type_base_sptr&); +bool +is_user_defined_type(const type_base*); + +bool +is_user_defined_type(const type_base_sptr&); + void remove_decl_from_scope(decl_base_sptr); @@ -1311,7 +1317,7 @@ typedef shared_ptr suppression_sptr; /// Convenience typedef for a vector of @ref suppression_sptr typedef vector suppressions_type; -} // end namespace comparison +} // end namespace suppr void dump(const decl_base_sptr, std::ostream&); diff --git a/include/abg-ir.h b/include/abg-ir.h index 43455714..6aaf6126 100644 --- a/include/abg-ir.h +++ b/include/abg-ir.h @@ -465,7 +465,8 @@ istring_type_or_decl_base_sptr_map_type; /// /// For instance, the type_maps contains a map of string to basic /// type, a map of string to class type, a map of string to union -/// types, etc. +/// types, etc. The key of a map entry is the pretty representation +/// of the type, and the value of the map entry is the type. class type_maps { struct priv; @@ -544,6 +545,9 @@ public: const istring_type_base_wptrs_map_type& function_types() const; + + const vector& + get_types_sorted_by_name() const; }; // end class type_maps; /// This is the abstraction of the set of relevant artefacts (types, @@ -1503,6 +1507,12 @@ public: void set_is_anonymous(bool); + bool + get_is_artificial() const; + + void + set_is_artificial(bool); + bool get_has_anonymous_parent() const; @@ -1586,6 +1596,9 @@ public: bool operator==(const decl_base_sptr&, const decl_base_sptr&); +bool +operator!=(const decl_base_sptr&, const decl_base_sptr&); + bool operator==(const type_base_sptr&, const type_base_sptr&); @@ -2996,12 +3009,6 @@ public: void set_index(unsigned i); - bool - get_artificial() const; - - void - set_artificial(bool f); - bool get_variadic_marker() const; diff --git a/include/abg-reader.h b/include/abg-reader.h index 4ddc8b0b..29906546 100644 --- a/include/abg-reader.h +++ b/include/abg-reader.h @@ -103,6 +103,9 @@ void add_read_context_suppressions(read_context& ctxt, const suppr::suppressions_type& supprs); +void +consider_types_not_reachable_from_public_interfaces(read_context& ctxt, + bool flag); }//end xml_reader }//end namespace abigail diff --git a/src/abg-comparison-priv.h b/src/abg-comparison-priv.h index 325f012f..14799468 100644 --- a/src/abg-comparison-priv.h +++ b/src/abg-comparison-priv.h @@ -217,6 +217,7 @@ struct diff_context::priv bool show_redundant_changes_; bool show_syms_unreferenced_by_di_; bool show_added_syms_unreferenced_by_di_; + bool show_unreachable_types_; bool show_impacted_interfaces_; bool dump_diff_tree_; @@ -245,6 +246,7 @@ struct diff_context::priv show_redundant_changes_(true), show_syms_unreferenced_by_di_(true), show_added_syms_unreferenced_by_di_(true), + show_unreachable_types_(false), show_impacted_interfaces_(true), dump_diff_tree_() {} @@ -792,16 +794,36 @@ struct scope_diff::priv /// A comparison functor for instances of @ref diff. struct diff_comp { + /// Lexicographically compare two diff nodes. + /// + /// Compare the pretty representation of the first subjects of two + /// diff nodes. + /// + /// @return true iff @p l is less than @p r. bool operator()(const diff& l, diff& r) const { - return (get_name(l.first_subject()) < get_name(r.first_subject())); + return (get_pretty_representation(l.first_subject(), true) + < + get_pretty_representation(r.first_subject(), true)); } + /// Lexicographically compare two diff nodes. + /// + /// Compare the pretty representation of the first subjects of two + /// diff nodes. + /// + /// @return true iff @p l is less than @p r. bool operator()(const diff* l, diff* r) const {return operator()(*l, *r);} + /// Lexicographically compare two diff nodes. + /// + /// Compare the pretty representation of the first subjects of two + /// diff nodes. + /// + /// @return true iff @p l is less than @p r. bool operator()(const diff_sptr l, diff_sptr r) const {return operator()(l.get(), r.get());} @@ -989,6 +1011,15 @@ struct corpus_diff::priv string_elf_symbol_map suppressed_added_unrefed_var_syms_; string_elf_symbol_map deleted_unrefed_var_syms_; string_elf_symbol_map suppressed_deleted_unrefed_var_syms_; + edit_script unreachable_types_edit_script_; + string_type_base_sptr_map deleted_unreachable_types_; + vector deleted_unreachable_types_sorted_; + string_type_base_sptr_map suppressed_deleted_unreachable_types_; + string_type_base_sptr_map added_unreachable_types_; + vector added_unreachable_types_sorted_; + string_type_base_sptr_map suppressed_added_unreachable_types_; + string_diff_sptr_map changed_unreachable_types_; + mutable vector changed_unreachable_types_sorted_; diff_maps leaf_diffs_; /// Default constructor of corpus_diff::priv. @@ -1029,7 +1060,7 @@ struct corpus_diff::priv ensure_lookup_tables_populated(); void - apply_suppressions_to_added_removed_fns_vars(); + apply_supprs_to_added_removed_fns_vars_unreachable_types(); bool deleted_function_is_suppressed(const function_decl* fn) const; @@ -1043,6 +1074,12 @@ struct corpus_diff::priv bool added_variable_is_suppressed(const var_decl* var) const; + bool + added_unreachable_type_is_suppressed(const type_base *t)const ; + + bool + deleted_unreachable_type_is_suppressed(const type_base *t)const ; + bool deleted_unrefed_fn_sym_is_suppressed(const elf_symbol*) const; @@ -1060,6 +1097,16 @@ struct corpus_diff::priv void count_leaf_type_changes(size_t &num_type_changes, size_t &num_type_changes_filtered); + void count_unreachable_types(size_t &num_added_unreachable_types, + size_t &num_removed_unreachable_types, + size_t &num_changed_unreachable_types, + size_t &num_filtered_added_unreachable_types, + size_t &num_filtered_removed_unreachable_types, + size_t &num_filtered_changed_unreachable_types); + + const vector& + changed_unreachable_types_sorted() const; + void apply_filters_and_compute_diff_stats(corpus_diff::diff_stats&); @@ -1236,6 +1283,12 @@ struct corpus_diff::diff_stats::priv size_t num_leaf_func_changes_filtered_out; size_t num_leaf_var_changes; size_t num_leaf_var_changes_filtered_out; + size_t num_added_unreachable_types; + size_t num_added_unreachable_types_filtered_out; + size_t num_removed_unreachable_types; + size_t num_removed_unreachable_types_filtered_out; + size_t num_changed_unreachable_types; + size_t num_changed_unreachable_types_filtered_out; priv(diff_context_sptr ctxt) : ctxt_(ctxt), @@ -1267,7 +1320,13 @@ struct corpus_diff::diff_stats::priv num_leaf_func_changes(), num_leaf_func_changes_filtered_out(), num_leaf_var_changes(), - num_leaf_var_changes_filtered_out() + num_leaf_var_changes_filtered_out(), + num_added_unreachable_types(), + num_added_unreachable_types_filtered_out(), + num_removed_unreachable_types(), + num_removed_unreachable_types_filtered_out(), + num_changed_unreachable_types(), + num_changed_unreachable_types_filtered_out() {} diff_context_sptr @@ -1292,6 +1351,9 @@ sort_string_function_ptr_map(const string_function_ptr_map& map, vector& sorted); void +sort_string_type_base_sptr_map(string_type_base_sptr_map& map, + vector& sorted); +void sort_string_function_decl_diff_sptr_map (const string_function_decl_diff_sptr_map& map, function_decl_diff_sptrs_type& sorted); diff --git a/src/abg-comparison.cc b/src/abg-comparison.cc index f23cf576..962811a7 100644 --- a/src/abg-comparison.cc +++ b/src/abg-comparison.cc @@ -470,7 +470,6 @@ sort_string_parm_map(const string_parm_map& map, ++i) sorted.push_back(i->second); - // TODO: finish this. parm_comp comp; std::sort(sorted.begin(), sorted.end(), comp); } @@ -496,6 +495,29 @@ sort_artifacts_set(const artifact_sptr_set_type& set, std::sort(sorted.begin(), sorted.end(), comp); } +/// Sort a map of string to type_base_sptr entities. +/// +/// The entries are sorted based on the lexicographic order of the +/// pretty representation of the type_sptr_sptr. The sorted result is +/// put in a vector of type_base_sptr. +/// +/// @param map the map to sort. +/// +/// @param sorted the resulting vector of type_base_sptr +/// lexicographically sorted using their pretty representation. +void +sort_string_type_base_sptr_map(string_type_base_sptr_map& map, + vector& sorted) +{ + for (string_type_base_sptr_map::const_iterator i = map.begin(); + i != map.end(); + ++i) + sorted.push_back(i->second); + + type_or_decl_base_comp comp; + std::sort(sorted.begin(), sorted.end(), comp); +} + /// Return the first underlying type that is not a qualified type. /// @param t the qualified type to consider. /// @@ -1780,6 +1802,24 @@ void diff_context::show_added_symbols_unreferenced_by_debug_info(bool f) {priv_->show_added_syms_unreferenced_by_di_ = f;} +/// Setter for the flag that indicates if changes on types unreachable +/// from global functions and variables are to be reported. +/// +/// @param f if true, then changes on types unreachable from global +/// functions and variables are to be reported. +void +diff_context::show_unreachable_types(bool f) +{priv_->show_unreachable_types_ = f;} + +/// Getter for the flag that indicates if changes on types unreachable +/// from global functions and variables are to be reported. +/// +/// @return true iff changes on types unreachable from global +/// functions and variables are to be reported. +bool +diff_context::show_unreachable_types() +{return priv_->show_unreachable_types_;} + /// Getter of the flag that indicates if the leaf reporter should /// display a summary of the interfaces impacted by a given leaf /// change or not. @@ -7622,6 +7662,13 @@ diff_maps::get_distinct_diff_map() /// /// @param dif the new diff node to insert into the @ref diff_maps. /// +/// @param impacted_iface the interface (global function or variable) +/// currently being analysed that led to analysing the diff node @p +/// dif. In other words, this is the interface impacted by the diff +/// node @p dif. Note that this can be nil in cases where we are +/// directly analysing changes to a type that is not reachable from +/// any global function or variable. +/// /// @return true iff the diff node could be added to the current /// instance of @ref diff_maps. bool @@ -7660,20 +7707,23 @@ diff_maps::insert_diff_node(const diff *dif, else ABG_ASSERT_NOT_REACHED; - // Update the map that associate the interface that is impacted by - // this diff, to this diff node. + // Update the map that associates this diff node to the set of + // interfaces it impacts. - diff_artifact_set_map_type::iterator i = - priv_->impacted_artifacts_map_.find(dif); - - if (i == priv_->impacted_artifacts_map_.end()) + if (impacted_iface) { - artifact_sptr_set_type set; - set.insert(impacted_iface); - priv_->impacted_artifacts_map_[dif] = set; + diff_artifact_set_map_type::iterator i = + priv_->impacted_artifacts_map_.find(dif); + + if (i == priv_->impacted_artifacts_map_.end()) + { + artifact_sptr_set_type set; + set.insert(impacted_iface); + priv_->impacted_artifacts_map_[dif] = set; + } + else + i->second.insert(impacted_iface); } - else - i->second.insert(impacted_iface); return true; } @@ -8384,6 +8434,202 @@ void corpus_diff::diff_stats::num_leaf_var_changes(size_t n) {priv_->num_leaf_var_changes = n;} +/// Getter of the number of added types that are unreachable from the +/// public interface of the ABI corpus. +/// +/// Public interface means the set of defined and publicly exported +/// functions and variables of the ABI corpus. +/// +/// @return the number of added types that are unreachable from the +/// public interface of the ABI corpus. +size_t +corpus_diff::diff_stats::num_added_unreachable_types() const +{return priv_->num_added_unreachable_types;} + +/// Setter of the number of added types that are unreachable from the +/// public interface (global functions or variables) of the ABI +/// corpus. +/// +/// Public interface means the set of defined and publicly exported +/// functions and variables of the ABI corpus. +/// +/// @param n the new number of added types that are unreachable from +/// the public interface of the ABI corpus. +void +corpus_diff::diff_stats::num_added_unreachable_types(size_t n) +{priv_->num_added_unreachable_types = n;} + +/// Getter of the number of added types that are unreachable from +/// public interfaces and that are filtered out by suppression +/// specifications. +/// +/// @return the number of added types that are unreachable from public +/// interfaces and that are filtered out by suppression +/// specifications. +size_t +corpus_diff::diff_stats::num_added_unreachable_types_filtered_out() const +{return priv_->num_added_unreachable_types_filtered_out;} + +/// Setter of the number of added types that are unreachable from +/// public interfaces and that are filtered out by suppression +/// specifications. +/// +/// @param n the new number of added types that are unreachable from +/// public interfaces and that are filtered out by suppression +/// specifications. +void +corpus_diff::diff_stats::num_added_unreachable_types_filtered_out(size_t n) +{priv_->num_added_unreachable_types_filtered_out = n;} + +/// Getter of the number of added types that are unreachable from +/// public interfaces and that are *NOT* filtered out by suppression +/// specifications. +/// +/// @return the number of added types that are unreachable from public +/// interfaces and that are *NOT* filtered out by suppression +/// specifications. +size_t +corpus_diff::diff_stats::net_num_added_unreachable_types() const +{ + ABG_ASSERT(num_added_unreachable_types() + >= + num_added_unreachable_types_filtered_out()); + + return (num_added_unreachable_types() + - + num_added_unreachable_types_filtered_out()); +} + +/// Getter of the number of removed types that are unreachable from +/// the public interface of the ABI corpus. +/// +/// Public interface means the set of defined and publicly exported +/// functions and variables of the ABI corpus. +/// +/// @return the number of removed types that are unreachable from +/// the public interface of the ABI corpus. +size_t +corpus_diff::diff_stats::num_removed_unreachable_types() const +{return priv_->num_removed_unreachable_types;} + +/// Setter of the number of removed types that are unreachable from +/// the public interface of the ABI corpus. +/// +/// Public interface means the set of defined and publicly exported +/// functions and variables of the ABI corpus. +/// +///@param n the new number of removed types that are unreachable from +/// the public interface of the ABI corpus. +void +corpus_diff::diff_stats::num_removed_unreachable_types(size_t n) +{priv_->num_removed_unreachable_types = n;} + +/// Getter of the number of removed types that are not reachable from +/// public interfaces and that have been filtered out by suppression +/// specifications. +/// +/// @return the number of removed types that are not reachable from +/// public interfaces and that have been filtered out by suppression +/// specifications. +size_t +corpus_diff::diff_stats::num_removed_unreachable_types_filtered_out() const +{return priv_->num_removed_unreachable_types_filtered_out;} + +/// Setter of the number of removed types that are not reachable from +/// public interfaces and that have been filtered out by suppression +/// specifications. +/// +/// @param n the new number of removed types that are not reachable +/// from public interfaces and that have been filtered out by +/// suppression specifications. +void +corpus_diff::diff_stats::num_removed_unreachable_types_filtered_out(size_t n) +{priv_->num_removed_unreachable_types_filtered_out = n;} + +/// Getter of the number of removed types that are not reachable from +/// public interfaces and that have *NOT* been filtered out by +/// suppression specifications. +/// +/// @return the number of removed types that are not reachable from +/// public interfaces and that have *NOT* been filtered out by +/// suppression specifications. +size_t +corpus_diff::diff_stats::net_num_removed_unreachable_types() const +{ + ABG_ASSERT(num_removed_unreachable_types() + >= + num_removed_unreachable_types_filtered_out()); + + return (num_removed_unreachable_types() + - + num_removed_unreachable_types_filtered_out()); +} + +/// Getter of the number of changed types that are unreachable from +/// the public interface of the ABI corpus. +/// +/// Public interface means the set of defined and publicly exported +/// functions and variables of the ABI corpus. +/// +/// @return the number of changed types that are unreachable from the +/// public interface of the ABI corpus. +size_t +corpus_diff::diff_stats::num_changed_unreachable_types() const +{return priv_->num_changed_unreachable_types;} + +/// Setter of the number of changed types that are unreachable from +/// the public interface of the ABI corpus. +/// +/// Public interface means the set of defined and publicly exported +/// functions and variables of the ABI corpus. +/// +///@param n the new number of changed types that are unreachable from +/// the public interface of the ABI corpus. +void +corpus_diff::diff_stats::num_changed_unreachable_types(size_t n) +{priv_->num_changed_unreachable_types = n;} + +/// Getter of the number of changed types that are unreachable from +/// public interfaces and that have been filtered out by suppression +/// specifications. +/// +/// @return the number of changed types that are unreachable from +/// public interfaces and that have been filtered out by suppression +/// specifications. +size_t +corpus_diff::diff_stats::num_changed_unreachable_types_filtered_out() const +{return priv_->num_changed_unreachable_types_filtered_out;} + +/// Setter of the number of changed types that are unreachable from +/// public interfaces and that have been filtered out by suppression +/// specifications. +/// +/// @param n the new number of changed types that are unreachable from +/// public interfaces and that have been filtered out by suppression +/// specifications. +void +corpus_diff::diff_stats::num_changed_unreachable_types_filtered_out(size_t n) +{priv_->num_changed_unreachable_types_filtered_out = n;} + +/// Getter of the number of changed types that are unreachable from +/// public interfaces and that have *NOT* been filtered out by +/// suppression specifications. +/// +/// @return the number of changed types that are unreachable from +/// public interfaces and that have *NOT* been filtered out by +/// suppression specifications. +size_t +corpus_diff::diff_stats::net_num_changed_unreachable_types() const +{ + ABG_ASSERT(num_changed_unreachable_types() + >= + num_changed_unreachable_types_filtered_out()); + + return (num_changed_unreachable_types() + - + num_changed_unreachable_types_filtered_out()); +} + /// Getter for the number of leaf variable changes diff nodes that /// have been filtered out. /// @@ -8412,7 +8658,9 @@ corpus_diff::diff_stats::num_leaf_var_changes_filtered_out(size_t n) size_t corpus_diff::diff_stats::net_num_leaf_var_changes() const {return num_leaf_var_changes() - num_leaf_var_changes_filtered_out();} -// + + +// /// Getter of the context associated with this corpus. /// @@ -8784,6 +9032,91 @@ corpus_diff::priv::ensure_lookup_tables_populated() } } } + + // Handle the unreachable_types_edit_script_ + { + edit_script& e = unreachable_types_edit_script_; + + // Populate the map of deleted unreachable types from the + // deletions of the edit script. + for (vector::const_iterator it = e.deletions().begin(); + it != e.deletions().end(); + ++it) + { + unsigned i = it->index(); + type_base_sptr t + (first_->get_types_not_reachable_from_public_interfaces()[i]); + + if (!is_user_defined_type(t)) + continue; + + string repr = abigail::ir::get_pretty_representation(t, true); + deleted_unreachable_types_[repr] = t; + } + + // Populate the map of added and change unreachable types from the + // insertions of the edit script. + for (vector::const_iterator it = e.insertions().begin(); + it != e.insertions().end(); + ++it) + { + for (vector::const_iterator iit = + it->inserted_indexes().begin(); + iit != it->inserted_indexes().end(); + ++iit) + { + unsigned i = *iit; + type_base_sptr t + (second_->get_types_not_reachable_from_public_interfaces()[i]); + + if (!is_user_defined_type(t)) + continue; + + string repr = abigail::ir::get_pretty_representation(t, true); + + // Let's see if the inserted type we are looking at was + // reported as deleted as well. + // + // If it's been deleted and a different version of it has + // now been added, it means it's been *changed*. In that + // case we'll compute the diff of that change and store it + // in the map of changed unreachable types. + // + // Otherwise, it means the type's been added so we'll add + // it to the set of added unreachable types. + + string_type_base_sptr_map::const_iterator j = + deleted_unreachable_types_.find(repr); + if (j != deleted_unreachable_types_.end()) + { + // So there was another type of the same pretty + // representation which was reported as deleted. + // Let's see if they are different or not ... + decl_base_sptr old_type = is_decl(j->second); + decl_base_sptr new_type = is_decl(t); + if (old_type != new_type) + { + // The previously added type is different from this + // one that is added. That means the initial type + // was changed. Let's compute its diff and store it + // as a changed type. + diff_sptr d = compute_diff(old_type, new_type, ctxt); + ABG_ASSERT(d->has_changes()); + changed_unreachable_types_[repr]= d; + } + + // In any case, the type was both deleted and added, + // so we cannot have it marked as being deleted. So + // let's remove it from the deleted types. + deleted_unreachable_types_.erase(j); + } + else + // The type wasn't previously reported as deleted, so + // it's really added. + added_unreachable_types_[repr] = t; + } + } + } } /// Test if a change reports about a given @ref function_decl that is @@ -8840,10 +9173,11 @@ variable_is_suppressed(const var_decl* var, return var_suppr->suppresses_variable(var, k, ctxt); } -/// Apply the suppression specifications for this corpus diff to the -/// set of added and removed functions and variables. +/// Apply suppression specifications for this corpus diff to the set +/// of added/removed functions/variables, as well as to types not +/// reachable from global functions/variables. void -corpus_diff::priv::apply_suppressions_to_added_removed_fns_vars() +corpus_diff::priv::apply_supprs_to_added_removed_fns_vars_unreachable_types() { diff_context_sptr ctxt = get_context(); @@ -8926,6 +9260,24 @@ corpus_diff::priv::apply_suppressions_to_added_removed_fns_vars() if (type_suppr->suppresses_type(c, ctxt)) suppressed_deleted_fns_[e->first] = e->second; } + + // Apply this type suppression to deleted types + // non-reachable from a public interface. + for (string_type_base_sptr_map::const_iterator e = + deleted_unreachable_types_.begin(); + e != deleted_unreachable_types_.end(); + ++e) + if (type_suppr->suppresses_type(e->second, ctxt)) + suppressed_deleted_unreachable_types_[e->first] = e->second; + + // Apply this type suppression to added types + // non-reachable from a public interface. + for (string_type_base_sptr_map::const_iterator e = + added_unreachable_types_.begin(); + e != added_unreachable_types_.end(); + ++e) + if (type_suppr->suppresses_type(e->second, ctxt)) + suppressed_added_unreachable_types_[e->first] = e->second; } // Added/Deleted variables else if (variable_suppression_sptr var_suppr = @@ -8991,6 +9343,50 @@ corpus_diff::priv::deleted_function_is_suppressed(const function_decl* fn) const return (i != suppressed_deleted_fns_.end()); } +/// Test if an added type that is unreachable from public interface +/// has been suppressed by a suppression specification. +/// +/// @param t the added unreachable type to be considered. +/// +/// @return true iff @p t has been suppressed by a suppression +/// specification. +bool +corpus_diff::priv::added_unreachable_type_is_suppressed(const type_base *t)const +{ + if (!t) + return false; + + string repr = abigail::ir::get_pretty_representation(t, /*internal=*/true); + string_type_base_sptr_map::const_iterator i = + suppressed_added_unreachable_types_.find(repr); + if (i == suppressed_added_unreachable_types_.end()) + return false; + + return true; +} + +/// Test if a deleted type that is unreachable from public interface +/// has been suppressed by a suppression specification. +/// +/// @param t the deleted unreachable type to be considered. +/// +/// @return true iff @p t has been suppressed by a suppression +/// specification. +bool +corpus_diff::priv::deleted_unreachable_type_is_suppressed(const type_base *t) const +{ + if (!t) + return false; + + string repr = abigail::ir::get_pretty_representation(t, /*internal=*/true); + string_type_base_sptr_map::const_iterator i = + suppressed_deleted_unreachable_types_.find(repr); + if (i == suppressed_deleted_unreachable_types_.end()) + return false; + + return true; +} + /// Test if the change reports for a give given added function has /// been deleted. /// @@ -9195,6 +9591,71 @@ corpus_diff::priv::count_leaf_type_changes(size_t &num_changes, num_changes, num_filtered); } +/// Count the number of types not reachable from the interface (i.e, +/// not reachable from global functions or variables). +/// +/// @param num_added this is set to the number of added types not +/// reachable from the interface. +/// +/// @param num_deleted this is set to the number of deleted types not +/// reachable from the interface. +/// +/// @param num_changed this is set to the number of changed types not +/// reachable from the interface. +/// +/// @param num_filtered_added this is set to the number of added types +/// not reachable from the interface and that have been filtered out +/// by suppression specifications. +/// +/// @param num_filtered_deleted this is set to the number of deleted +/// types not reachable from the interface and that have been filtered +/// out by suppression specifications. +/// +/// @param num_filtered_changed this is set to the number of changed +/// types not reachable from the interface and that have been filtered +/// out by suppression specifications. +void +corpus_diff::priv::count_unreachable_types(size_t &num_added, + size_t &num_deleted, + size_t &num_changed, + size_t &num_filtered_added, + size_t &num_filtered_deleted, + size_t &num_filtered_changed) +{ + num_added = added_unreachable_types_.size(); + num_deleted = deleted_unreachable_types_.size(); + num_changed = changed_unreachable_types_.size(); + num_filtered_added = suppressed_added_unreachable_types_.size(); + num_filtered_deleted = suppressed_deleted_unreachable_types_.size(); + + for (vector::const_iterator i = + changed_unreachable_types_sorted().begin(); + i != changed_unreachable_types_sorted().end(); + ++i) + if (!(*i)->to_be_reported()) + ++num_filtered_changed; +} + +/// Get the sorted vector of diff nodes representing changed +/// unreachable types. +/// +/// Upon the first invocation of this method, if the vector is empty, +/// this function gets the diff nodes representing changed +/// unreachable, sort them, and return the sorted vector. +/// +/// @return the sorted vector of diff nodes representing changed +/// unreachable types. +const vector& +corpus_diff::priv::changed_unreachable_types_sorted() const +{ +if (changed_unreachable_types_sorted_.empty()) + if (!changed_unreachable_types_.empty()) + sort_string_diff_sptr_map(changed_unreachable_types_, + changed_unreachable_types_sorted_); + + return changed_unreachable_types_sorted_; +} + /// Compute the diff stats. /// /// To know the number of functions that got filtered out, this @@ -9211,7 +9672,6 @@ corpus_diff::priv::count_leaf_type_changes(size_t &num_changes, /// got filtered out from the report void corpus_diff::priv::apply_filters_and_compute_diff_stats(diff_stats& stat) - { stat.num_func_removed(deleted_fns_.size()); stat.num_removed_func_filtered_out(suppressed_deleted_fns_.size()); @@ -9249,6 +9709,17 @@ corpus_diff::priv::apply_filters_and_compute_diff_stats(diff_stats& stat) ctxt->maybe_apply_filters(diff); } + // walk the changed unreachable types to apply categorization + // filters + for (diff_sptrs_type::const_iterator i = + changed_unreachable_types_sorted().begin(); + i != changed_unreachable_types_sorted().end(); + ++i) + { + diff_sptr diff = *i; + ctxt->maybe_apply_filters(diff); + } + categorize_redundant_changed_sub_nodes(); // Walk the changed function diff nodes to count the number of @@ -9326,6 +9797,33 @@ corpus_diff::priv::apply_filters_and_compute_diff_stats(diff_stats& stat) stat.num_leaf_changes(num_changes); stat.num_leaf_changes_filtered_out(num_filtered); } + + // Walk the unreachable types to count them + { + size_t num_added_unreachable_types = 0, + num_changed_unreachable_types = 0, + num_deleted_unreachable_types = 0, + num_added_unreachable_types_filtered = 0, + num_changed_unreachable_types_filtered = 0, + num_deleted_unreachable_types_filtered = 0; + + count_unreachable_types(num_added_unreachable_types, + num_deleted_unreachable_types, + num_changed_unreachable_types, + num_added_unreachable_types_filtered, + num_deleted_unreachable_types_filtered, + num_changed_unreachable_types_filtered); + + stat.num_added_unreachable_types(num_added_unreachable_types); + stat.num_removed_unreachable_types(num_deleted_unreachable_types); + stat.num_changed_unreachable_types(num_changed_unreachable_types); + stat.num_added_unreachable_types_filtered_out + (num_added_unreachable_types_filtered); + stat.num_removed_unreachable_types_filtered_out + (num_deleted_unreachable_types_filtered); + stat.num_changed_unreachable_types_filtered_out + (num_changed_unreachable_types_filtered); + } } /// Emit the summary of the functions & variables that got @@ -9487,6 +9985,43 @@ corpus_diff::priv::emit_diff_stats(const diff_stats& s, out << "\n"; } + // Show statistics about types not reachable from global + // functions/variables. + if (ctxt->show_unreachable_types()) + { + size_t total_nb_variable_changes = + s.num_removed_unreachable_types() + + s.num_changed_unreachable_types() + + s.num_added_unreachable_types(); + + // Show summary of unreachable types + out << indent << "Unreachable types summary: " + << s.net_num_removed_unreachable_types() + << " removed"; + if (s.num_removed_unreachable_types_filtered_out()) + out << " (" << s.num_removed_unreachable_types_filtered_out() + << " filtered out)"; + out << ", "; + + out << s.net_num_changed_unreachable_types() + << " changed"; + if (s.num_changed_unreachable_types_filtered_out()) + out << " (" << s.num_changed_unreachable_types_filtered_out() + << " filtered out)"; + out << ", "; + + out << s.net_num_added_unreachable_types() + << " added"; + if (s.num_added_unreachable_types_filtered_out()) + out << " (" << s.num_added_unreachable_types_filtered_out() + << " filtered out)"; + if (total_nb_variable_changes <= 1) + out << " type"; + else + out << " types"; + out << "\n"; + } + if (ctxt->show_symbols_unreferenced_by_debug_info() && (s.num_func_syms_removed() || s.num_func_syms_added() @@ -9577,6 +10112,15 @@ corpus_diff::priv::categorize_redundant_changed_sub_nodes() diff_sptr diff = *i; categorize_redundancy(diff); } + + for (diff_sptrs_type::const_iterator i = + changed_unreachable_types_sorted().begin(); + i!= changed_unreachable_types_sorted().end(); + ++i) + { + diff_sptr diff = *i; + categorize_redundancy(diff); + } } /// Walk the changed functions and variables diff nodes and clear the @@ -9641,6 +10185,20 @@ corpus_diff::priv::maybe_dump_diff_tree() print_diff_tree(d, *ctxt->error_output_stream()); } } + + if (!changed_unreachable_types_sorted().empty()) + { + *ctxt->error_output_stream() << "\nchanged unreachable " + "types diff tree: \n\n"; + for (vector::const_iterator i = + changed_unreachable_types_sorted().begin(); + i != changed_unreachable_types_sorted().end(); + ++i) + { + diff_sptr d = *i; + print_diff_tree(d, *ctxt->error_output_stream()); + } + } } /// Populate the vector of children node of the @ref corpus_diff type. @@ -9868,6 +10426,77 @@ const string_elf_symbol_map& corpus_diff::added_unrefed_variable_symbols() const {return priv_->added_unrefed_var_syms_;} +/// Getter for a map of deleted types that are not reachable from +/// global functions/variables. +/// +/// @return a map that associates pretty representation of deleted +/// unreachable types and said types. +const string_type_base_sptr_map& +corpus_diff::deleted_unreachable_types() const +{return priv_->deleted_unreachable_types_;} + +/// Getter of a sorted vector of deleted types that are not reachable +/// from global functions/variables. +/// +/// @return a sorted vector of deleted types that are not reachable +/// from global functions/variables. The types are lexicographically +/// sorted by considering their pretty representation. +const vector& +corpus_diff::deleted_unreachable_types_sorted() const +{ + if (priv_->deleted_unreachable_types_sorted_.empty()) + if (!priv_->deleted_unreachable_types_.empty()) + sort_string_type_base_sptr_map(priv_->deleted_unreachable_types_, + priv_->deleted_unreachable_types_sorted_); + + return priv_->deleted_unreachable_types_sorted_; +} + +/// Getter for a map of added types that are not reachable from global +/// functions/variables. +/// +/// @return a map that associates pretty representation of added +/// unreachable types and said types. +const string_type_base_sptr_map& +corpus_diff::added_unreachable_types() const +{return priv_->added_unreachable_types_;} + +/// Getter of a sorted vector of added types that are not reachable +/// from global functions/variables. +/// +/// @return a sorted vector of added types that are not reachable from +/// global functions/variables. The types are lexicographically +/// sorted by considering their pretty representation. +const vector& +corpus_diff::added_unreachable_types_sorted() const +{ + if (priv_->added_unreachable_types_sorted_.empty()) + if (!priv_->added_unreachable_types_.empty()) + sort_string_type_base_sptr_map(priv_->added_unreachable_types_, + priv_->added_unreachable_types_sorted_); + + return priv_->added_unreachable_types_sorted_; +} + +/// Getter for a map of changed types that are not reachable from +/// global functions/variables. +/// +/// @return a map that associates pretty representation of changed +/// unreachable types and said types. +const string_diff_sptr_map& +corpus_diff::changed_unreachable_types() const +{return priv_->changed_unreachable_types_;} + +/// Getter of a sorted vector of changed types that are not reachable +/// from global functions/variables. +/// +/// @return a sorted vector of changed types that are not reachable +/// from global functions/variables. The diffs are lexicographically +/// sorted by considering their pretty representation. +const vector& +corpus_diff::changed_unreachable_types_sorted() const +{return priv_->changed_unreachable_types_sorted();} + /// Getter of the diff context of this diff /// /// @return the diff context for this diff. @@ -9892,7 +10521,8 @@ corpus_diff::get_pretty_representation() const } return priv_->pretty_representation_; } -/// Return true iff the current diff node carries a change. +/// Return true iff the current @ref corpus_diff node carries a +/// change. /// /// @return true iff the current diff node carries a change. bool @@ -9909,7 +10539,10 @@ corpus_diff::has_changes() const || priv_->added_unrefed_fn_syms_.size() || priv_->deleted_unrefed_fn_syms_.size() || priv_->added_unrefed_var_syms_.size() - || priv_->deleted_unrefed_var_syms_.size()); + || priv_->deleted_unrefed_var_syms_.size() + || priv_->deleted_unreachable_types_.size() + || priv_->added_unreachable_types_.size() + || priv_->changed_unreachable_types_.size()); } /// Test if the current instance of @ref corpus_diff carries changes @@ -9949,7 +10582,9 @@ corpus_diff::has_incompatible_changes() const && stats.net_num_func_changed() != 0) || stats.net_num_vars_removed() != 0 || stats.net_num_removed_func_syms() != 0 - || stats.net_num_removed_var_syms() != 0); + || stats.net_num_removed_var_syms() != 0 + || stats.net_num_removed_unreachable_types() != 0 + || stats.net_num_changed_unreachable_types() != 0); } /// Test if the current instance of @ref corpus_diff carries subtype @@ -9966,7 +10601,9 @@ corpus_diff::has_net_subtype_changes() const apply_filters_and_suppressions_before_reporting(); return (stats.net_num_func_changed() != 0 - || stats.net_num_vars_changed() != 0); + || stats.net_num_vars_changed() != 0 + || stats.net_num_removed_unreachable_types() != 0 + || stats.net_num_changed_unreachable_types() != 0); } /// Test if the current instance of @ref corpus_diff carries changes @@ -9992,7 +10629,10 @@ corpus_diff::has_net_changes() const || stats.net_num_vars_added() || stats.net_num_added_var_syms() || stats.net_num_vars_removed() - || stats.net_num_removed_var_syms()); + || stats.net_num_removed_var_syms() + || stats.net_num_added_unreachable_types() + || stats.net_num_removed_unreachable_types() + || stats.net_num_changed_unreachable_types()); } /// Apply the different filters that are registered to be applied to @@ -10037,10 +10677,14 @@ struct leaf_diff_node_marker_visitor : public diff_node_visitor /// This is called when the visitor visits a diff node. /// /// It basically tests if the diff node being visited is a leaf diff - /// node - that is, if contains local changes. If it does, then the + /// node - that is, it contains local changes. If it does, then the /// node is added to the set of maps that hold leaf diffs in the /// current corpus_diff. /// + /// Note that only leaf nodes that are reachable from public + /// interfaces (global functions or variables) are collected by this + /// visitor. + /// /// @param d the diff node being visited. virtual void visit_begin(diff *d) @@ -10089,12 +10733,15 @@ struct leaf_diff_node_marker_visitor : public diff_node_visitor diff_context_sptr ctxt = d->context(); const corpus_diff *corpus_diff_node = ctxt->get_corpus_diff().get(); ABG_ASSERT(corpus_diff_node); - type_or_decl_base_sptr iface = - get_current_topmost_iface_diff()->first_subject(); - // So this is diff node carries a leaf change. Let's add it - // to the set of of leaf diffs of corpus_diff_node. - const_cast(corpus_diff_node)-> - get_leaf_diffs().insert_diff_node(d, iface); + + if (diff *iface_diff = get_current_topmost_iface_diff()) + { + type_or_decl_base_sptr iface = iface_diff->first_subject(); + // So this diff node carries a leaf change. Let's add it + // to the set of of leaf diffs of corpus_diff_node. + const_cast(corpus_diff_node)-> + get_leaf_diffs().insert_diff_node(d, iface); + } } } }; // end struct leaf_diff_node_marker_visitor @@ -10218,6 +10865,27 @@ corpus_diff::traverse(diff_node_visitor& v) v.set_current_topmost_iface_diff(0); + // Traverse the changed unreachable type diffs. These diffs are on + // types that are not reachable from global functions or variables. + for (vector::const_iterator i = + changed_unreachable_types_sorted().begin(); + i != changed_unreachable_types_sorted().end(); + ++i) + { + if (diff_sptr d = *i) + { + const diff_context_sptr &ctxt = context(); + if (ctxt->visiting_a_node_twice_is_forbidden_per_interface()) + ctxt->forget_visited_diffs(); + + if (!d->traverse(v)) + { + v.visit_end(this); + return false; + } + } + } + v.visit_end(this); return true; } @@ -10243,6 +10911,7 @@ compute_diff(const corpus_sptr f, typedef corpus::variables::const_iterator vars_it_type; typedef elf_symbols::const_iterator symbols_it_type; typedef diff_utils::deep_ptr_eq_functor eq_type; + typedef vector::const_iterator type_base_wptr_it_type; ABG_ASSERT(f && s); @@ -10262,17 +10931,21 @@ compute_diff(const corpus_sptr f, r->priv_->architectures_equal_ = f->get_architecture_name() == s->get_architecture_name(); + // Compute the diff of publicly defined and exported functions diff_utils::compute_diff(f->get_functions().begin(), f->get_functions().end(), s->get_functions().begin(), s->get_functions().end(), r->priv_->fns_edit_script_); + // Compute the diff of publicly defined and exported variables. diff_utils::compute_diff (f->get_variables().begin(), f->get_variables().end(), s->get_variables().begin(), s->get_variables().end(), r->priv_->vars_edit_script_); + // Compute the diff of function elf symbols not referenced by debug + // info. diff_utils::compute_diff (f->get_unreferenced_function_symbols().begin(), f->get_unreferenced_function_symbols().end(), @@ -10280,6 +10953,8 @@ compute_diff(const corpus_sptr f, s->get_unreferenced_function_symbols().end(), r->priv_->unrefed_fn_syms_edit_script_); + // Compute the diff of variable elf symbols not referenced by debug + // info. diff_utils::compute_diff (f->get_unreferenced_variable_symbols().begin(), f->get_unreferenced_variable_symbols().end(), @@ -10287,6 +10962,16 @@ compute_diff(const corpus_sptr f, s->get_unreferenced_variable_symbols().end(), r->priv_->unrefed_var_syms_edit_script_); + if (ctxt->show_unreachable_types()) + // Compute the diff of types not reachable from public functions + // or global variables that are exported. + diff_utils::compute_diff + (f->get_types_not_reachable_from_public_interfaces().begin(), + f->get_types_not_reachable_from_public_interfaces().end(), + s->get_types_not_reachable_from_public_interfaces().begin(), + s->get_types_not_reachable_from_public_interfaces().end(), + r->priv_->unreachable_types_edit_script_); + r->priv_->ensure_lookup_tables_populated(); return r; @@ -10953,8 +11638,8 @@ void apply_suppressions(diff_sptr diff_tree) {apply_suppressions(diff_tree.get());} -/// Walk a diff tree and appply the suppressions carried by the -/// context. If the suppression applies to a given node than +/// Walk a @ref corpus_diff tree and appply the suppressions carried +/// by the context. If the suppression applies to a given node then /// categorize the node into the SUPPRESSED_CATEGORY category and /// propagate that categorization. /// @@ -10974,9 +11659,11 @@ apply_suppressions(const corpus_diff* diff_tree) const_cast(diff_tree)->traverse(v); diff_tree->context()->forbid_visiting_a_node_twice(s); - // ... then also visit the set added and removed functions, - // variables, and symbols - diff_tree->priv_->apply_suppressions_to_added_removed_fns_vars(); + // ... then also visit the set of added and removed functions, + // variables, symbols, and types not reachable from global + // functions and variables. + diff_tree->priv_-> + apply_supprs_to_added_removed_fns_vars_unreachable_types(); } } diff --git a/src/abg-corpus-priv.h b/src/abg-corpus-priv.h index 4422532e..bb7dcde6 100644 --- a/src/abg-corpus-priv.h +++ b/src/abg-corpus-priv.h @@ -718,6 +718,8 @@ struct corpus::priv // the type maps of each translation unit. type_maps types_; type_maps type_per_loc_map_; + mutable vector types_not_reachable_from_pub_ifaces_; + unordered_set *pub_type_pretty_reprs_; private: priv(); @@ -728,7 +730,8 @@ public: : env(e), group(), origin_(ARTIFICIAL_ORIGIN), - path(p) + path(p), + pub_type_pretty_reprs_() {} void @@ -739,6 +742,11 @@ public: const type_maps& get_types() const; + + unordered_set* + get_public_types_pretty_representations(); + + ~priv(); }; // end struct corpus::priv void diff --git a/src/abg-corpus.cc b/src/abg-corpus.cc index 486e335e..4088ff99 100644 --- a/src/abg-corpus.cc +++ b/src/abg-corpus.cc @@ -450,6 +450,29 @@ const type_maps& corpus::priv::get_types() const {return types_;} +/// Getter of the set of pretty representation of types that are +/// reachable from public interfaces (global functions and variables). +/// +/// @return the set of pretty representation of types that are +/// reachable from public interfaces (global functions and variables). +unordered_set* +corpus::priv::get_public_types_pretty_representations() +{ + if (group) + return group->get_public_types_pretty_representations(); + + if (pub_type_pretty_reprs_ == 0) + pub_type_pretty_reprs_ = + new unordered_set; + return pub_type_pretty_reprs_; +} + +/// Destructor of the @ref corpus::priv type. +corpus::priv::~priv() +{ + delete pub_type_pretty_reprs_; +} + /// Record a canonical type which has been computed for the current /// corpus. /// @@ -610,6 +633,81 @@ const type_maps& corpus::get_type_per_loc_map() const {return priv_->type_per_loc_map_;} +/// Test if the recording of reachable types (and thus, indirectly, +/// the recording of non-reachable types) is activated for the +/// current @ref corpus. +/// +/// @return true iff the recording of reachable types is activated for +/// the current @ref corpus. +bool +corpus::recording_types_reachable_from_public_interface_supported() +{ + return (priv_->get_public_types_pretty_representations() + && !priv_->get_public_types_pretty_representations()->empty()); +} + +/// Record a type as being reachable from public interfaces (global +/// functions and variables). +/// +/// @param t the type to record as reachable. +void +corpus::record_type_as_reachable_from_public_interfaces(const type_base& t) +{ + string repr = get_pretty_representation(&t, /*internal=*/true); + interned_string s = t.get_environment()->intern(repr); + priv_->get_public_types_pretty_representations()->insert(s); +} + +/// Test if a type is reachable from public interfaces (global +/// functions and variables). +/// +/// For a type to be considered reachable from public interfaces, it +/// must have been previously marked as such by calling +/// corpus::record_type_as_reachable_from_public_interfaces. +/// +/// @param t the type to test for. +/// +/// @return true iff @p t is reachable from public interfaces. +bool +corpus::type_is_reachable_from_public_interfaces(const type_base& t) const +{ + string repr = get_pretty_representation(&t, /*internal=*/true); + interned_string s = t.get_environment()->intern(repr); + + return (priv_->get_public_types_pretty_representations()->find(s) + != priv_->get_public_types_pretty_representations()->end()); +} + +/// Getter ofa a sorted vector of the types that are *NOT* reachable +/// from public interfaces. +/// +/// Note that for this to be non-empty, the libabigail reader that +/// analyzed the input (be it a binary or an abixml file) must have be +/// configured to load types that are not reachable from public +/// interfaces. +/// +/// @return a reference to a vector of sorted types NON reachable from +/// public interfaces. +const vector& +corpus::get_types_not_reachable_from_public_interfaces() const +{ + if (priv_->types_not_reachable_from_pub_ifaces_.empty()) + { + const type_maps& types = get_types(); + for (vector::const_iterator it = + types.get_types_sorted_by_name().begin(); + it != types.get_types_sorted_by_name().end(); + ++it) + { + type_base_sptr t(*it); + if (!type_is_reachable_from_public_interfaces(*t)) + priv_->types_not_reachable_from_pub_ifaces_.push_back(t); + } + } + + return priv_->types_not_reachable_from_pub_ifaces_; +} + /// Get the maps that associate a location string to a certain kind of /// type. /// @@ -1524,6 +1622,7 @@ struct corpus_group::priv unordered_map unrefed_var_symbol_map; elf_symbols unrefed_var_symbols; bool unrefed_var_symbols_built; + unordered_set pub_type_pretty_reprs_; priv() : unrefed_fun_symbols_built(), @@ -1913,6 +2012,22 @@ corpus_group::get_unreferenced_variable_symbols() const return priv_->unrefed_var_symbols; } +/// Getter of a pointer to the set of types reachable from public +/// interfaces of a given corpus group. +unordered_set* +corpus_group::get_public_types_pretty_representations() +{return &priv_->pub_type_pretty_reprs_;} + +/// Test if the recording of reachable types (and thus, indirectly, +/// the recording of non-reachable types) is activated for the +/// current @ref corpus_group. +/// +/// @return true iff the recording of reachable types is activated for +/// the current @ref corpus_group. +bool +corpus_group::recording_types_reachable_from_public_interface_supported() +{return !get_public_types_pretty_representations()->empty();} + // }// end namespace ir diff --git a/src/abg-default-reporter.cc b/src/abg-default-reporter.cc index 5e582861..5234b51d 100644 --- a/src/abg-default-reporter.cc +++ b/src/abg-default-reporter.cc @@ -2245,6 +2245,11 @@ default_reporter::report(const corpus_diff& d, ostream& out, out << '\n'; } + // Report added/removed/changed types not reacheable from public + // interfaces. + + maybe_report_unreachable_type_changes(d, s, indent, out); + d.priv_->maybe_dump_diff_tree(); } diff --git a/src/abg-dwarf-reader.cc b/src/abg-dwarf-reader.cc index 6f8f5beb..69c32859 100644 --- a/src/abg-dwarf-reader.cc +++ b/src/abg-dwarf-reader.cc @@ -14547,6 +14547,8 @@ build_enum_type(read_context& ctxt, while (dwarf_siblingof(&child, &child) == 0); } + bool is_artificial = die_is_artificial(die); + // DWARF up to version 4 (at least) doesn't seem to carry the // underlying type, so let's create an artificial one here, which // sole purpose is to be passed to the constructor of the @@ -14563,6 +14565,7 @@ build_enum_type(read_context& ctxt, ABG_ASSERT(t); result.reset(new enum_type_decl(name, loc, t, enms, linkage_name)); result->set_is_anonymous(enum_is_anonymous); + result->set_is_artificial(is_artificial); ctxt.associate_die_to_type(die, result, where_offset); return result; } @@ -14619,7 +14622,7 @@ finish_member_function_reading(Dwarf_Die* die, first_parm = f->get_parameters()[0]; bool is_artificial = - first_parm && first_parm->get_artificial();; + first_parm && first_parm->get_is_artificial();; pointer_type_def_sptr this_ptr_type; type_base_sptr other_klass; @@ -15045,6 +15048,7 @@ add_or_update_class_type(read_context& ctxt, uint64_t size = 0; die_size_in_bits(die, size); + bool is_artificial = die_is_artificial(die); Dwarf_Die child; bool has_child = (dwarf_child(die, &child) == 0); @@ -15074,6 +15078,8 @@ add_or_update_class_type(read_context& ctxt, if (size) result->set_size_in_bits(size); + result->set_is_artificial(is_artificial); + ctxt.associate_die_to_type(die, result, where_offset); ctxt.maybe_schedule_declaration_only_class_for_resolution(result); @@ -15383,6 +15389,7 @@ add_or_update_union_type(read_context& ctxt, uint64_t size = 0; die_size_in_bits(die, size); + bool is_artificial = die_is_artificial(die); if (union_type) { @@ -15406,6 +15413,8 @@ add_or_update_union_type(read_context& ctxt, result->set_is_declaration_only(false); } + result->set_is_artificial(is_artificial); + ctxt.associate_die_to_type(die, result, where_offset); // TODO: maybe schedule declaration-only union for result like we do @@ -17619,7 +17628,15 @@ build_ir_node_from_die(read_context& ctxt, if ((result = ctxt.lookup_decl_from_die_offset(dwarf_dieoffset(die), source_of_die))) - return result; + { + if (ctxt.load_all_types()) + if (called_from_public_decl) + if (type_base_sptr t = is_type(result)) + if (corpus *abi_corpus = scope->get_corpus()) + abi_corpus->record_type_as_reachable_from_public_interfaces(*t); + + return result; + } switch (tag) { @@ -18091,6 +18108,13 @@ build_ir_node_from_die(read_context& ctxt, ctxt.associate_die_to_decl(die, is_decl(result), where_offset, /*associate_by_repr=*/false); + if (result) + if (ctxt.load_all_types()) + if (called_from_public_decl) + if (type_base_sptr t = is_type(result)) + if (corpus *abi_corpus = scope->get_corpus()) + abi_corpus->record_type_as_reachable_from_public_interfaces(*t); + return result; } diff --git a/src/abg-ir.cc b/src/abg-ir.cc index a6ae7ae6..9ac24c38 100644 --- a/src/abg-ir.cc +++ b/src/abg-ir.cc @@ -420,6 +420,7 @@ struct type_maps::priv mutable istring_type_base_wptrs_map_type array_types_; mutable istring_type_base_wptrs_map_type subrange_types_; mutable istring_type_base_wptrs_map_type function_types_; + mutable vector sorted_types_; }; // end struct type_maps::priv type_maps::type_maps() @@ -596,6 +597,99 @@ istring_type_base_wptrs_map_type& type_maps::function_types() {return priv_->function_types_;} +/// A comparison functor to compare/sort types based on their pretty +/// representations. +struct type_name_comp +{ + /// Comparison operator for two instances of @ref type_base. + /// + /// This compares the two types by lexicographically comparing their + /// pretty representation. + /// + /// @param l the left-most type to compare. + /// + /// @param r the right-most type to compare. + /// + /// @return true iff @p l < @p r. + bool + operator()(type_base *l, type_base *r) const + { + if (l == 0 && r == 0) + return false; + + string l_repr = get_pretty_representation(l); + string r_repr = get_pretty_representation(r); + return l_repr < r_repr; + } + + /// Comparison operator for two instances of @ref type_base. + /// + /// This compares the two types by lexicographically comparing their + /// pretty representation. + /// + /// @param l the left-most type to compare. + /// + /// @param r the right-most type to compare. + /// + /// @return true iff @p l < @p r. + bool + operator()(const type_base_sptr &l, const type_base_sptr &r) const + {return operator()(l.get(), r.get());} + + /// Comparison operator for two instances of @ref type_base. + /// + /// This compares the two types by lexicographically comparing their + /// pretty representation. + /// + /// @param l the left-most type to compare. + /// + /// @param r the right-most type to compare. + /// + /// @return true iff @p l < @p r. + bool + operator()(const type_base_wptr &l, const type_base_wptr &r) const + {return operator()(type_base_sptr(l), type_base_sptr(r));} +}; // end struct type_name_comp + +/// Getter of all types types sorted by their pretty representation. +/// +/// @return a sorted vector of all types sorted by their pretty +/// representation. +const vector& +type_maps::get_types_sorted_by_name() const +{ + if (priv_->sorted_types_.empty()) + { + istring_type_base_wptrs_map_type::const_iterator i; + vector::const_iterator j; + + for (i = basic_types().begin(); i != basic_types().end(); ++i) + for (j = i->second.begin(); j != i->second.end(); ++j) + priv_->sorted_types_.push_back(*j); + + for (i = class_types().begin(); i != class_types().end(); ++i) + for (j = i->second.begin(); j != i->second.end(); ++j) + priv_->sorted_types_.push_back(*j); + + for (i = union_types().begin(); i != union_types().end(); ++i) + for (j = i->second.begin(); j != i->second.end(); ++j) + priv_->sorted_types_.push_back(*j); + + for (i = enum_types().begin(); i != enum_types().end(); ++i) + for (j = i->second.begin(); j != i->second.end(); ++j) + priv_->sorted_types_.push_back(*j); + + for (i = typedef_types().begin(); i != typedef_types().end(); ++i) + for (j = i->second.begin(); j != i->second.end(); ++j) + priv_->sorted_types_.push_back(*j); + + type_name_comp comp; + sort(priv_->sorted_types_.begin(), priv_->sorted_types_.end(), comp); + } + + return priv_->sorted_types_; +} + // // @@ -3191,6 +3285,7 @@ struct decl_base::priv { bool in_pub_sym_tab_; bool is_anonymous_; + bool is_artificial_; bool has_anonymous_parent_; location location_; context_rel *context_; @@ -3218,6 +3313,7 @@ struct decl_base::priv priv() : in_pub_sym_tab_(false), is_anonymous_(true), + is_artificial_(false), has_anonymous_parent_(false), context_(), visibility_(VISIBILITY_DEFAULT) @@ -3491,6 +3587,29 @@ void decl_base::set_is_anonymous(bool f) {priv_->is_anonymous_ = f;} +/// Getter of the flag that says if the declaration is artificial. +/// +/// Being artificial means the parameter was not explicitely +/// mentionned in the source code, but was rather artificially created +/// by the compiler. +/// +/// @return true iff the declaration is artificial. +bool +decl_base::get_is_artificial() const +{return priv_->is_artificial_;} + +/// Setter of the flag that says if the declaration is artificial. +/// +/// Being artificial means the parameter was not explicitely +/// mentionned in the source code, but was rather artificially created +/// by the compiler. +/// +/// @param f the new value of the flag that says if the declaration is +/// artificial. +void +decl_base::set_is_artificial(bool f) +{priv_->is_artificial_ = f;} + /// Get the "has_anonymous_parent" flag of the current declaration. /// /// Having an anoymous parent means having a anonymous parent scope @@ -3926,7 +4045,8 @@ operator<<(std::ostream& o, decl_base::binding b) } /// Turn equality of shared_ptr of decl_base into a deep equality; -/// that is, make it compare the pointed to objects too. +/// that is, make it compare the pointed to objects, not just the +/// pointers. /// /// @param l the shared_ptr of decl_base on left-hand-side of the /// equality. @@ -3947,6 +4067,20 @@ operator==(const decl_base_sptr& l, const decl_base_sptr& r) return *l == *r; } +/// Inequality operator of shared_ptr of @ref decl_base. +/// +/// This is a deep equality operator, that is, it compares the +/// pointed-to objects, rather than just the pointers. +/// +/// @param l the left-hand-side operand. +/// +/// @param r the right-hand-side operand. +/// +/// @return true iff @p l is different from @p r. +bool +operator!=(const decl_base_sptr& l, const decl_base_sptr& r) +{return !operator==(l, r);} + /// Turn equality of shared_ptr of type_base into a deep equality; /// that is, make it compare the pointed to objects too. /// @@ -4061,6 +4195,43 @@ is_member_type(const type_base_sptr& t) return is_member_decl(d); } +/// Test if a type is user-defined. +/// +/// A type is considered user-defined if it's a +/// struct/class/union/enum that is *NOT* artificial. +/// +/// @param t the type to consider. +/// +/// @return true iff the type @p t is user-defined. +bool +is_user_defined_type(const type_base* t) +{ + if (t == 0) + return false; + + t = peel_qualified_or_typedef_type(t); + decl_base *d = is_decl(t); + + if ((is_class_or_union_type(t) || is_enum_type(t)) + && d && !d->get_is_artificial()) + return true; + + return false; +} + +/// Test if a type is user-defined. +/// +/// A type is considered user-defined if it's a +/// struct/class/union/enum. +/// +/// +/// @param t the type to consider. +/// +/// @return true iff the type @p t is user-defined. +bool +is_user_defined_type(const type_base_sptr& t) +{return is_user_defined_type(t.get());} + /// Gets the access specifier for a class member. /// /// @param d the declaration of the class member to consider. Note @@ -4982,7 +5153,7 @@ strip_typedef(const type_base_sptr type) p->get_name(), p->get_location(), p->get_variadic_marker(), - p->get_artificial())); + p->get_is_artificial())); parm.push_back(stripped); } type_base_sptr p = strip_typedef(ty->get_return_type()); @@ -5009,7 +5180,7 @@ strip_typedef(const type_base_sptr type) p->get_name(), p->get_location(), p->get_variadic_marker(), - p->get_artificial())); + p->get_is_artificial())); parm.push_back(stripped); } type_base_sptr p = strip_typedef(ty->get_return_type()); @@ -11043,7 +11214,7 @@ synthesize_function_type_from_translation_unit(const function_type& fn_type, (*i)->get_name(), (*i)->get_location(), (*i)->get_variadic_marker(), - (*i)->get_artificial())); + (*i)->get_is_artificial())); parms.push_back(parm); } @@ -15859,7 +16030,7 @@ function_type::function_type(type_base_sptr return_type, i < priv_->parms_.size(); ++i, ++j) { - if (i == 0 && priv_->parms_[i]->get_artificial()) + if (i == 0 && priv_->parms_[i]->get_is_artificial()) // If the first parameter is artificial, then it certainly // means that this is a member function, and the first // parameter is the implicit this pointer. In that case, set @@ -15980,7 +16151,7 @@ function_type::set_parameters(const parameters &p) i < priv_->parms_.size(); ++i, ++j) { - if (i == 0 && priv_->parms_[i]->get_artificial()) + if (i == 0 && priv_->parms_[i]->get_is_artificial()) // If the first parameter is artificial, then it certainly // means that this is a member function, and the first // parameter is the implicit this pointer. In that case, set @@ -17212,22 +17383,18 @@ struct function_decl::parameter::priv type_base_wptr type_; unsigned index_; bool variadic_marker_; - bool artificial_; priv() : index_(), - variadic_marker_(), - artificial_() + variadic_marker_() {} priv(type_base_sptr type, unsigned index, - bool variadic_marker, - bool artificial) + bool variadic_marker) : type_(type), index_(index), - variadic_marker_(variadic_marker), - artificial_(artificial) + variadic_marker_(variadic_marker) {} };// end struct function_decl::parameter::priv @@ -17239,7 +17406,7 @@ function_decl::parameter::parameter(const type_base_sptr type, : type_or_decl_base(type->get_environment(), FUNCTION_PARAMETER_DECL | ABSTRACT_DECL_BASE), decl_base(type->get_environment(), name, loc), - priv_(new priv(type, index, is_variadic, /*is_artificial=*/false)) + priv_(new priv(type, index, is_variadic)) { runtime_type_instance(this); } @@ -17253,9 +17420,10 @@ function_decl::parameter::parameter(const type_base_sptr type, : type_or_decl_base(type->get_environment(), FUNCTION_PARAMETER_DECL | ABSTRACT_DECL_BASE), decl_base(type->get_environment(), name, loc), - priv_(new priv(type, index, is_variadic, is_artificial)) + priv_(new priv(type, index, is_variadic)) { runtime_type_instance(this); + set_is_artificial(is_artificial); } function_decl::parameter::parameter(const type_base_sptr type, @@ -17266,9 +17434,10 @@ function_decl::parameter::parameter(const type_base_sptr type, : type_or_decl_base(type->get_environment(), FUNCTION_PARAMETER_DECL | ABSTRACT_DECL_BASE), decl_base(type->get_environment(), name, loc), - priv_(new priv(type, 0, is_variadic, is_artificial)) + priv_(new priv(type, 0, is_variadic)) { runtime_type_instance(this); + set_is_artificial(is_artificial); } function_decl::parameter::parameter(const type_base_sptr type, @@ -17277,7 +17446,7 @@ function_decl::parameter::parameter(const type_base_sptr type, : type_or_decl_base(type->get_environment(), FUNCTION_PARAMETER_DECL | ABSTRACT_DECL_BASE), decl_base(type->get_environment(), "", location()), - priv_(new priv(type, index, variad, /*is_artificial=*/false)) + priv_(new priv(type, index, variad)) { runtime_type_instance(this); } @@ -17350,32 +17519,11 @@ void function_decl::parameter::set_index(unsigned i) {priv_->index_ = i;} -/// Test if the parameter is artificial. -/// -/// Being artificial means the parameter was not explicitely -/// mentionned in the source code, but was rather artificially -/// created by the compiler. -/// -/// @return true if the parameter is artificial, false otherwise. -bool -function_decl::parameter::get_artificial() const -{return priv_->artificial_;} bool function_decl::parameter::get_variadic_marker() const {return priv_->variadic_marker_;} -/// Getter for the artificial-ness of the parameter. -/// -/// Being artificial means the parameter was not explicitely -/// mentionned in the source code, but was rather artificially -/// created by the compiler. -/// -/// @param f set to true if the parameter is artificial. -void -function_decl::parameter::set_artificial(bool f) -{priv_->artificial_ = f;} - /// Compares two instances of @ref function_decl::parameter. /// /// If the two intances are different, set a bitfield to give some diff --git a/src/abg-leaf-reporter.cc b/src/abg-leaf-reporter.cc index 06fdee9a..85ecf067 100644 --- a/src/abg-leaf-reporter.cc +++ b/src/abg-leaf-reporter.cc @@ -1231,6 +1231,10 @@ leaf_reporter::report(const corpus_diff& d, // Now show the changed types. const diff_maps& leaf_diffs = d.get_leaf_diffs(); report_type_changes_from_diff_maps(*this, leaf_diffs, out, indent); + + // Report added/removed/changed types not reacheable from public + // interfaces. + maybe_report_unreachable_type_changes(d, s, indent, out); } } // end namespace comparison } // end namespace abigail diff --git a/src/abg-reader.cc b/src/abg-reader.cc index ce1f3ea7..61d5de96 100644 --- a/src/abg-reader.cc +++ b/src/abg-reader.cc @@ -76,6 +76,9 @@ using zip_utils::open_file_in_archive; #endif //WITH_ZIP_ARCHIVE static bool read_is_declaration_only(xmlNodePtr, bool&); +static bool read_is_artificial(xmlNodePtr, bool&); +static bool read_tracking_non_reachable_types(xmlNodePtr, bool&); +static bool read_is_non_reachable_type(xmlNodePtr, bool&); class read_context; @@ -125,6 +128,7 @@ private: corpus_group_sptr m_corpus_group; corpus::exported_decls_builder* m_exported_decls_builder; suppr::suppressions_type m_supprs; + bool m_tracking_non_reachable_types; read_context(); @@ -134,9 +138,28 @@ public: : m_env(env), m_reader(reader), m_corp_node(), - m_exported_decls_builder() + m_exported_decls_builder(), + m_tracking_non_reachable_types() {} + /// Getter for the flag that tells us if we are tracking types that + /// are not reachable from global functions and variables. + /// + /// @return true iff we are tracking types that are not reachable + /// from global functions and variables. + bool + tracking_non_reachable_types() const + {return m_tracking_non_reachable_types;} + + /// Setter for the flag that tells us if we are tracking types that + /// are not reachable from global functions and variables. + /// + /// @param f the new value of the flag. + /// from global functions and variables. + void + tracking_non_reachable_types(bool f) + {m_tracking_non_reachable_types = f;} + /// Getter of the path to the ABI file. /// /// @return the path to the native xml abi file. @@ -1768,6 +1791,19 @@ add_read_context_suppressions(read_context& ctxt, ctxt.get_suppressions().push_back(*i); } +/// Configure the @ref read_context so that types not reachable from +/// public interface are taken into account when the abixml file is +/// read. +/// +/// @param ctxt the @read_context to consider. +/// +/// @param flag if yes, then types not reachable from public interface +/// are taken into account when the abixml file is read. +void +consider_types_not_reachable_from_public_interfaces(read_context& ctxt, + bool flag) +{ctxt.tracking_non_reachable_types(flag);} + /// Parse the input XML document containing an ABI corpus, represented /// by an 'abi-corpus' element node, associated to the current /// context. @@ -1907,6 +1943,17 @@ read_corpus_from_input(read_context& ctxt) } while (is_ok); + if (ctxt.tracking_non_reachable_types()) + { + bool is_tracking_non_reachable_types = false; + read_tracking_non_reachable_types(node, is_tracking_non_reachable_types); + + ABG_ASSERT + (corp.recording_types_reachable_from_public_interface_supported() + == is_tracking_non_reachable_types); + } + + ctxt.perform_late_type_canonicalizing(); ctxt.get_environment()->canonicalization_is_done(true); @@ -2116,6 +2163,23 @@ handle_element_node(read_context& ctxt, xmlNodePtr node, add_to_current_scope)) || (decl = handle_class_tdecl(ctxt, node, add_to_current_scope))); + + // If the user wants us to track non-reachable types, then read the + // 'is-non-reachable-type' attribute on type elements and record + // reachable types accordingly. + if (ctxt.tracking_non_reachable_types()) + { + if (type_base_sptr t = is_type(decl)) + { + corpus_sptr abi = ctxt.get_corpus(); + ABG_ASSERT(abi); + bool is_non_reachable_type = false; + read_is_non_reachable_type(node, is_non_reachable_type); + if (!is_non_reachable_type) + abi->record_type_as_reachable_from_public_interfaces(*t); + } + } + return decl; } @@ -2384,22 +2448,98 @@ read_cdtor_const(xmlNodePtr node, /// @param node the xml node to consider. /// /// @param is_decl_only is set to true iff the "is-declaration-only" attribute -/// is present and set to "yes" +/// is present and set to "yes". /// /// @return true iff the is_decl_only attribute was set. static bool read_is_declaration_only(xmlNodePtr node, bool& is_decl_only) { - if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "is-declaration-only")) - { - string str = CHAR_STR(s); - if (str == "yes") - is_decl_only = true; - else - is_decl_only = false; - return true; - } - return false; + if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "is-declaration-only")) + { + string str = CHAR_STR(s); + if (str == "yes") + is_decl_only = true; + else + is_decl_only = false; + return true; + } + return false; +} + +/// Read the "is-artificial" attribute of the current XML node. +/// +/// @param node the XML node to consider. +/// +/// @param is_artificial this output parameter is set to true iff the +/// "is-artificial" parameter is present and set to 'yes'. +/// +/// @return true iff the "is-artificial" parameter was present on the +/// XML node. +static bool +read_is_artificial(xmlNodePtr node, bool& is_artificial) +{ + if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "is-artificial")) + { + string is_artificial_str = CHAR_STR(s) ? CHAR_STR(s) : ""; + is_artificial = (is_artificial_str == "yes") ? true : false; + return true; + } + return false; +} + +/// Read the 'tracking-non-reachable-types' attribute on the current +/// XML element. +/// +/// @param node the current XML element. +/// +/// @param tracking_non_reachable_types output parameter. This is set +/// to true iff the 'tracking-non-reachable-types' attribute is +/// present on the current XML node and set to 'yes'. In that case, +/// the function returns true. +/// +/// @return true iff the 'tracking-non-reachable-types' attribute is +/// present on the current XML node and set to 'yes'. +static bool +read_tracking_non_reachable_types(xmlNodePtr node, + bool& tracking_non_reachable_types) +{ + if (xml_char_sptr s = + XML_NODE_GET_ATTRIBUTE(node, "tracking-non-reachable-types")) + { + string tracking_non_reachable_types_str = CHAR_STR(s) ? CHAR_STR(s) : ""; + tracking_non_reachable_types = + (tracking_non_reachable_types_str == "yes") + ? true + : false; + return true; + } + return false; +} + +/// Read the 'is-non-reachable' attribute on the current XML element. +/// +/// @param node the current XML element. +/// +/// @param is_non_reachable_type output parameter. This is set to true +/// iff the 'is-non-reachable' attribute is present on the current XML +/// element with a value se to 'yes'. +/// +/// @return true iff the 'is-non-reachable' attribute is present on +/// the current XML element with a value se to 'yes'. +static bool +read_is_non_reachable_type(xmlNodePtr node, bool& is_non_reachable_type) +{ + if (xml_char_sptr s = + XML_NODE_GET_ATTRIBUTE(node, "is-non-reachable")) + { + string is_non_reachable_type_str = CHAR_STR(s) ? CHAR_STR(s) : ""; + is_non_reachable_type = + (is_non_reachable_type_str == "yes") + ? true + : false; + return true; + } + return false; } /// Read the "is-virtual" attribute of the current xml node. @@ -2849,13 +2989,7 @@ build_function_parameter(read_context& ctxt, const xmlNodePtr node) } bool is_artificial = false; - string is_artificial_str; - if (xml_char_sptr s = - xml::build_sptr(xmlGetProp(node, BAD_CAST("is-artificial")))) - { - is_artificial_str = CHAR_STR(s) ? CHAR_STR(s) : ""; - is_artificial = (is_artificial_str == "yes") ? true : false; - } + read_is_artificial(node, is_artificial); string type_id; if (xml_char_sptr a = xml::build_sptr(xmlGetProp(node, BAD_CAST("type-id")))) @@ -3963,6 +4097,9 @@ build_enum_type_decl(read_context& ctxt, bool is_anonymous = false; read_is_anonymous(node, is_anonymous); + bool is_artificial = false; + read_is_artificial(node, is_artificial); + string id; if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "id")) id = CHAR_STR(s); @@ -4016,6 +4153,7 @@ build_enum_type_decl(read_context& ctxt, underlying_type, enums, linkage_name)); t->set_is_anonymous(is_anonymous); + t->set_is_artificial(is_artificial); if (ctxt.push_and_key_type_decl(t, id, add_to_current_scope)) { ctxt.map_xml_node_to_decl(node, t); @@ -4182,6 +4320,9 @@ build_class_decl(read_context& ctxt, decl_base::visibility vis = decl_base::VISIBILITY_NONE; read_visibility(node, vis); + bool is_artificial = false; + read_is_artificial(node, is_artificial); + string id; if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "id")) id = CHAR_STR(s); @@ -4271,6 +4412,8 @@ build_class_decl(read_context& ctxt, decl->set_is_anonymous(is_anonymous); } + decl->set_is_artificial(is_artificial); + string def_id; bool is_def_of_decl = false; if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "def-of-decl-id")) @@ -4595,6 +4738,9 @@ build_union_decl(read_context& ctxt, decl_base::visibility vis = decl_base::VISIBILITY_NONE; read_visibility(node, vis); + bool is_artificial = false; + read_is_artificial(node, is_artificial); + string id; if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "id")) id = CHAR_STR(s); @@ -4673,6 +4819,8 @@ build_union_decl(read_context& ctxt, decl->set_is_anonymous(is_anonymous); } + decl->set_is_artificial(is_artificial); + string def_id; bool is_def_of_decl = false; if (xml_char_sptr s = XML_NODE_GET_ATTRIBUTE(node, "def-of-decl-id")) @@ -5325,6 +5473,16 @@ build_type(read_context& ctxt, || (t = build_union_decl_if_not_suppressed(ctxt, node, add_to_current_scope))); + if (ctxt.tracking_non_reachable_types() && t) + { + corpus_sptr abi = ctxt.get_corpus(); + ABG_ASSERT(abi); + bool is_non_reachable_type = false; + read_is_non_reachable_type(node, is_non_reachable_type); + if (!is_non_reachable_type) + abi->record_type_as_reachable_from_public_interfaces(*t); + } + return t; } diff --git a/src/abg-reporter-priv.cc b/src/abg-reporter-priv.cc index 6bdf177e..f9e08c91 100644 --- a/src/abg-reporter-priv.cc +++ b/src/abg-reporter-priv.cc @@ -1219,8 +1219,148 @@ show_linkage_name_and_aliases(ostream& out, out << ", aliases " << aliases; } +/// Report changes about types that are not reachable from global +/// functions and variables, in a given @param corpus_diff. +/// +/// @param d the corpus_diff to consider. +/// +/// @param s the statistics of the changes, after filters and +/// suppressions are reported. This is typically what is returned by +/// corpus_diff::apply_filters_and_suppressions_before_reporting(). +/// +/// @param indent the indendation string (usually a string of white +/// spaces) to use for indentation during the reporting. +/// +/// @param out the output stream to emit the report to. +void +maybe_report_unreachable_type_changes(const corpus_diff& d, + const corpus_diff::diff_stats &s, + const string& indent, + ostream& out) +{ + const unsigned large_num = 100; + const diff_context_sptr& ctxt = d.context(); + + if (!(ctxt->show_unreachable_types() + && (!d.priv_->deleted_unreachable_types_.empty() + || !d.priv_->added_unreachable_types_.empty() + || !d.priv_->changed_unreachable_types_.empty()))) + // The user either doesn't want us to show changes about + // unreachable types or there are not such changes. + return; + + // Handle removed unreachable types. + if (s.net_num_removed_unreachable_types() == 1) + out << indent + << "1 removed type unreachable from any public interface:\n\n"; + else if (s.net_num_removed_unreachable_types() > 1) + out << indent + << s.net_num_removed_unreachable_types() + << " removed types unreachable from any public interface:\n\n"; + + vector sorted_removed_unreachable_types; + sort_string_type_base_sptr_map(d.priv_->deleted_unreachable_types_, + sorted_removed_unreachable_types); + bool emitted = false; + for (vector::const_iterator i = + sorted_removed_unreachable_types.begin(); + i != sorted_removed_unreachable_types.end(); + ++i) + { + if (d.priv_->deleted_unreachable_type_is_suppressed((*i).get())) + continue; + + out << indent << " "; + if (s.num_removed_unreachable_types() > large_num) + out << "[D] "; + out << get_pretty_representation(*i); + report_loc_info(*i, *ctxt, out); + out << "\n"; + emitted = true; + } + if (emitted) + { + out << "\n"; + emitted = false; + } + + // Handle changed unreachable types! + if (s.net_num_changed_unreachable_types() == 1) + out << indent + << "1 changed type unreachable from any public interface:\n\n"; + else if (s.net_num_changed_unreachable_types() > 1) + out << indent + << s.net_num_changed_unreachable_types() + << " changed types unreachable from any public interface:\n\n"; + + diff_sptrs_type sorted_diff_sptrs; + sort_string_diff_sptr_map(d.priv_->changed_unreachable_types_, + sorted_diff_sptrs); + emitted = true; + for (diff_sptrs_type::const_iterator i = sorted_diff_sptrs.begin(); + i != sorted_diff_sptrs.end(); + ++i) + { + diff_sptr diff = *i; + if (!diff || !diff->to_be_reported()) + continue; + + string repr = diff->first_subject()->get_pretty_representation(); + + out << " "; + + if (sorted_diff_sptrs.size() > large_num) + out << "[C] "; + + out << "'" << repr << "' changed:\n"; + diff->report(out, indent + " "); + out << "\n"; + emitted = true; + } + if (emitted) + { + out << "\n"; + emitted = false; + } + + // Handle added unreachable types. + if (s.net_num_added_unreachable_types() == 1) + out << indent + << "1 added type unreachable from any public interface:\n\n"; + else if (s.net_num_added_unreachable_types() > 1) + out << indent + << s.net_num_added_unreachable_types() + << " added types unreachable from any public interface:\n\n"; + + vector sorted_added_unreachable_types; + sort_string_type_base_sptr_map(d.priv_->added_unreachable_types_, + sorted_added_unreachable_types); + emitted = false; + for (vector::const_iterator i = + sorted_added_unreachable_types.begin(); + i != sorted_added_unreachable_types.end(); + ++i) + { + if (d.priv_->added_unreachable_type_is_suppressed((*i).get())) + continue; + + out << indent << " "; + if (s.num_added_unreachable_types() > large_num) + out << "[A] "; + out << "'" << get_pretty_representation(*i) << "'"; + report_loc_info(*i, *ctxt, out); + out << "\n"; + emitted = true; + } + if (emitted) + { + out << "\n"; + emitted = false; + } +} + /// If a given diff node impacts some public interfaces, then report -/// about those impacted interfaces on standard output. +/// about those impacted interfaces on a given output stream. /// /// @param d the diff node to get the impacted interfaces for. /// diff --git a/src/abg-reporter-priv.h b/src/abg-reporter-priv.h index d1b52008..530b7488 100644 --- a/src/abg-reporter-priv.h +++ b/src/abg-reporter-priv.h @@ -235,6 +235,12 @@ show_linkage_name_and_aliases(ostream& out, const elf_symbol& symbol, const string_elf_symbols_map_type& sym_map); +void +maybe_report_unreachable_type_changes(const corpus_diff& d, + const corpus_diff::diff_stats &s, + const string& indent, + ostream& out); + void maybe_report_interfaces_impacted_by_diff(const diff *d, ostream &out, diff --git a/src/abg-writer.cc b/src/abg-writer.cc index bc2246ba..78844cf4 100644 --- a/src/abg-writer.cc +++ b/src/abg-writer.cc @@ -772,6 +772,9 @@ static void write_location(const location&, write_context&); static void write_location(const decl_base_sptr&, write_context&); static bool write_visibility(const decl_base_sptr&, ostream&); static bool write_binding(const decl_base_sptr&, ostream&); +static bool write_is_artificial(const decl_base_sptr&, ostream&); +static bool write_is_non_reachable(const type_base_sptr&, ostream&); +static bool write_tracking_non_reachable_types(const corpus_sptr&, ostream&); static void write_array_size_and_alignment(const array_type_def_sptr, ostream&); static void write_size_and_alignment(const type_base_sptr, ostream&); @@ -1127,7 +1130,7 @@ annotate(const function_decl::parameter_sptr& parm, o << "variadic parameter"; else { - if (parm->get_artificial()) + if (parm->get_is_artificial()) { if (parm->get_index() == 0) o << "implicit "; @@ -1287,6 +1290,75 @@ write_binding(const shared_ptr& decl, ostream& o) return true; } +/// Write the "is-artificial" attribute of the @ref decl. +/// +/// @param decl the declaration to consider. +/// +/// @param o the output stream to emit the "is-artificial" attribute +/// to. +/// +/// @return true iff the "is-artificial" attribute was emitted. +static bool +write_is_artificial(const decl_base_sptr& decl, ostream& o) +{ + if (!decl) + return false; + + if (decl->get_is_artificial()) + o << " is-artificial='yes'"; + + return true; +} + +/// Write the 'is-non-reachable' attribute if a given type we are +/// looking at is not reachable from global functions and variables +/// and if the user asked us to track that information. +/// +/// @param t the type to consider. +/// +/// @param o the output stream to write the 'is-non-reachable' +/// attribute to. +static bool +write_is_non_reachable(const type_base_sptr& t, ostream& o) +{ + if (!t) + return false; + + corpus* c = t->get_corpus(); + if (!c) + return false; + + if (!c->recording_types_reachable_from_public_interface_supported() + || c->type_is_reachable_from_public_interfaces(*t)) + return false; + + o << " is-non-reachable='yes'"; + + return true; +} + +/// Write the 'tracking-non-reachable-types' attribute if for a given +/// corpus, the user wants us to track non-reachable types. +/// +/// @param corpus the ABI corpus to consider. +/// +/// @param o the output parameter to write the +/// 'tracking-non-reachable-types' attribute to. +static bool +write_tracking_non_reachable_types(const corpus_sptr& corpus, + ostream& o) +{ + corpus_group* group = corpus->get_group(); + if (!group) + if (corpus->recording_types_reachable_from_public_interface_supported()) + { + o << " tracking-non-reachable-types='yes'"; + return true; + } + + return false; +} + /// Serialize the size and alignment attributes of a given type. /// /// @param decl the type to consider. @@ -2704,6 +2776,8 @@ write_enum_type_decl(const enum_type_decl_sptr& decl, o << "get_linkage_name().empty()) o << " linkage-name='" << decl->get_linkage_name() << "'"; @@ -3075,8 +3149,7 @@ write_function_decl(const function_decl_sptr& decl, write_context& ctxt, if (!(*pi)->get_name().empty()) o << " name='" << (*pi)->get_name() << "'"; } - if ((*pi)->get_artificial()) - o << " is-artificial='yes'"; + write_is_artificial(*pi, o); write_location((*pi)->get_location(), ctxt); o << "/>\n"; } @@ -3171,8 +3244,7 @@ write_function_type(const function_type_sptr& fn_type, o << " name='" << name << "'"; } } - if ((*pi)->get_artificial()) - o << " is-artificial='yes'"; + write_is_artificial(*pi, o); o << "/>\n"; } @@ -3230,6 +3302,10 @@ write_class_decl_opening_tag(const class_decl_sptr& decl, write_is_anonymous(decl, o); + write_is_artificial(decl, o); + + write_is_non_reachable(is_type(decl), o); + write_naming_typedef(decl, ctxt); write_visibility(decl, o); @@ -3304,6 +3380,10 @@ write_union_decl_opening_tag(const union_decl_sptr& decl, write_visibility(decl, o); + write_is_artificial(decl, o); + + write_is_non_reachable(is_type(decl), o); + write_location(decl, ctxt); write_class_or_union_is_declaration_only(decl, o); @@ -4329,6 +4409,8 @@ write_corpus(write_context& ctxt, if (!corpus->get_soname().empty()) out << " soname='" << corpus->get_soname()<< "'"; + write_tracking_non_reachable_types(corpus, out); + if (corpus->is_empty()) { out << "/>\n"; @@ -4424,6 +4506,8 @@ std::ostream& out = ctxt.get_ostream(); if (!group->get_architecture_name().empty() && ctxt.get_write_architecture()) out << " architecture='" << group->get_architecture_name()<< "'"; + write_tracking_non_reachable_types(group, out); + if (group->is_empty()) { out << "/>\n"; diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 30e17a0f..16026545 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -1240,6 +1240,27 @@ test-diff-suppr/test45-abi.xml \ test-diff-suppr/test46-PR25128-base.xml \ test-diff-suppr/test46-PR25128-new.xml \ test-diff-suppr/test46-PR25128-report-1.txt \ +test-diff-suppr/test47-non-reachable-types-report-1.txt \ +test-diff-suppr/test47-non-reachable-types-report-2.txt \ +test-diff-suppr/test47-non-reachable-types-report-3.txt \ +test-diff-suppr/test47-non-reachable-types-report-4.txt \ +test-diff-suppr/test47-non-reachable-types-report-5.txt \ +test-diff-suppr/test47-non-reachable-types-report-6.txt \ +test-diff-suppr/test47-non-reachable-types-report-7.txt \ +test-diff-suppr/test47-non-reachable-types-report-8.txt \ +test-diff-suppr/test47-non-reachable-types-report-9.txt \ +test-diff-suppr/test47-non-reachable-types-suppr-1.txt \ +test-diff-suppr/test47-non-reachable-types-suppr-2.txt \ +test-diff-suppr/test47-non-reachable-types-suppr-3.txt \ +test-diff-suppr/test47-non-reachable-types-suppr-4.txt \ +test-diff-suppr/test47-non-reachable-types-suppr-5.txt \ +test-diff-suppr/test47-non-reachable-types-v0.c \ +test-diff-suppr/test47-non-reachable-types-v0.o \ +test-diff-suppr/test47-non-reachable-types-v1.c \ +test-diff-suppr/test47-non-reachable-types-v1.o \ +test-diff-suppr/test47-non-reachable-types-v0.o.alltypes.abixml \ +test-diff-suppr/test47-non-reachable-types-v1.o.alltypes.abixml \ +test-diff-suppr/test47-non-reachable-types-report-10.txt \ \ test-diff-dwarf-abixml/test0-pr19026-libvtkIOSQL-6.1.so.1 \ test-diff-dwarf-abixml/test0-pr19026-libvtkIOSQL-6.1.so.1.abi \ @@ -1529,6 +1550,15 @@ test-diff-pkg/PR24410-old/poppler-qt5-0.73.0-4.fc30.x86_64.rpm \ test-diff-pkg/PR24410-old/poppler-qt5-debuginfo-0.73.0-4.fc30.x86_64.rpm \ test-diff-pkg/PR24410-old/poppler-qt5-devel-0.73.0-4.fc30.x86_64.rpm \ test-diff-pkg/PR24410-report-0.txt \ +test-diff-pkg/PR24690/PR24690-report-0.txt \ +test-diff-pkg/PR24690/flatpak-debuginfo-1.2.4-3.fc30.x86_64.rpm \ +test-diff-pkg/PR24690/flatpak-debuginfo-1.4.0-1.fc30.x86_64.rpm \ +test-diff-pkg/PR24690/flatpak-devel-1.2.4-3.fc30.x86_64.rpm \ +test-diff-pkg/PR24690/flatpak-devel-1.4.0-1.fc30.x86_64.rpm \ +test-diff-pkg/PR24690/flatpak-libs-1.2.4-3.fc30.x86_64.rpm \ +test-diff-pkg/PR24690/flatpak-libs-1.4.0-1.fc30.x86_64.rpm \ +test-diff-pkg/PR24690/flatpak-libs-debuginfo-1.2.4-3.fc30.x86_64.rpm \ +test-diff-pkg/PR24690/flatpak-libs-debuginfo-1.4.0-1.fc30.x86_64.rpm \ \ test-fedabipkgdiff/dbus-glib-0.104-3.fc23.x86_64.rpm \ test-fedabipkgdiff/dbus-glib-debuginfo-0.104-3.fc23.x86_64.rpm \ diff --git a/tests/data/test-abidiff/test-struct1-report.txt b/tests/data/test-abidiff/test-struct1-report.txt index 49f33c16..7f372cd7 100644 --- a/tests/data/test-abidiff/test-struct1-report.txt +++ b/tests/data/test-abidiff/test-struct1-report.txt @@ -1,27 +1,26 @@ 5 changed types: - 'const s0' changed: - in unqualified underlying type 'class s0': - type size changed from 192 to 256 (in bits) - 1 member function insertion: - 'method virtual int s0::foo(int, char) const', virtual at voffset 2/2 + 'class s0' changed: + type size changed from 192 to 256 (in bits) + 1 member function insertion: + 'method virtual int s0::foo(int, char) const', virtual at voffset 2/2 - 1 data member deletion: - 'char s0::m1', at offset 96 (in bits) + 1 data member deletion: + 'char s0::m1', at offset 96 (in bits) - 1 data member insertion: - 'double s0::m01', at offset 128 (in bits) - 2 data member changes: - type of 'int s0::m0' changed: - type name changed from 'int' to 'char' - type size changed from 32 to 8 (in bits) - type alignement changed from 32 to 8 + 1 data member insertion: + 'double s0::m01', at offset 128 (in bits) + 2 data member changes: + type of 'int s0::m0' changed: + type name changed from 'int' to 'char' + type size changed from 32 to 8 (in bits) + type alignement changed from 32 to 8 - 'unsigned int s0::m2' offset changed from 128 to 192 (in bits) (by +64 bits) + 'unsigned int s0::m2' offset changed from 128 to 192 (in bits) (by +64 bits) + 'const s0' changed: + unqualified underlying type 'class s0' changed, as reported earlier 'const s0*' changed: in pointed to type 'const s0': unqualified underlying type 'class s0' changed, as reported earlier - 'class s0' changed: - details were reported earlier 's0&' changed: referenced type 'class s0' changed, as reported earlier 's0*' changed: diff --git a/tests/data/test-diff-pkg/PR24690/PR24690-report-0.txt b/tests/data/test-diff-pkg/PR24690/PR24690-report-0.txt new file mode 100644 index 00000000..d106bf9b --- /dev/null +++ b/tests/data/test-diff-pkg/PR24690/PR24690-report-0.txt @@ -0,0 +1,51 @@ +================ changes of 'libflatpak.so.0.10204.0'=============== + Functions changes summary: 0 Removed, 0 Changed (16 filtered out), 16 Added functions + Variables changes summary: 0 Removed, 0 Changed, 0 Added variable + Unreachable types summary: 3 removed (2 filtered out), 1 changed (15 filtered out), 3 added (1 filtered out) types + + 16 Added functions: + + 'function gboolean flatpak_installation_add_remote(FlatpakInstallation*, FlatpakRemote*, gboolean, GCancellable*, GError**)' {flatpak_installation_add_remote} + 'function FlatpakRemoteRef* flatpak_installation_fetch_remote_ref_sync_full(FlatpakInstallation*, const char*, FlatpakRefKind, const char*, const char*, const char*, FlatpakQueryFlags, GCancellable*, GError**)' {flatpak_installation_fetch_remote_ref_sync_full} + 'function GPtrArray* flatpak_installation_list_remote_refs_sync_full(FlatpakInstallation*, const char*, FlatpakQueryFlags, GCancellable*, GError**)' {flatpak_installation_list_remote_refs_sync_full} + 'function GType flatpak_query_flags_get_type()' {flatpak_query_flags_get_type} + 'function char* flatpak_remote_get_comment(FlatpakRemote*)' {flatpak_remote_get_comment} + 'function char* flatpak_remote_get_description(FlatpakRemote*)' {flatpak_remote_get_description} + 'function char* flatpak_remote_get_filter(FlatpakRemote*)' {flatpak_remote_get_filter} + 'function char* flatpak_remote_get_homepage(FlatpakRemote*)' {flatpak_remote_get_homepage} + 'function char* flatpak_remote_get_icon(FlatpakRemote*)' {flatpak_remote_get_icon} + 'function FlatpakRemote* flatpak_remote_new_from_file(const char*, GBytes*, GError**)' {flatpak_remote_new_from_file} + 'function void flatpak_remote_set_comment(FlatpakRemote*, const char*)' {flatpak_remote_set_comment} + 'function void flatpak_remote_set_description(FlatpakRemote*, const char*)' {flatpak_remote_set_description} + 'function void flatpak_remote_set_filter(FlatpakRemote*, const char*)' {flatpak_remote_set_filter} + 'function void flatpak_remote_set_homepage(FlatpakRemote*, const char*)' {flatpak_remote_set_homepage} + 'function void flatpak_remote_set_icon(FlatpakRemote*, const char*)' {flatpak_remote_set_icon} + 'function gboolean flatpak_transaction_add_rebase(FlatpakTransaction*, const char*, const char*, const char**, const char**, GError**)' {flatpak_transaction_add_rebase} + + 3 removed types unreachable from any public interface: + + struct _GUnixFDList at gunixfdlist.h:58:1 + typedef GUnixFDList at giotypes.h:549:1 + typedef GUnixFDListPrivate at gunixfdlist.h:41:1 + + 1 changed type unreachable from any public interface: + + 'struct _FlatpakTransactionClass' changed: + type size changed from 2176 to 2240 (in bits) + 1 data member insertion: + 'typedef gboolean (FlatpakTransaction*, const char*, const char*, const char*, const char*, const char**)* _FlatpakTransactionClass::end_of_lifed_with_rebase', at offset 1408 (in bits) at flatpak-transaction.h:117:1 + 4 data member changes (3 filtered): + 'typedef gboolean (FlatpakTransaction*)* _FlatpakTransactionClass::ready' offset changed from 1408 to 1472 (in bits) (by +64 bits) + 'typedef gboolean (FlatpakTransaction*, typedef FlatpakTransactionRemoteReason, const char*, const char*, const char*)* _FlatpakTransactionClass::add_new_remote' offset changed from 1472 to 1536 (in bits) (by +64 bits) + 'typedef gboolean (FlatpakTransaction*, GCancellable*, GError**)* _FlatpakTransactionClass::run' offset changed from 1536 to 1600 (in bits) (by +64 bits) + 'gpointer _FlatpakTransactionClass::padding[9]' offset changed from 1600 to 1664 (in bits) (by +64 bits) + + + 3 added types unreachable from any public interface: + + 'struct gpgme_data_cbs' at gpgme-64.h:1139:1 + 'typedef GSubprocess' at giotypes.h:642:1 + 'typedef OstreeRepoResolveRevExtFlags' at ostree-repo.h:473:1 + +================ end of changes of 'libflatpak.so.0.10204.0'=============== + diff --git a/tests/data/test-diff-pkg/PR24690/flatpak-debuginfo-1.2.4-3.fc30.x86_64.rpm b/tests/data/test-diff-pkg/PR24690/flatpak-debuginfo-1.2.4-3.fc30.x86_64.rpm new file mode 100644 index 00000000..bb85cd94 Binary files /dev/null and b/tests/data/test-diff-pkg/PR24690/flatpak-debuginfo-1.2.4-3.fc30.x86_64.rpm differ diff --git a/tests/data/test-diff-pkg/PR24690/flatpak-debuginfo-1.4.0-1.fc30.x86_64.rpm b/tests/data/test-diff-pkg/PR24690/flatpak-debuginfo-1.4.0-1.fc30.x86_64.rpm new file mode 100644 index 00000000..6ece8ac8 Binary files /dev/null and b/tests/data/test-diff-pkg/PR24690/flatpak-debuginfo-1.4.0-1.fc30.x86_64.rpm differ diff --git a/tests/data/test-diff-pkg/PR24690/flatpak-devel-1.2.4-3.fc30.x86_64.rpm b/tests/data/test-diff-pkg/PR24690/flatpak-devel-1.2.4-3.fc30.x86_64.rpm new file mode 100644 index 00000000..f34b434c Binary files /dev/null and b/tests/data/test-diff-pkg/PR24690/flatpak-devel-1.2.4-3.fc30.x86_64.rpm differ diff --git a/tests/data/test-diff-pkg/PR24690/flatpak-devel-1.4.0-1.fc30.x86_64.rpm b/tests/data/test-diff-pkg/PR24690/flatpak-devel-1.4.0-1.fc30.x86_64.rpm new file mode 100644 index 00000000..75c8d41a Binary files /dev/null and b/tests/data/test-diff-pkg/PR24690/flatpak-devel-1.4.0-1.fc30.x86_64.rpm differ diff --git a/tests/data/test-diff-pkg/PR24690/flatpak-libs-1.2.4-3.fc30.x86_64.rpm b/tests/data/test-diff-pkg/PR24690/flatpak-libs-1.2.4-3.fc30.x86_64.rpm new file mode 100644 index 00000000..844dcc1b Binary files /dev/null and b/tests/data/test-diff-pkg/PR24690/flatpak-libs-1.2.4-3.fc30.x86_64.rpm differ diff --git a/tests/data/test-diff-pkg/PR24690/flatpak-libs-1.4.0-1.fc30.x86_64.rpm b/tests/data/test-diff-pkg/PR24690/flatpak-libs-1.4.0-1.fc30.x86_64.rpm new file mode 100644 index 00000000..877bf7ba Binary files /dev/null and b/tests/data/test-diff-pkg/PR24690/flatpak-libs-1.4.0-1.fc30.x86_64.rpm differ diff --git a/tests/data/test-diff-pkg/PR24690/flatpak-libs-debuginfo-1.2.4-3.fc30.x86_64.rpm b/tests/data/test-diff-pkg/PR24690/flatpak-libs-debuginfo-1.2.4-3.fc30.x86_64.rpm new file mode 100644 index 00000000..42d42143 Binary files /dev/null and b/tests/data/test-diff-pkg/PR24690/flatpak-libs-debuginfo-1.2.4-3.fc30.x86_64.rpm differ diff --git a/tests/data/test-diff-pkg/PR24690/flatpak-libs-debuginfo-1.4.0-1.fc30.x86_64.rpm b/tests/data/test-diff-pkg/PR24690/flatpak-libs-debuginfo-1.4.0-1.fc30.x86_64.rpm new file mode 100644 index 00000000..3e976136 Binary files /dev/null and b/tests/data/test-diff-pkg/PR24690/flatpak-libs-debuginfo-1.4.0-1.fc30.x86_64.rpm differ diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-report-1.txt b/tests/data/test-diff-suppr/test47-non-reachable-types-report-1.txt new file mode 100644 index 00000000..5efd5df5 --- /dev/null +++ b/tests/data/test-diff-suppr/test47-non-reachable-types-report-1.txt @@ -0,0 +1,20 @@ +Functions changes summary: 0 Removed, 0 Changed, 0 Added function +Variables changes summary: 0 Removed, 0 Changed, 0 Added variable +Unreachable types summary: 1 removed, 1 changed, 1 added types + +1 removed type unreachable from any public interface: + + struct unreachable_struct1 at test-v0.c:6:1 + +1 changed type unreachable from any public interface: + + 'struct unreachable_struct2' changed: + type size changed from 32 to 64 (in bits) + 1 data member insertion: + 'char unreachable_struct2::m2', at offset 32 (in bits) at test-v1.c:9:1 + + +1 added type unreachable from any public interface: + + 'struct unreachable_struct3' at test-v1.c:12:1 + diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-report-10.txt b/tests/data/test-diff-suppr/test47-non-reachable-types-report-10.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-report-2.txt b/tests/data/test-diff-suppr/test47-non-reachable-types-report-2.txt new file mode 100644 index 00000000..21005c2a --- /dev/null +++ b/tests/data/test-diff-suppr/test47-non-reachable-types-report-2.txt @@ -0,0 +1,16 @@ +Functions changes summary: 0 Removed, 0 Changed, 0 Added function +Variables changes summary: 0 Removed, 0 Changed, 0 Added variable +Unreachable types summary: 0 removed (1 filtered out), 1 changed, 1 added types + +1 changed type unreachable from any public interface: + + 'struct unreachable_struct2' changed: + type size changed from 32 to 64 (in bits) + 1 data member insertion: + 'char unreachable_struct2::m2', at offset 32 (in bits) at test-v1.c:9:1 + + +1 added type unreachable from any public interface: + + 'struct unreachable_struct3' at test-v1.c:12:1 + diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-report-3.txt b/tests/data/test-diff-suppr/test47-non-reachable-types-report-3.txt new file mode 100644 index 00000000..d0736bf3 --- /dev/null +++ b/tests/data/test-diff-suppr/test47-non-reachable-types-report-3.txt @@ -0,0 +1,13 @@ +Functions changes summary: 0 Removed, 0 Changed, 0 Added function +Variables changes summary: 0 Removed, 0 Changed, 0 Added variable +Unreachable types summary: 1 removed, 0 changed (1 filtered out), 1 added types + +1 removed type unreachable from any public interface: + + struct unreachable_struct1 at test-v0.c:6:1 + + +1 added type unreachable from any public interface: + + 'struct unreachable_struct3' at test-v1.c:12:1 + diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-report-4.txt b/tests/data/test-diff-suppr/test47-non-reachable-types-report-4.txt new file mode 100644 index 00000000..1004cdf2 --- /dev/null +++ b/tests/data/test-diff-suppr/test47-non-reachable-types-report-4.txt @@ -0,0 +1,16 @@ +Functions changes summary: 0 Removed, 0 Changed, 0 Added function +Variables changes summary: 0 Removed, 0 Changed, 0 Added variable +Unreachable types summary: 1 removed, 1 changed, 0 added (1 filtered out) types + +1 removed type unreachable from any public interface: + + struct unreachable_struct1 at test-v0.c:6:1 + +1 changed type unreachable from any public interface: + + 'struct unreachable_struct2' changed: + type size changed from 32 to 64 (in bits) + 1 data member insertion: + 'char unreachable_struct2::m2', at offset 32 (in bits) at test-v1.c:9:1 + + diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-report-5.txt b/tests/data/test-diff-suppr/test47-non-reachable-types-report-5.txt new file mode 100644 index 00000000..11d0d202 --- /dev/null +++ b/tests/data/test-diff-suppr/test47-non-reachable-types-report-5.txt @@ -0,0 +1,5 @@ +Functions changes summary: 0 Removed, 0 Changed, 0 Added function +Variables changes summary: 0 Removed, 0 Changed, 0 Added variable +Unreachable types summary: 0 removed (1 filtered out), 0 changed (1 filtered out), 0 added (1 filtered out) types + + diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-report-6.txt b/tests/data/test-diff-suppr/test47-non-reachable-types-report-6.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-report-7.txt b/tests/data/test-diff-suppr/test47-non-reachable-types-report-7.txt new file mode 100644 index 00000000..51af7fea --- /dev/null +++ b/tests/data/test-diff-suppr/test47-non-reachable-types-report-7.txt @@ -0,0 +1,22 @@ +Leaf changes summary: 0 artifact changed +Changed leaf types summary: 0 leaf type changed +Removed/Changed/Added functions summary: 0 Removed, 0 Changed, 0 Added function +Removed/Changed/Added variables summary: 0 Removed, 0 Changed, 0 Added variable +Unreachable types summary: 1 removed, 1 changed, 1 added types + +1 removed type unreachable from any public interface: + + struct unreachable_struct1 at test-v0.c:6:1 + +1 changed type unreachable from any public interface: + + 'struct unreachable_struct2' changed: + type size changed from 32 to 64 (in bits) + 1 data member insertion: + 'char unreachable_struct2::m2', at offset 32 (in bits) at test-v1.c:9:1 + + +1 added type unreachable from any public interface: + + 'struct unreachable_struct3' at test-v1.c:12:1 + diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-report-8.txt b/tests/data/test-diff-suppr/test47-non-reachable-types-report-8.txt new file mode 100644 index 00000000..46e854b6 --- /dev/null +++ b/tests/data/test-diff-suppr/test47-non-reachable-types-report-8.txt @@ -0,0 +1,7 @@ +Leaf changes summary: 0 artifact changed +Changed leaf types summary: 0 leaf type changed +Removed/Changed/Added functions summary: 0 Removed, 0 Changed, 0 Added function +Removed/Changed/Added variables summary: 0 Removed, 0 Changed, 0 Added variable +Unreachable types summary: 0 removed (1 filtered out), 0 changed (1 filtered out), 0 added (1 filtered out) types + + diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-report-9.txt b/tests/data/test-diff-suppr/test47-non-reachable-types-report-9.txt new file mode 100644 index 00000000..e69de29b diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-suppr-1.txt b/tests/data/test-diff-suppr/test47-non-reachable-types-suppr-1.txt new file mode 100644 index 00000000..7c38db8a --- /dev/null +++ b/tests/data/test-diff-suppr/test47-non-reachable-types-suppr-1.txt @@ -0,0 +1,3 @@ +[suppress_type] + name = unreachable_struct1 + diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-suppr-2.txt b/tests/data/test-diff-suppr/test47-non-reachable-types-suppr-2.txt new file mode 100644 index 00000000..7a1630b5 --- /dev/null +++ b/tests/data/test-diff-suppr/test47-non-reachable-types-suppr-2.txt @@ -0,0 +1,3 @@ +[suppress_type] + name = unreachable_struct2 + diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-suppr-3.txt b/tests/data/test-diff-suppr/test47-non-reachable-types-suppr-3.txt new file mode 100644 index 00000000..83dcad70 --- /dev/null +++ b/tests/data/test-diff-suppr/test47-non-reachable-types-suppr-3.txt @@ -0,0 +1,3 @@ +[suppress_type] + name = unreachable_struct3 + diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-suppr-4.txt b/tests/data/test-diff-suppr/test47-non-reachable-types-suppr-4.txt new file mode 100644 index 00000000..4e32841a --- /dev/null +++ b/tests/data/test-diff-suppr/test47-non-reachable-types-suppr-4.txt @@ -0,0 +1,2 @@ +[suppress_type] + name_regexp = unreachable_struct. diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-suppr-5.txt b/tests/data/test-diff-suppr/test47-non-reachable-types-suppr-5.txt new file mode 100644 index 00000000..db75c0c2 --- /dev/null +++ b/tests/data/test-diff-suppr/test47-non-reachable-types-suppr-5.txt @@ -0,0 +1,4 @@ +[suppress_type] + name_regexp = unreachable_struct. + drop = true + diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-v0.c b/tests/data/test-diff-suppr/test47-non-reachable-types-v0.c new file mode 100644 index 00000000..f04d8313 --- /dev/null +++ b/tests/data/test-diff-suppr/test47-non-reachable-types-v0.c @@ -0,0 +1,28 @@ +struct reachable_struct1 +{ + int m0; +}; + +struct unreachable_struct1 +{ + int m1; +}; + +struct unreachable_struct2 +{ + int m1; +}; + +void foo(struct reachable_struct1* __attribute__((unused)) s) +{ +} + +static void private_func() +{ + struct unreachable_struct1 s1; + s1.m1 = 0; + + struct unreachable_struct2 s0; + s0.m1 = 0; + +} diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-v0.o b/tests/data/test-diff-suppr/test47-non-reachable-types-v0.o new file mode 100644 index 00000000..2b4786fb Binary files /dev/null and b/tests/data/test-diff-suppr/test47-non-reachable-types-v0.o differ diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-v0.o.alltypes.abixml b/tests/data/test-diff-suppr/test47-non-reachable-types-v0.o.alltypes.abixml new file mode 100644 index 00000000..eecad63d --- /dev/null +++ b/tests/data/test-diff-suppr/test47-non-reachable-types-v0.o.alltypes.abixml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-v1.c b/tests/data/test-diff-suppr/test47-non-reachable-types-v1.c new file mode 100644 index 00000000..43fbc79c --- /dev/null +++ b/tests/data/test-diff-suppr/test47-non-reachable-types-v1.c @@ -0,0 +1,27 @@ +struct reachable_struct1 +{ + int m0; +}; + +struct unreachable_struct2 +{ + int m1; + char m2; +}; + +struct unreachable_struct3 +{ + int m1; +}; + +void foo(struct reachable_struct1* __attribute__((unused)) s) +{ +} + +static void private_func() +{ + struct unreachable_struct2 s0; + s0.m1 = 0; + struct unreachable_struct3 s1; + s1.m1 = 0; +} diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-v1.o b/tests/data/test-diff-suppr/test47-non-reachable-types-v1.o new file mode 100644 index 00000000..fe387115 Binary files /dev/null and b/tests/data/test-diff-suppr/test47-non-reachable-types-v1.o differ diff --git a/tests/data/test-diff-suppr/test47-non-reachable-types-v1.o.alltypes.abixml b/tests/data/test-diff-suppr/test47-non-reachable-types-v1.o.alltypes.abixml new file mode 100644 index 00000000..ddfd4b57 --- /dev/null +++ b/tests/data/test-diff-suppr/test47-non-reachable-types-v1.o.alltypes.abixml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/test-diff-pkg.cc b/tests/test-diff-pkg.cc index 95338beb..d8fa24f5 100644 --- a/tests/test-diff-pkg.cc +++ b/tests/test-diff-pkg.cc @@ -605,6 +605,20 @@ static InOutSpec in_out_specs[] = "data/test-diff-pkg/PR24410-report-0.txt", "output/test-diff-pkg/PR24410-report-0.txt" }, + { + "data/test-diff-pkg/PR24690/flatpak-libs-1.2.4-3.fc30.x86_64.rpm", + "data/test-diff-pkg/PR24690/flatpak-libs-1.4.0-1.fc30.x86_64.rpm", + "--non-reachable-types", + "", + "data/test-diff-pkg/PR24690/flatpak-debuginfo-1.2.4-3.fc30.x86_64.rpm, " + "data/test-diff-pkg/PR24690/flatpak-libs-debuginfo-1.2.4-3.fc30.x86_64.rpm", + "data/test-diff-pkg/PR24690/flatpak-debuginfo-1.4.0-1.fc30.x86_64.rpm, " + "data/test-diff-pkg/PR24690/flatpak-libs-debuginfo-1.4.0-1.fc30.x86_64.rpm", + "data/test-diff-pkg/PR24690/flatpak-devel-1.2.4-3.fc30.x86_64.rpm", + "data/test-diff-pkg/PR24690/flatpak-devel-1.4.0-1.fc30.x86_64.rpm", + "data/test-diff-pkg/PR24690/PR24690-report-0.txt", + "output/test-diff-pkg/PR24690/PR24690-report-0.txt" + }, #endif //WITH_RPM #ifdef WITH_DEB diff --git a/tests/test-diff-suppr.cc b/tests/test-diff-suppr.cc index a8e50969..5d9528fe 100644 --- a/tests/test-diff-suppr.cc +++ b/tests/test-diff-suppr.cc @@ -1838,6 +1838,116 @@ InOutSpec in_out_specs[] = "data/test-diff-suppr/test46-PR25128-report-1.txt", "output/test-diff-suppr/test46-PR25128-report-1.txt" }, + { + "data/test-diff-suppr/test47-non-reachable-types-v0.o", + "data/test-diff-suppr/test47-non-reachable-types-v1.o", + "", + "", + "", + "--no-default-suppression --non-reachable-types", + "data/test-diff-suppr/test47-non-reachable-types-report-1.txt", + "output/test-diff-suppr/test47-non-reachable-types-report-1.txt" + }, + { + "data/test-diff-suppr/test47-non-reachable-types-v0.o", + "data/test-diff-suppr/test47-non-reachable-types-v1.o", + "", + "", + "data/test-diff-suppr/test47-non-reachable-types-suppr-1.txt", + "--no-default-suppression --non-reachable-types", + "data/test-diff-suppr/test47-non-reachable-types-report-2.txt", + "output/test-diff-suppr/test47-non-reachable-types-report-2.txt" + }, + { + "data/test-diff-suppr/test47-non-reachable-types-v0.o", + "data/test-diff-suppr/test47-non-reachable-types-v1.o", + "", + "", + "data/test-diff-suppr/test47-non-reachable-types-suppr-2.txt", + "--no-default-suppression --non-reachable-types", + "data/test-diff-suppr/test47-non-reachable-types-report-3.txt", + "output/test-diff-suppr/test47-non-reachable-types-report-3.txt" + }, + { + "data/test-diff-suppr/test47-non-reachable-types-v0.o", + "data/test-diff-suppr/test47-non-reachable-types-v1.o", + "", + "", + "data/test-diff-suppr/test47-non-reachable-types-suppr-3.txt", + "--no-default-suppression --non-reachable-types", + "data/test-diff-suppr/test47-non-reachable-types-report-4.txt", + "output/test-diff-suppr/test47-non-reachable-types-report-4.txt" + }, + { + "data/test-diff-suppr/test47-non-reachable-types-v0.o", + "data/test-diff-suppr/test47-non-reachable-types-v1.o", + "", + "", + "data/test-diff-suppr/test47-non-reachable-types-suppr-4.txt", + "--no-default-suppression --non-reachable-types", + "data/test-diff-suppr/test47-non-reachable-types-report-5.txt", + "output/test-diff-suppr/test47-non-reachable-types-report-5.txt" + }, + { + "data/test-diff-suppr/test47-non-reachable-types-v0.o", + "data/test-diff-suppr/test47-non-reachable-types-v1.o", + "", + "", + "data/test-diff-suppr/test47-non-reachable-types-suppr-5.txt", + "--no-default-suppression --non-reachable-types", + "data/test-diff-suppr/test47-non-reachable-types-report-6.txt", + "output/test-diff-suppr/test47-non-reachable-types-report-6.txt" + }, + { + "data/test-diff-suppr/test47-non-reachable-types-v0.o", + "data/test-diff-suppr/test47-non-reachable-types-v1.o", + "", + "", + "", + "--no-default-suppression --non-reachable-types --leaf-changes-only", + "data/test-diff-suppr/test47-non-reachable-types-report-7.txt", + "output/test-diff-suppr/test47-non-reachable-types-report-7.txt" + }, + { + "data/test-diff-suppr/test47-non-reachable-types-v0.o", + "data/test-diff-suppr/test47-non-reachable-types-v1.o", + "", + "", + "data/test-diff-suppr/test47-non-reachable-types-suppr-4.txt", + "--no-default-suppression --non-reachable-types --leaf-changes-only", + "data/test-diff-suppr/test47-non-reachable-types-report-8.txt", + "output/test-diff-suppr/test47-non-reachable-types-report-8.txt" + }, + { + "data/test-diff-suppr/test47-non-reachable-types-v0.o", + "data/test-diff-suppr/test47-non-reachable-types-v1.o", + "", + "", + "data/test-diff-suppr/test47-non-reachable-types-suppr-5.txt", + "--no-default-suppression --non-reachable-types --leaf-changes-only", + "data/test-diff-suppr/test47-non-reachable-types-report-9.txt", + "output/test-diff-suppr/test47-non-reachable-types-report-9.txt" + }, + { + "data/test-diff-suppr/test47-non-reachable-types-v0.o.alltypes.abixml", + "data/test-diff-suppr/test47-non-reachable-types-v1.o.alltypes.abixml", + "", + "", + "", + "--no-default-suppression --non-reachable-types", + "data/test-diff-suppr/test47-non-reachable-types-report-1.txt", + "output/test-diff-suppr/test47-non-reachable-types-report-1.txt" + }, + { + "data/test-diff-suppr/test47-non-reachable-types-v0.o.alltypes.abixml", + "data/test-diff-suppr/test47-non-reachable-types-v1.o.alltypes.abixml", + "", + "", + "", + "--no-default-suppression", + "data/test-diff-suppr/test47-non-reachable-types-report-10.txt", + "output/test-diff-suppr/test47-non-reachable-types-report-10.txt" + }, // This should be the last entry {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL} }; diff --git a/tools/abidiff.cc b/tools/abidiff.cc index a8b78289..1edb1b40 100644 --- a/tools/abidiff.cc +++ b/tools/abidiff.cc @@ -101,6 +101,7 @@ struct options bool show_changed_vars; bool show_added_vars; bool show_all_vars; + bool show_all_types; bool show_linkage_names; bool show_locs; bool show_harmful_changes; @@ -141,6 +142,7 @@ struct options show_changed_vars(), show_added_vars(), show_all_vars(true), + show_all_types(false), show_linkage_names(true), show_locs(true), show_harmful_changes(true), @@ -203,6 +205,8 @@ display_usage(const string& prog_name, ostream& out) << " --deleted-vars display deleted global public variables\n" << " --changed-vars display changed global public variables\n" << " --added-vars display added global public variables\n" + << " --non-reachable-types|-t consider types non reachable" + " from public interfaces\n" << " --no-added-syms do not display added functions or variables\n" << " --no-linkage-name do not display linkage names of " "added/removed/changed\n" @@ -399,6 +403,9 @@ parse_command_line(int argc, char* argv[], options& opts) opts.show_all_fns = false; opts.show_all_vars = false; } + else if (!strcmp(argv[i], "--non-reachable-types") + || !strcmp(argv[i], "-t")) + opts.show_all_types = true; else if (!strcmp(argv[i], "--no-added-syms")) { opts.show_added_syms = false; @@ -647,6 +654,7 @@ set_diff_context_from_opts(diff_context_sptr ctxt, (opts.show_symbols_not_referenced_by_debug_info); ctxt->show_added_symbols_unreferenced_by_debug_info (opts.show_symbols_not_referenced_by_debug_info && opts.show_added_syms); + ctxt->show_unreachable_types(opts.show_all_types); ctxt->show_impacted_interfaces(opts.show_impacted_interfaces); if (!opts.show_harmless_changes) @@ -769,6 +777,20 @@ set_suppressions(ReadContextType& read_ctxt, const options& opts) add_read_context_suppressions(read_ctxt, supprs); } +/// Configure the abigail::xml_reacher::read_context based on the +/// relevant command-line options. +/// +/// @param ctxt the read context to configure. +/// +/// @param opts the command-line options to configure @p ctxt from. +static void +set_native_xml_reader_options(abigail::xml_reader::read_context& ctxt, + const options& opts) +{ + consider_types_not_reachable_from_public_interfaces(ctxt, + opts.show_all_types); +} + /// Set the regex patterns describing the functions to drop from the /// symbol table of a given corpus. /// @@ -1059,13 +1081,14 @@ main(int argc, char* argv[]) t1 = abigail::xml_reader::read_translation_unit_from_file(opts.file1, env.get()); break; - case abigail::tools_utils::FILE_TYPE_ELF: + case abigail::tools_utils::FILE_TYPE_ELF: // fall through case abigail::tools_utils::FILE_TYPE_AR: { abigail::dwarf_reader::read_context_sptr ctxt = abigail::dwarf_reader::create_read_context - (opts.file1, opts.prepared_di_root_paths1, env.get(), - /*readalltypes*/false, opts.linux_kernel_mode); + (opts.file1, opts.prepared_di_root_paths1, + env.get(), /*readalltypes=*/opts.show_all_types, + opts.linux_kernel_mode); assert(ctxt); abigail::dwarf_reader::set_show_stats(*ctxt, opts.show_stats); @@ -1087,6 +1110,7 @@ main(int argc, char* argv[]) env.get()); assert(ctxt); set_suppressions(*ctxt, opts); + set_native_xml_reader_options(*ctxt, opts); c1 = abigail::xml_reader::read_corpus_from_input(*ctxt); if (!c1) return handle_error(c1_status, /*ctxt=*/0, @@ -1100,6 +1124,7 @@ main(int argc, char* argv[]) env.get()); assert(ctxt); set_suppressions(*ctxt, opts); + set_native_xml_reader_options(*ctxt, opts); g1 = abigail::xml_reader::read_corpus_group_from_input(*ctxt); if (!g1) return handle_error(c1_status, /*ctxt=*/0, @@ -1132,13 +1157,14 @@ main(int argc, char* argv[]) t2 = abigail::xml_reader::read_translation_unit_from_file(opts.file2, env.get()); break; - case abigail::tools_utils::FILE_TYPE_ELF: + case abigail::tools_utils::FILE_TYPE_ELF: // Fall through case abigail::tools_utils::FILE_TYPE_AR: { abigail::dwarf_reader::read_context_sptr ctxt = abigail::dwarf_reader::create_read_context - (opts.file2, opts.prepared_di_root_paths2, env.get(), - /*readalltypes=*/false, opts.linux_kernel_mode); + (opts.file2, opts.prepared_di_root_paths2, + env.get(), /*readalltypes=*/opts.show_all_types, + opts.linux_kernel_mode); assert(ctxt); abigail::dwarf_reader::set_show_stats(*ctxt, opts.show_stats); abigail::dwarf_reader::set_do_log(*ctxt, opts.do_log); @@ -1160,6 +1186,7 @@ main(int argc, char* argv[]) env.get()); assert(ctxt); set_suppressions(*ctxt, opts); + set_native_xml_reader_options(*ctxt, opts); c2 = abigail::xml_reader::read_corpus_from_input(*ctxt); if (!c2) return handle_error(c2_status, /*ctxt=*/0, argv[0], opts); @@ -1173,6 +1200,7 @@ main(int argc, char* argv[]) env.get()); assert(ctxt); set_suppressions(*ctxt, opts); + set_native_xml_reader_options(*ctxt, opts); g2 = abigail::xml_reader::read_corpus_group_from_input(*ctxt); if (!g2) return handle_error(c2_status, /*ctxt=*/0, argv[0], opts); diff --git a/tools/abipkgdiff.cc b/tools/abipkgdiff.cc index f3065fdf..ce60f676 100644 --- a/tools/abipkgdiff.cc +++ b/tools/abipkgdiff.cc @@ -192,6 +192,7 @@ public: bool compare_dso_only; bool compare_private_dsos; bool leaf_changes_only; + bool show_all_types; bool show_hexadecimal_values; bool show_offsets_sizes_in_bits; bool show_impacted_interfaces; @@ -226,6 +227,7 @@ public: compare_dso_only(), compare_private_dsos(), leaf_changes_only(), + show_all_types(), show_hexadecimal_values(), show_offsets_sizes_in_bits(true), show_impacted_interfaces(), @@ -853,6 +855,8 @@ display_usage(const string& prog_name, ostream& out) "interfaces impacted by ABI changes\n" << " --full-impact|-f when comparing kernel packages, show the " "full impact analysis report rather than the default leaf changes reports\n" + << " --non-reachable-types|-t consider types non reachable" + " from public interfaces\n" << " --no-linkage-name do not display linkage names of " "added/removed/changed\n" << " --redundant display redundant changes\n" @@ -1173,6 +1177,7 @@ set_diff_context_from_opts(diff_context_sptr ctxt, ctxt->show_redundant_changes(opts.show_redundant_changes); ctxt->show_leaf_changes_only(opts.leaf_changes_only); ctxt->show_impacted_interfaces(opts.show_impacted_interfaces); + ctxt->show_unreachable_types(opts.show_all_types); ctxt->show_hex_values(opts.show_hexadecimal_values); ctxt->show_offsets_sizes_in_bits(opts.show_offsets_sizes_in_bits); ctxt->show_relative_offset_changes(opts.show_relative_offset_changes); @@ -1294,8 +1299,9 @@ compare(const elf_file& elf1, corpus_sptr corpus1; { - read_context_sptr c = create_read_context(elf1.path, di_dirs1, env.get(), - /*load_all_types=*/false); + read_context_sptr c = + create_read_context(elf1.path, di_dirs1, env.get(), + /*load_all_types=*/opts.show_all_types); add_read_context_suppressions(*c, priv_types_supprs1); if (!opts.kabi_suppressions.empty()) add_read_context_suppressions(*c, opts.kabi_suppressions); @@ -1379,8 +1385,9 @@ compare(const elf_file& elf1, corpus_sptr corpus2; { - read_context_sptr c = create_read_context(elf2.path, di_dirs2, env.get(), - /*load_all_types=*/false); + read_context_sptr c = + create_read_context(elf2.path, di_dirs2, env.get(), + /*load_all_types=*/opts.show_all_types); add_read_context_suppressions(*c, priv_types_supprs2); if (!opts.kabi_suppressions.empty()) @@ -2809,6 +2816,9 @@ parse_command_line(int argc, char* argv[], options& opts) else if (!strcmp(argv[i], "--impacted-interfaces") ||!strcmp(argv[i], "-i")) opts.show_impacted_interfaces = true; + else if (!strcmp(argv[i], "--non-reachable-types") + ||!strcmp(argv[i], "-t")) + opts.show_all_types = true; else if (!strcmp(argv[i], "--full-impact") ||!strcmp(argv[i], "-f")) opts.show_full_impact_report = true;