Initial implementation of a --leaf-changes-only option to abidiff

This patch allows abidiff to take the --leaf-changes-only option and
then to display only the changes that are local to any given type.
That means the reporting agent won't follow pointers when displaying
changes.  That gives less context to the ABI change reports but then
they are less cluttered.

To do this, the patch introduces a new reporting agent to libabigail:
abigail::comparison::leaf_reporter.  When given a graph of diff nodes,
this agent only reports about the leaf (local) changes.  That is, it
will *NOT* follow pointers, references, underlying types of qualified
and typedef types and things like that. It will just report about
changes that are local to a given type.

This reporting agent is then used (in lieu of the default
abigail::comparison::default_reporter agent) when the
--leaf-changes-only option is provided by the user on the command line
of abidiff.

Note that abidiff also takes the --impacted-interfaces option to so
that the leaf reporter shows the set of interfaces impacted
by each leaf change.

	* doc/manuals/abidiff.rst: Add documentation the new
	--leaf-changes-only and --impacted-interfaces options.
	* src/abg-leaf-reporter.cc: New file.
	* src/Makefile.am: Add the new src/abg-leaf-reporter.cc file to
	source distribution.
	* include/abg-fwd.h (get_var_size_in_bits)
	(function_decl_is_less_than): Declare new functions.
	(get_name): Add new overload for type_or_decl_base*.
	* include/abg-ir.h (struct type_or_decl_hash, type_or_decl_equal)
	(type_or_decl_base_comp): Define new types.
	(artifact_sptr_set_type, artifact_ptr_set_type): Define new
	typedefs.
	* include/abg-comp-filter.h: Update copyright year.
	(has_basic_type_name_change): Add new function declaration.
	* src/abg-comp-filter.cc (decl_name_changed): Take a
	type_or_decl_base rather than just a decl.  Add an overload for
	diff*.
	(has_basic_type_name_change): Define new function.
	* include/abg-comparison.h: Update copyright year.
	(string_diff_ptr_map): Define this new typedef.
	(class diff_maps): Define this new class.
	(diff_context::{set_corpora}): Remove this member function.
	(diff_context::{set_corpus_diff, get_corpus_diff,
	show_leaf_changes_only, show_impacted_interfaces,
	forbid_visiting_a_node_twice_per_interface}): Declare these new
	member functions.
	(diff_node_visitor::priv_): Add a new pimpl data member.
	(diff_node_visitor::{diff_node_visitor, get_visiting_kind,
	set_visiting_kind}): Turn these into out-of-line member functions.
	(diff_node_visitor::{set,get}_current_topmost_iface_diff): Add new
	member functions.
	(class {scope_diff, function_type_diff, corpus_diff}): Add class
	leaf_reporter as a friend.
	(corpus_diff::mark_leaf_diff_nodes, get_leaf_diffs): Declare new
	member functions.
	(diff::{visiting_a_node_twice_is_forbidden_per_interface,
	parent_interface_node}): Define new member functions.
	(is_diff_of_basic_type): Return a type_decl_diff* rather than just
	a bool.
	(is_enum_diff, is_array_diff, is_function_type, is_typedef_diff)
	(is_corpus_diff): Declare new functions.
	(corpus_diff::diff_stats::{num_leaf_changes,
	num_leaf_changes_filtered_out, net_num_leaf_changes}): Add new
	member functions.
	(is_distinct_diff): Declare new function.
	* include/abg-reporter.h: Forward-declare "class diff_maps".
	(reporter_base::diff_to_be_reported): Declare a new virtual member
	function.
	(reporter_base::{report_local_typedef_changes,
	report_local_reference_type_changes,
	report_local_function_type_changes}): Declare new member
	functions.
	(class leaf_reporter): Define new type.
	* src/abg-comparison-priv.h (struct diff_hash, diff_equal): Define
	new types.
	(diff_artifact_set_map_type): Define new typedef.
	(diff_context::priv::{first_corpus_, second_corpus_}): Remove
	these data members.
	(diff_context::priv::{corpus_diff_, leaf_changes_only_,
	reset_visited_diffs_for_each_interface_,
	show_impacted_interfaces_}): Add new data members.
	(diff_context::priv::priv): Adjust.
	(corpus_diff::priv::{leaf_diffs_, parent_interface_}): Add new
	data member.
	(corpus_diff::diff_stats::priv::{num_leaf_changes,
	num_leaf_changes_filtered_out}): Add new data members.
	(corpus_diff::priv::count_leaf_changes): Define new member
	function.
	(sort_artifacts_set, get_fn_decl_or_var_decl_diff_ancestor)
	(is_diff_of_global_decls): Declare new functions.
	(function_comp::operator()): Factorize this out into the new
	function abigail::ir::function_decl_is_less_than.
	* src/abg-ir.cc (get_var_size_in_bits)
	(function_decl_is_less_than): Define new functions.
	(get_name): Define new overload for type_or_decl_base*.
	* src/abg-comparison.cc (is_enum_diff, is_typedef_diff)
	(is_array_diff, is_function_type_diff, is_corpus_diff)
	(is_distinct_diff, sort_artifacts_set, is_diff_of_global_decls):
	Define new functions.
	(is_union_diff): Fix comment.
	(diff_context::forbid_visiting_a_node_twice_per_interface): Define
	new member functions.
	(diff_context::set_corpus_diff, get_corpus_diff)
	(diff_context::show_leaf_changes_only)
	(diff_context::visiting_a_node_twice_is_forbidden_per_interface)
	(diff_context::show_impacted_interfaces): Define new member
	functions.
	(diff_context::get_reporter): Create the reporter that matches
	what diff_context::show_leaf_changes_only says.
	(diff_node_visitor::priv): Define a new type.
	(diff_node_visitor::{diff_node_visitor, get_visiting_kind,
	set_visiting_kind, or_visiting_kind,
	set_current_topmost_iface_diff, get_current_topmost_iface_diff}):
	Define new out-of-line member functions.
	(struct diff_maps::priv): Define new type.
	(diff_maps::{diff_maps, get_type_decl_diff_map,
	get_type_decl_diff_map, get_enum_diff_map, get_class_diff_map,
	get_union_diff_map, get_typedef_diff_map, get_array_diff_map,
	get_function_type_diff_map, get_function_decl_diff_map,
	get_var_decl_diff_map, get_reference_diff_map,
	get_fn_parm_diff_map, get_distinct_diff_map, insert_diff_node,
	lookup_impacted_interfaces}): Define member functions.
	(corpus_diff::{mark_leaf_diff_nodes, get_leaf_diffs}): Define new
	member functions.
	(struct leaf_diff_node_marker_visitor): Define new type.
	(corpus_diff::apply_filters_and_suppressions_before_reporting):
	Mark diff nodes in here.
	(corpus_diff::traverse): Appropriately set the current topmost
	interface into the visitor before visiting a diff node.
	(compute_diff): In the overload for corpus_sptr, adjust to reflect
	that we are now storing the corpus_diff in the diff context.
	(is_diff_of_basic_type): Return a type_decl_diff*, not just a
	bool.
	(corpus_diff::priv::count_leaf_changes): Define a new member
	function.
	(corpus_diff::diff_stats::{num_leaf_changes,
	num_leaf_changes_filtered_out, net_num_leaf_changes}): Define new
	member functions.
	(corpus_diff::priv::apply_filters_and_compute_diff_stats): Use the
	new corpus_diff::priv::count_leaf_changes to compute the number of
	leaf changes.
	(corpus_diff::priv::emit_diff_stats): Emit the report about leaf
	type changes when necessary.
	* src/abg-reporter-priv.h (report_mem_header): Declare new
	overload.
	(maybe_show_relative_offset_change,): Pass the var_diff_sptr
	parameter by const reference.
	(represent): Pass the var_diff_sptr parameter by const reference
	and take a new "local-only" flag.
	(maybe_show_relative_size_change)
	(maybe_report_interfaces_impacted_by_diff): Declare new functions.
	* src/abg-default-reporter.cc: Adjust copyright year.
	(default_reporter::{report_local_typedef_changes,
	report_local_qualified_type_changes,
	report_local_reference_type_changes,
	report_local_function_type_changes}): Define new member functions.
	(default_reporter::report): Adjust.  Add an overload for
	function_type_diff&. In the overload for qualified_type_diff, if
	the name of the underlying type changed, do not detail the changes
	any further.  In the overload for function_decl_diff, Adjust to
	use the new diff_context::get_{first, second}_corpus member
	function.  In the overload for enum_diff, call the new
	maybe_report_interfaces_impacted_by_diff that is advertised below.
	* src/abg-reporter-priv.cc (represent): Adjust the overload for
	var_diff_sptr.
	(report_mem_header): Define new overload.
	(maybe_show_relative_size_change)
	(maybe_report_interfaces_impacted_by_diff): Define new functions.
	(reporter_base::diff_to_be_reported): Define new member function.
	(maybe_show_relative_offset_change): Pass the var_diff_sptr
	parameter by const reference.
	(represent): In the overload for var_diff_sptr, pass the
	var_diff_sptr parameter by reference.  Take a 'local_only' flag.
	Iisplay type changes only if we are not displaying "local changes
	only".  Display size changes of data members too, when in
	"local-only" mode.
	* src/abg-suppression.cc (sonames_of_binaries_match)
	(names_of_binaries_match): Adjust.
	* tools/abidiff.cc (options::{leaf_changes_only,
	show_impacted_interfaces}): Add new data members.
	(display_usage): Emit usage string for the new --leaf-changes-only
	and --impacted-interfaces options.
	(parse_command_line): Parse the new --leaf-changes-only and the
	--impacted-interfaces options.
	(set_diff_context_from_opts): Set the 'show-leaf-changes' and the
	'show-impacted-interfaces' flags.
	* tests/data/test-diff-filter/libtest42-leaf-report-v{0,1}.so: New
	test input.
	* tests/data/test-diff-filter/test42-leaf-report-output-0.txt: New
	test reference output.
	* tests/data/test-diff-filter/test42-leaf-report-v{0,1}.cc: Source
	code of the new test inputs.
	* tests/test-diff-filter.cc (in_out_specs): Use the new test
	inputs above in this harness.
	* tests/data/test-diff-suppr/libtest35-leaf-v0.so: New test input.
	* tests/data/test-diff-suppr/test35-leaf-report-0.txt: New test
	reference output.
	* tests/data/test-diff-suppr/test35-leaf-v{0,1}.cc: Source code of
	the new test inputs.
	* tests/data/test-diff-suppr/test35-leaf.suppr: Suppression
	specification to use for the test35 test.
	* tests/data/test-diff-suppr/libtest36-leaf-v0.so: New test input.
	* tests/data/test-diff-suppr/libtest36-leaf-v1.so: Likewise.
	* tests/data/test-diff-suppr/test36-leaf-report-0.txt: New
	reference test output.
	* tests/data/test-diff-suppr/test36-leaf-v0.cc: Source code of
	test input above.
	* tests/data/test-diff-suppr/test36-leaf-v1.cc: Likewise.
	* tests/test-diff-suppr.cc (in_out_specs): Use the new test inputs
	above in this harness.
	* tests/data/Makefile.am: Add the new test inputs above to source
	distribution.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
This commit is contained in:
Dodji Seketeli 2017-08-02 18:00:23 +02:00
parent 3824d8f3c2
commit 108a6074a5
36 changed files with 3335 additions and 235 deletions

View File

@ -330,6 +330,68 @@ Options
Do not emit the path attribute for the ABI corpus.
* ``--leaf-changes-only|-l`` only show leaf changes, so don't show
impact analysis report.
The typical output of abidiff when comparing two binaries looks
like this ::
$ abidiff libtest-v0.so libtest-v1.so
Functions changes summary: 0 Removed, 1 Changed, 0 Added function
Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
1 function with some indirect sub-type change:
[C]'function void fn(C&)' at test-v1.cc:13:1 has some indirect sub-type changes:
parameter 1 of type 'C&' has sub-type changes:
in referenced type 'struct C' at test-v1.cc:7:1:
type size hasn't changed
1 data member change:
type of 'leaf* C::m0' changed:
in pointed to type 'struct leaf' at test-v1.cc:1:1:
type size changed from 32 to 64 bits
1 data member insertion:
'char leaf::m1', at offset 32 (in bits) at test-v1.cc:4:1
$
So in that example the report emits information about how the data
member insertion change of "struct leaf" is reachable from
function "void fn(C&)". In other words, the report not only shows
the data member change on "struct leaf", but it also shows the
impact of that change on the function "void fn(C&)".
In abidiff parlance, the change on "struct leaf" is called a leaf
change. So the ``--leaf-changes-only --impacted-interfaces``
options show, well, only the leaf change. And it goes like this:
::
$ abidiff -l libtest-v0.so libtest-v1.so
'struct leaf' changed:
type size changed from 32 to 64 bits
1 data member insertion:
'char leaf::m1', at offset 32 (in bits) at test-v1.cc:4:1
one impacted interface:
function void fn(C&)
$
Note how the report ends by showing the list of interfaces
impacted by the leaf change.
Now if you don't want to see that list of impacted interfaces,
then you can just avoid using the ``--impacted-interface`` option.
You can learn about that option below, in any case.
* ``--impacted-interfaces``
When showing leaf changes, this option instructs abidiff to show
the list of impacted interfaces. This option is thus to be used
in addition the ``--leaf-changes-only`` option, otherwise, it's
ignored.
* ``--dump-diff-tree``
After the diff report, emit a textual representation of the diff

View File

@ -1,6 +1,6 @@
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2016 Red Hat, Inc.
// Copyright (C) 2013-2017 Red Hat, Inc.
//
// This file is part of the GNU Application Binary Interface Generic
// Analysis and Instrumentation Library (libabigail). This library is
@ -49,6 +49,9 @@ bool
has_class_decl_only_def_change(const class_or_union_sptr& first,
const class_or_union_sptr& second);
bool
has_basic_type_name_change(const diff *);
struct filter_base;
/// Convenience typedef for a shared pointer to filter_base
typedef shared_ptr<filter_base> filter_base_sptr;

View File

@ -1,6 +1,6 @@
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2016 Red Hat, Inc.
// Copyright (C) 2013-2017 Red Hat, Inc.
//
// This file is part of the GNU Application Binary Interface Generic
// Analysis and Instrumentation Library (libabigail). This library is
@ -182,6 +182,10 @@ typedef unordered_map<string,
/// of the map is the qualified name of the changed type.
typedef unordered_map<string, diff_sptr> string_diff_sptr_map;
/// Convenience typedef for a map which value is a diff*. The key of
/// the map is the qualified name of the changed type.
typedef unordered_map<string, diff*> string_diff_ptr_map;
/// Convenience typedef for a map whose key is a string and whose
/// value is a changed variable of type @ref var_diff_sptr.
typedef unordered_map<string,
@ -423,6 +427,99 @@ operator<<(ostream& o, diff_category);
class corpus_diff;
/// This type contains maps. Each map associates a type name to a
/// diff of that type. Not all kinds of diffs are present; only those
/// that carry leaf changes are, for now.
class diff_maps
{
struct priv;
typedef shared_ptr<priv> priv_sptr;
priv_sptr priv_;
public:
diff_maps();
const string_diff_ptr_map&
get_type_decl_diff_map() const;
string_diff_ptr_map&
get_type_decl_diff_map();
const string_diff_ptr_map&
get_enum_diff_map() const;
string_diff_ptr_map&
get_enum_diff_map();
const string_diff_ptr_map&
get_class_diff_map() const;
string_diff_ptr_map&
get_class_diff_map();
const string_diff_ptr_map&
get_union_diff_map() const;
string_diff_ptr_map&
get_union_diff_map();
const string_diff_ptr_map&
get_typedef_diff_map() const;
string_diff_ptr_map&
get_typedef_diff_map();
const string_diff_ptr_map&
get_array_diff_map() const;
string_diff_ptr_map&
get_array_diff_map();
const string_diff_ptr_map&
get_reference_diff_map() const;
string_diff_ptr_map&
get_reference_diff_map();
const string_diff_ptr_map&
get_fn_parm_diff_map() const;
string_diff_ptr_map&
get_fn_parm_diff_map();
const string_diff_ptr_map&
get_function_type_diff_map() const;
string_diff_ptr_map&
get_function_type_diff_map();
const string_diff_ptr_map&
get_function_decl_diff_map() const;
string_diff_ptr_map&
get_function_decl_diff_map();
const string_diff_ptr_map&
get_var_decl_diff_map() const;
string_diff_ptr_map&
get_var_decl_diff_map();
const string_diff_ptr_map&
get_distinct_diff_map() const;
string_diff_ptr_map&
get_distinct_diff_map();
bool
insert_diff_node(const diff *d,
const type_or_decl_base_sptr& impacted_iface);
artifact_sptr_set_type*
lookup_impacted_interfaces(const diff *d) const;
}; // end class diff_maps
/// A convenience typedef for a shared pointer to @ref corpus_diff.
typedef shared_ptr<corpus_diff> corpus_diff_sptr;
@ -473,13 +570,15 @@ public:
diff_context();
void
set_corpora(const corpus_sptr corp1,
const corpus_sptr corp2);
set_corpus_diff(const corpus_diff_sptr&);
const corpus_sptr
const corpus_diff_sptr&
get_corpus_diff() const;
corpus_sptr
get_first_corpus() const;
const corpus_sptr
corpus_sptr
get_second_corpus() const;
reporter_base_sptr
@ -528,6 +627,12 @@ public:
bool
visiting_a_node_twice_is_forbidden() const;
void
forbid_visiting_a_node_twice_per_interface(bool);
bool
visiting_a_node_twice_is_forbidden_per_interface() const;
diff_category
get_allowed_category() const;
@ -561,6 +666,12 @@ public:
void
add_suppressions(const suppr::suppressions_type& supprs);
void
show_leaf_changes_only(bool f);
bool
show_leaf_changes_only() const;
void
show_relative_offset_changes(bool f);
@ -651,6 +762,12 @@ public:
void
show_added_symbols_unreferenced_by_debug_info(bool f);
bool
show_impacted_interfaces() const;
void
show_impacted_interfaces(bool f);
void
default_output_stream(ostream*);
@ -1717,6 +1834,7 @@ public:
chain_into_hierarchy();
friend class default_reporter;
friend class leaf_reporter;
};// end class scope_diff
scope_diff_sptr
@ -1851,6 +1969,7 @@ public:
chain_into_hierarchy();
friend class default_reporter;
friend class leaf_reporter;
};// end class function_type_diff
function_type_diff_sptr
@ -2174,6 +2293,15 @@ public:
const diff_stats&
apply_filters_and_suppressions_before_reporting();
void
mark_leaf_diff_nodes();
diff_maps&
get_leaf_diffs();
const diff_maps&
get_leaf_diffs() const;
virtual void
report(ostream& out, const string& indent = "") const;
@ -2192,6 +2320,7 @@ public:
apply_suppressions(const corpus_diff* diff_tree);
friend class default_reporter;
friend class leaf_reporter;
}; // end class corpus_diff
corpus_diff_sptr
@ -2302,6 +2431,14 @@ public:
size_t net_num_removed_var_syms() const;
size_t net_num_added_var_syms() const;
size_t num_leaf_changes() const;
void num_leaf_changes(size_t);
size_t num_leaf_changes_filtered_out() const;
void num_leaf_changes_filtered_out(size_t);
size_t net_num_leaf_changes() const;
}; // end class corpus_diff::diff_stats
/// The base class for the node visitors. These are the types used to
@ -2309,50 +2446,30 @@ public:
class diff_node_visitor : public node_visitor_base
{
protected:
visiting_kind visiting_kind_;
struct priv;
typedef shared_ptr<priv> priv_sptr;
priv_sptr priv_;
public:
/// Default constructor of the @ref diff_node_visitor type.
diff_node_visitor()
: visiting_kind_()
{}
diff_node_visitor();
/// Constructor of the @ref diff_node_visitor type.
///
/// @param k how the visiting has to be performed.
diff_node_visitor(visiting_kind k)
: visiting_kind_(k)
{}
diff_node_visitor(visiting_kind k);
/// Getter for the visiting policy of the traversing code while
/// invoking this visitor.
///
/// @return the visiting policy used by the traversing code when
/// invoking this visitor.
visiting_kind
get_visiting_kind() const
{return visiting_kind_;}
get_visiting_kind() const;
/// Setter for the visiting policy of the traversing code while
/// invoking this visitor.
///
/// @param v a bit map representing the new visiting policy used by
/// the traversing code when invoking this visitor.
void
set_visiting_kind(visiting_kind v)
{visiting_kind_ = v;}
set_visiting_kind(visiting_kind v);
/// Setter for the visiting policy of the traversing code while
/// invoking this visitor. This one makes a logical or between the
/// current policy and the bitmap given in argument and assigns the
/// current policy to the result.
///
/// @param v a bitmap representing the visiting policy to or with
/// the current policy.
void
or_visiting_kind(visiting_kind v)
{visiting_kind_ = visiting_kind_ | v;}
or_visiting_kind(visiting_kind v);
void
set_current_topmost_iface_diff(diff*);
diff*
get_current_topmost_iface_diff() const;
virtual void
visit_begin(diff*);
@ -2496,18 +2613,30 @@ is_type_diff(const diff* diff);
const decl_diff_base*
is_decl_diff(const diff* diff);
bool
const type_decl_diff*
is_diff_of_basic_type(const diff* diff);
bool
has_basic_type_change_only(const diff* diff);
const enum_diff*
is_enum_diff(const diff *diff);
const class_diff*
is_class_diff(const diff* diff);
const union_diff*
is_union_diff(const diff* diff);
const array_diff*
is_array_diff(const diff* diff);
const function_type_diff*
is_function_type_diff(const diff* diff);
const typedef_diff*
is_typedef_diff(const diff *diff);
const var_diff*
is_var_diff(const diff* diff);
@ -2529,12 +2658,18 @@ is_fn_parm_diff(const diff* diff);
const base_diff*
is_base_diff(const diff* diff);
const distinct_diff*
is_distinct_diff(const diff *diff);
bool
is_child_node_of_function_parm_diff(const diff* diff);
bool
is_child_node_of_base_diff(const diff* diff);
const corpus_diff*
is_corpus_diff(const diff* diff);
}// end namespace comparison
}// end namespace abigail

View File

@ -604,6 +604,9 @@ get_data_member_offset(const var_decl_sptr);
uint64_t
get_data_member_offset(const decl_base_sptr);
uint64_t
get_var_size_in_bits(const var_decl_sptr&);
void
set_data_member_is_laid_out(var_decl_sptr, bool);
@ -729,6 +732,9 @@ peel_typedef_pointer_or_reference_type(const type_base_sptr);
type_base*
peel_typedef_pointer_or_reference_type(const type_base*);
string
get_name(const type_or_decl_base*, bool qualified = true);
string
get_name(const type_or_decl_base_sptr&,
bool qualified = true);
@ -1173,6 +1179,10 @@ hash_type_or_decl(const type_or_decl_base *);
size_t
hash_type_or_decl(const type_or_decl_base_sptr &);
bool
function_decl_is_less_than(const function_decl&f, const function_decl &s);
} // end namespace ir
using namespace abigail::ir;

View File

@ -321,6 +321,90 @@ struct ir_traversable_base : public traversable_base
traverse(ir_node_visitor& v);
}; // end class ir_traversable_base
/// The hashing functor for using instances of @ref type_or_decl_base
/// as values in a hash map or set.
struct type_or_decl_hash
{
/// Function-call Operator to hash the string representation of an
/// ABI artifact.
///
/// @param artifact the ABI artifact to hash.
///
/// @return the hash value of the string representation of @p
/// artifact.
size_t
operator()(const type_or_decl_base *artifact) const
{
string repr = get_pretty_representation(artifact);
std::tr1::hash<string> do_hash;
return do_hash(repr);
}
/// Function-call Operator to hash the string representation of an
/// ABI artifact.
///
/// @param artifact the ABI artifact to hash.
///
/// @return the hash value of the string representation of @p
/// artifact.
size_t
operator()(const type_or_decl_base_sptr& artifact) const
{return operator()(artifact.get());}
}; // end struct type_or_decl_hash
/// The comparison functor for using instances of @ref
/// type_or_decl_base as values in a hash map or set.
struct type_or_decl_equal
{
/// The function-call operator to compare the string representations
/// of two ABI artifacts.
///
/// @param l the left hand side ABI artifact operand of the
/// comparison.
///
/// @param r the right hand side ABI artifact operand of the
/// comparison.
///
/// @return true iff the string representation of @p l equals the one
/// of @p r.
bool
operator()(const type_or_decl_base *l, const type_or_decl_base *r) const
{
string repr1 = get_pretty_representation(l);
string repr2 = get_pretty_representation(r);
return repr1 == repr2;
}
/// The function-call operator to compare the string representations
/// of two ABI artifacts.
///
/// @param l the left hand side ABI artifact operand of the
/// comparison.
///
/// @param r the right hand side ABI artifact operand of the
/// comparison.
///
/// @return true iff the string representation of @p l equals the one
/// of @p r.
bool
operator()(const type_or_decl_base_sptr &l,
const type_or_decl_base_sptr &r) const
{return operator()(l.get(), r.get());}
}; // end type_or_decl_equal
/// A convenience typedef for a hash set of type_or_decl_base_sptr
typedef unordered_set<type_or_decl_base_sptr,
type_or_decl_hash,
type_or_decl_equal> artifact_sptr_set_type;
/// A convenience typedef for a hash set of const type_or_decl_base*
typedef unordered_set<const type_or_decl_base*,
type_or_decl_hash,
type_or_decl_equal> artifact_ptr_set_type;
/// A convenience typedef for a map which key is a string and which
/// value is a @ref type_base_wptr.
typedef unordered_map<string, type_base_wptr> string_type_base_wptr_map_type;
@ -2492,6 +2576,48 @@ equals(const function_decl::parameter&,
const function_decl::parameter&,
change_kind*);
/// A comparison functor to compare pointer to instances of @ref
/// type_or_decl_base.
struct type_or_decl_base_comp
{
/// Comparison operator for ABI artifacts.
///
/// @param f the first ABI artifact to consider for the comparison.
///
/// @param s the second ABI artifact to consider for the comparison.
///
/// @return true iff @p f is lexicographically less than than @p s.
bool
operator()(const type_or_decl_base *f,
const type_or_decl_base *s)
{
function_decl *f_fn = is_function_decl(f), *s_fn = is_function_decl(s);
if (f_fn && s_fn)
return function_decl_is_less_than(*f_fn, *s_fn);
var_decl *f_var = is_var_decl(f), *s_var = is_var_decl(s);
if (f_var && s_var)
return get_name(f_var) < get_name(s_var);
string l_repr = get_pretty_representation(f),
r_repr = get_pretty_representation(s);
return l_repr < r_repr;
}
/// Comparison operator for ABI artifacts.
///
/// @param f the first ABI artifact to consider for the comparison.
///
/// @param s the second ABI artifact to consider for the comparison.
///
/// @return true iff @p f is lexicographically less than than @p s.
bool
operator()(const type_or_decl_base_sptr& f,
const type_or_decl_base_sptr& s)
{return operator()(f.get(), s.get());}
}; // end struct type_or_decl_base_comp
/// Abstraction of a function parameter.
class function_decl::parameter : public decl_base
{

View File

@ -52,7 +52,7 @@ class function_decl_diff;
class var_diff;
class translation_unit_diff;
class corpus_diff;
class diff_maps;
class reporter_base;
/// A convenience typedef for a shared pointer to a @ref
@ -64,6 +64,8 @@ class reporter_base
{
public:
virtual bool diff_to_be_reported(const diff *d) const;
virtual void
report(const type_decl_diff& d, ostream& out,
const string& indent = "") const = 0;
@ -159,10 +161,20 @@ public:
report(const enum_diff& d, ostream& out,
const string& indent = "") const;
bool
report_local_typedef_changes(const typedef_diff &d,
ostream& out,
const string& indent) const;
virtual void
report(const typedef_diff& d, ostream& out,
const string& indent = "") const;
bool
report_local_qualified_type_changes(const qualified_type_diff& d,
ostream& out,
const string& indent) const;
virtual void
report(const qualified_type_diff& d, ostream& out,
const string& indent = "") const;
@ -170,6 +182,11 @@ public:
virtual void
report(const pointer_diff& d, ostream& out, const string& indent = "") const;
void
report_local_reference_type_changes(const reference_diff& d,
ostream& out,
const string& indent) const;
virtual void
report(const reference_diff& d, ostream& out,
const string& indent = "") const;
@ -178,6 +195,11 @@ public:
report(const fn_parm_diff& d, ostream& out,
const string& indent = "") const;
void
report_local_function_type_changes(const function_type_diff& d,
ostream& out,
const string& indent) const;
virtual void
report(const function_type_diff& d, ostream& out,
const string& indent = "") const;
@ -220,6 +242,77 @@ public:
const string& indent = "") const;
}; // end class default_reporter
/// A reporter that only reports leaf changes
class leaf_reporter : public default_reporter
{
public:
virtual bool diff_to_be_reported(const diff *d) const;
void
report_changes_from_diff_maps(const diff_maps&,
ostream& out,
const string& indent) const;
virtual void
report(const typedef_diff& d, ostream& out,
const string& indent = "") const;
virtual void
report(const qualified_type_diff& d, ostream& out,
const string& indent = "") const;
virtual void
report(const pointer_diff& d, ostream& out,
const string& indent = "") const;
virtual void
report(const reference_diff& d, ostream& out,
const string& indent = "") const;
virtual void
report(const fn_parm_diff& d, ostream& out,
const string& indent = "") const;
virtual void
report(const function_type_diff& d, ostream& out,
const string& indent = "") const;
virtual void
report(const array_diff& d, ostream& out, const string& indent = "") const;
virtual void
report(const scope_diff& d, ostream& out, const string& indent = "") const;
virtual void
report(const class_or_union_diff& d, ostream& out,
const string& indent = "") const;
virtual void
report(const class_diff& d, ostream& out, const string& indent = "") const;
virtual void
report(const union_diff& d, ostream& out, const string& indent = "") const;
virtual void
report(const distinct_diff& d, ostream& out, const string& indent = "") const;
virtual void
report(const function_decl_diff& d, ostream& out,
const string& indent = "") const;
virtual void
report(const var_diff& d, ostream& out, const string& indent = "") const;
virtual void
report(const translation_unit_diff& d, ostream& out,
const string& indent = "") const;
virtual void
report(const corpus_diff& d, ostream& out,
const string& indent = "") const;
}; // end class leaf_reporter
} // end namespace comparison
} // end namespace abigail

View File

@ -26,6 +26,7 @@ abg-comparison-priv.h \
abg-reporter-priv.h \
abg-reporter-priv.cc \
abg-default-reporter.cc \
abg-leaf-reporter.cc \
abg-suppression-priv.h \
abg-suppression.cc \
abg-comp-filter.cc \

View File

@ -362,10 +362,18 @@ is_compatible_change(const decl_base_sptr& d1, const decl_base_sptr& d2)
///
/// @return true if d1 and d2 have different names.
static bool
decl_name_changed(const decl_base_sptr& d1, const decl_base_sptr& d2)
decl_name_changed(const type_or_decl_base* a1, const type_or_decl_base *a2)
{
string d1_name, d2_name;
const decl_base *d1 = dynamic_cast<const decl_base*>(a1);
if (d1 == 0)
return false;
const decl_base *d2 = dynamic_cast<const decl_base*>(a2);
if (d2 == 0)
return false;
if (d1)
d1_name = d1->get_qualified_name();
if (d2)
@ -374,6 +382,29 @@ decl_name_changed(const decl_base_sptr& d1, const decl_base_sptr& d2)
return d1_name != d2_name;
}
/// Test if two decls have different names.
///
/// @param d1 the first declaration to consider.
///
/// @param d2 the second declaration to consider.
///
/// @return true if d1 and d2 have different names.
static bool
decl_name_changed(const type_or_decl_base_sptr& d1,
const type_or_decl_base_sptr& d2)
{return decl_name_changed(d1.get(), d2.get());}
/// Test if a diff nodes carries a changes in which two decls have
/// different names.
///
/// @param d the diff node to consider.
///
/// @return true iff d carries a changes in which two decls have
/// different names.
static bool
decl_name_changed(const diff *d)
{return decl_name_changed(d->first_subject(), d->second_subject());}
/// Test if two decls represents a harmless name change.
///
/// For now, a harmless name change is a name change for a typedef,
@ -776,6 +807,20 @@ has_class_decl_only_def_change(const diff *diff)
return has_class_decl_only_def_change(f, s);
}
/// Test if a diff node carries a basic type name change.
///
/// @param d the diff node to consider.
///
/// @return true iff the diff node carries a basic type name change.
bool
has_basic_type_name_change(const diff *d)
{
if (const type_decl_diff *dif = is_diff_of_basic_type(d))
if (decl_name_changed(dif))
return true;
return false;
}
/// Test if an enum_diff carries an enumerator insertion.
///

View File

@ -91,6 +91,94 @@ typedef unordered_map<types_or_decls_type, diff_sptr,
types_or_decls_hash, types_or_decls_equal>
types_or_decls_diff_map_type;
/// A hashing functor for using @ref diff_sptr and @ref diff* in a
/// hash map or set.
struct diff_hash
{
/// The function-call operator to hash a @ref diff node.
///
/// @param d the @ref diff node to hash.
///
/// @return the hash value of @p d.
size_t
operator()(const diff_sptr& d) const
{return operator()(*d);}
/// The function-call operator to hash a @ref diff node.
///
/// @param d the @ref diff node to hash.
///
/// @return the hash value of @p d.
size_t
operator()(const diff *d) const
{return operator()(*d);}
/// The function-call operator to hash a @ref diff node.
///
/// @param d the @ref diff node to hash.
///
/// @return the hash value of @p d.
size_t
operator()(const diff& d) const
{
diff* canonical_diff = d.get_canonical_diff();
assert(canonical_diff);
return reinterpret_cast<size_t>(canonical_diff);
}
}; // end struct diff_hash
/// A comparison functor for using @ref diff_sptr and @ref diff* in a
/// hash map or set.
struct diff_equal
{
/// The function-call operator to compare two @ref diff nodes.
///
/// @param d1 the first diff node involved in the comparison.
///
/// @param d2 the second diff node involved in the comparison.
///
/// @return true iff @p d1 equals @p d2.
bool
operator()(const diff* d1, const diff* d2) const
{return operator()(*d1, *d2);}
/// The function-call operator to compare two @ref diff nodes.
///
/// @param d1 the first diff node involved in the comparison.
///
/// @param d2 the second diff node involved in the comparison.
///
/// @return true iff @p d1 equals @p d2.
bool
operator()(const diff_sptr& d1, const diff_sptr& d2) const
{return operator()(*d1, *d2);}
/// The function-call operator to compare two @ref diff nodes.
///
/// @param d1 the first diff node involved in the comparison.
///
/// @param d2 the second diff node involved in the comparison.
///
/// @return true iff @p d1 equals @p d2.
bool
operator()(const diff& d1, const diff& d2) const
{
diff* canonical_diff1 = d1.get_canonical_diff();
assert(canonical_diff1);
diff *canonical_diff2 = d2.get_canonical_diff();
assert(canonical_diff2);
return canonical_diff1 == canonical_diff2;
}
}; // end struct diff_equal
/// A convenience typedef for an unordered_map which key is a @ref
/// diff* and which value is a @ref artifact_sptr_set_type.
typedef unordered_map<const diff*, artifact_sptr_set_type,
diff_hash, diff_equal>
diff_artifact_set_map_type;
/// The private member (pimpl) for @ref diff_context.
struct diff_context::priv
{
@ -105,11 +193,12 @@ struct diff_context::priv
// This is the last visited diff node, per class of equivalence.
// It's set during the redundant diff node marking process.
pointer_map last_visited_diff_node_;
corpus_sptr first_corpus_;
corpus_sptr second_corpus_;
corpus_diff_sptr corpus_diff_;
ostream* default_output_stream_;
ostream* error_output_stream_;
bool leaf_changes_only_;
bool forbid_visiting_a_node_twice_;
bool reset_visited_diffs_for_each_interface_;
bool show_relative_offset_changes_;
bool show_stats_only_;
bool show_soname_change_;
@ -125,14 +214,17 @@ struct diff_context::priv
bool show_redundant_changes_;
bool show_syms_unreferenced_by_di_;
bool show_added_syms_unreferenced_by_di_;
bool show_impacted_interfaces_;
bool dump_diff_tree_;
priv()
: allowed_category_(EVERYTHING_CATEGORY),
reporter_(new default_reporter),
reporter_(),
default_output_stream_(),
error_output_stream_(),
leaf_changes_only_(),
forbid_visiting_a_node_twice_(true),
reset_visited_diffs_for_each_interface_(),
show_relative_offset_changes_(true),
show_stats_only_(false),
show_soname_change_(true),
@ -148,6 +240,7 @@ struct diff_context::priv
show_redundant_changes_(true),
show_syms_unreferenced_by_di_(true),
show_added_syms_unreferenced_by_di_(true),
show_impacted_interfaces_(true),
dump_diff_tree_()
{}
};// end struct diff_context::priv
@ -167,6 +260,7 @@ struct diff::priv
type_or_decl_base_sptr second_subject_;
vector<diff*> children_;
diff* parent_;
diff* parent_interface_;
diff* canonical_diff_;
diff_context_wptr ctxt_;
diff_category local_category_;
@ -190,6 +284,7 @@ public:
first_subject_(first_subject),
second_subject_(second_subject),
parent_(),
parent_interface_(),
canonical_diff_(),
ctxt_(ctxt),
local_category_(category),
@ -888,6 +983,7 @@ 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_;
diff_maps leaf_diffs_;
/// Default constructor of corpus_diff::priv.
priv()
@ -953,6 +1049,8 @@ struct corpus_diff::priv
bool
added_unrefed_var_sym_is_suppressed(const elf_symbol*) const;
void count_leaf_changes(size_t &, size_t&);
void
apply_filters_and_compute_diff_stats(corpus_diff::diff_stats&);
@ -985,31 +1083,7 @@ struct function_comp
/// @return true iff @p f is less than @p s.
bool
operator()(const function_decl& f, const function_decl& s)
{
string fr = f.get_pretty_representation_of_declarator(),
sr = s.get_pretty_representation_of_declarator();
if (fr != sr)
return fr < sr;
fr = f.get_pretty_representation(),
sr = s.get_pretty_representation();
if (fr != sr)
return fr < sr;
if (f.get_symbol())
fr = f.get_symbol()->get_id_string();
else if (!f.get_linkage_name().empty())
fr = f.get_linkage_name();
if (s.get_symbol())
sr = s.get_symbol()->get_id_string();
else if (!s.get_linkage_name().empty())
sr = s.get_linkage_name();
return fr < sr;
}
{return abigail::ir::function_decl_is_less_than(f, s);}
/// The actual "less than" operator for instances of @ref
/// function_decl. It returns true if the first @ref function_decl
@ -1145,6 +1219,8 @@ struct corpus_diff::diff_stats::priv
size_t num_removed_var_syms_filtered_out;
size_t num_var_syms_added;
size_t num_added_var_syms_filtered_out;
size_t num_leaf_changes;
size_t num_leaf_changes_filtered_out;
priv(diff_context_sptr ctxt)
: ctxt_(ctxt),
@ -1244,9 +1320,19 @@ void
sort_string_parm_map(const string_parm_map& map,
vector<function_decl::parameter_sptr>& sorted);
void
sort_artifacts_set(const artifact_sptr_set_type& set,
vector<type_or_decl_base_sptr>& sorted);
type_base_sptr
get_leaf_type(qualified_type_def_sptr t);
diff*
get_fn_decl_or_var_decl_diff_ancestor(const diff *);
bool
is_diff_of_global_decls(const diff*);
} // end namespace comparison
} // namespace abigail

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2017 Red Hat, Inc.
// Copyright (C) 2017 Red Hat, Inc.
//
// This file is part of the GNU Application Binary Interface Generic
// Analysis and Instrumentation Library (libabigail). This library is
@ -181,6 +181,51 @@ default_reporter::report(const enum_diff& d, ostream& out,
}
out << "\n\n";
}
if (d.context()->show_leaf_changes_only())
maybe_report_interfaces_impacted_by_diff(&d, out, indent,
/*new_line_prefix=*/false);
}
/// For a @ref typedef_diff node, report the changes that are local.
///
/// @param d the @ref typedef_diff node to consider.
///
/// @param out the output stream to report to.
///
/// @param indent the white space string to use for indentation.
///
/// @return true iff the caller needs to emit a newline to the output
/// stream before emitting anything else.
bool
default_reporter::report_local_typedef_changes(const typedef_diff &d,
ostream& out,
const string& indent) const
{
if (!d.to_be_reported())
return false;
bool emit_nl = false;
typedef_decl_sptr f = d.first_typedef_decl(), s = d.second_typedef_decl();
maybe_report_diff_for_member(f, s, d.context(), out, indent);
if (filtering::has_harmless_name_change(f, s)
&& ((d.context()->get_allowed_category()
& HARMLESS_DECL_NAME_CHANGE_CATEGORY)
||
d.context()->show_leaf_changes_only()))
{
out << indent << "typedef name changed from "
<< f->get_qualified_name()
<< " to "
<< s->get_qualified_name();
report_loc_info(s, *d.context(), out);
out << "\n";
emit_nl = true;
}
return emit_nl;
}
/// Reports the difference between the two subjects of the diff in a
@ -192,31 +237,17 @@ default_reporter::report(const enum_diff& d, ostream& out,
///
/// @param indent the indentation string to use.
void
default_reporter::report(const typedef_diff& d, ostream& out,
default_reporter::report(const typedef_diff& d,
ostream& out,
const string& indent) const
{
if (!d.to_be_reported())
return;
bool emit_nl = false;
typedef_decl_sptr f = d.first_typedef_decl(), s = d.second_typedef_decl();
RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER(f, s);
maybe_report_diff_for_member(f, s, d.context(), out, indent);
if (filtering::has_harmless_name_change(f, s)
&& (d.context()->get_allowed_category()
& HARMLESS_DECL_NAME_CHANGE_CATEGORY))
{
out << indent << "typedef name changed from "
<< f->get_qualified_name()
<< " to "
<< s->get_qualified_name();
report_loc_info(s, *d.context(), out);
out << "\n";
emit_nl = true;
}
bool emit_nl = report_local_typedef_changes(d, out, indent);
diff_sptr dif = d.underlying_type_diff();
if (dif && dif->to_be_reported())
@ -235,6 +266,36 @@ default_reporter::report(const typedef_diff& d, ostream& out,
out << "\n";
}
/// For a @ref qualified_type_diff node, report the changes that are
/// local.
///
/// @param d the @ref qualified_type_diff node to consider.
///
/// @param out the output stream to emit the report to.
///
/// @param indent the white string to use for indentation.
///
/// @return true iff a local change has been emitted. In this case,
/// the local change is a name change.
bool
default_reporter::report_local_qualified_type_changes(const qualified_type_diff& d,
ostream& out,
const string& indent) const
{
if (!d.to_be_reported())
return false;
string fname = d.first_qualified_type()->get_pretty_representation(),
sname = d.second_qualified_type()->get_pretty_representation();
if (fname != sname)
{
out << indent << "'" << fname << "' changed to '" << sname << "'\n";
return true;
}
return false;
}
/// Report a @ref qualified_type_diff in a serialized form.
///
/// @param d the @ref qualified_type_diff node to consider.
@ -249,17 +310,14 @@ default_reporter::report(const qualified_type_diff& d, ostream& out,
if (!d.to_be_reported())
return;
string fname = d.first_qualified_type()->get_pretty_representation(),
sname = d.second_qualified_type()->get_pretty_representation();
RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER(d.first_qualified_type(),
d.second_qualified_type());
if (fname != sname)
{
out << indent << "'" << fname << "' changed to '" << sname << "'\n";
return;
}
if (report_local_qualified_type_changes(d, out, indent))
// The local change was emitted and it's a name change. If the
// type name changed, the it means the type changed altogether.
// It makes a little sense to detail the changes in extenso here.
return;
diff_sptr dif = d.leaf_underlying_type_diff();
assert(dif);
@ -305,16 +363,18 @@ default_reporter::report(const pointer_diff& d, ostream& out,
}
}
/// Report a @ref reference_diff in a serialized form.
/// For a @reference_diff node, report the local changes carried by
/// the diff node.
///
/// @param d the @ref reference_diff node to consider.
/// @param d the @reference_diff node to consider.
///
/// @param out the output stream to serialize the dif to.
/// @param out the output stream to report to.
///
/// @param indent the string to use for indenting the report.
/// @param indent the white space indentation to use in the report.
void
default_reporter::report(const reference_diff& d, ostream& out,
const string& indent) const
default_reporter::report_local_reference_type_changes(const reference_diff& d,
ostream& out,
const string& indent) const
{
if (!d.to_be_reported())
return;
@ -340,6 +400,23 @@ default_reporter::report(const reference_diff& d, ostream& out,
<< s_repr
<< "'\n";
}
}
/// Report a @ref reference_diff in a serialized form.
///
/// @param d the @ref reference_diff node to consider.
///
/// @param out the output stream to serialize the dif to.
///
/// @param indent the string to use for indenting the report.
void
default_reporter::report(const reference_diff& d, ostream& out,
const string& indent) const
{
if (!d.to_be_reported())
return;
report_local_reference_type_changes(d, out, indent);
if (diff_sptr dif = d.underlying_type_diff())
{
@ -394,16 +471,19 @@ default_reporter::report(const fn_parm_diff& d, ostream& out,
}
}
/// Build and emit a textual report about a @ref function_type_diff.
/// For a @ref function_type_diff node, report the local changes
/// carried by the diff node.
///
/// @param d the @ref function_type_diff to consider.
/// @param d the @ref function_type_diff node to consider.
///
/// @param out the output stream.
/// @param out the output stream to report to.
///
/// @param indent the indentation string to use.
/// @param indent the white space indentation string to use.
void
default_reporter::report(const function_type_diff& d, ostream& out,
const string& indent) const
default_reporter::report_local_function_type_changes(const function_type_diff& d,
ostream& out,
const string& indent) const
{
if (!d.to_be_reported())
return;
@ -412,8 +492,6 @@ default_reporter::report(const function_type_diff& d, ostream& out,
function_type_sptr sft = d.second_function_type();
diff_context_sptr ctxt = d.context();
corpus_sptr fc = ctxt->get_first_corpus();
corpus_sptr sc = ctxt->get_second_corpus();
// Report about the size of the function address
if (fft->get_size_in_bits() != sft->get_size_in_bits())
@ -436,41 +514,9 @@ default_reporter::report(const function_type_diff& d, ostream& out,
<< " bits\n";
}
// Report about return type differences.
if (d.priv_->return_type_diff_
&& d.priv_->return_type_diff_->to_be_reported())
{
out << indent << "return type changed:\n";
d.priv_->return_type_diff_->report(out, indent + " ");
}
// Hmmh, the above was quick. Now report about function parameters;
// this shouldn't be as straightforward.
//
// Report about the parameter types that have changed sub-types.
for (vector<fn_parm_diff_sptr>::const_iterator i =
d.priv_->sorted_subtype_changed_parms_.begin();
i != d.priv_->sorted_subtype_changed_parms_.end();
++i)
{
diff_sptr dif = *i;
if (dif && dif->to_be_reported())
dif->report(out, indent);
}
// Report about parameters that have changed, while staying
// compatible -- otherwise they would have changed the mangled name
// of the function and the function would have been reported as
// removed.
for (vector<fn_parm_diff_sptr>::const_iterator i =
d.priv_->sorted_changed_parms_by_id_.begin();
i != d.priv_->sorted_changed_parms_by_id_.end();
++i)
{
diff_sptr dif = *i;
if (dif && dif->to_be_reported())
dif->report(out, indent);
}
// Report about the parameters that got removed.
bool emitted = false;
@ -504,6 +550,50 @@ default_reporter::report(const function_type_diff& d, ostream& out,
out << "\n";
}
/// Build and emit a textual report about a @ref function_type_diff.
///
/// @param d the @ref function_type_diff to consider.
///
/// @param out the output stream.
///
/// @param indent the indentation string to use.
void
default_reporter::report(const function_type_diff& d, ostream& out,
const string& indent) const
{
if (!d.to_be_reported())
return;
function_type_sptr fft = d.first_function_type();
function_type_sptr sft = d.second_function_type();
diff_context_sptr ctxt = d.context();
corpus_sptr fc = ctxt->get_first_corpus();
corpus_sptr sc = ctxt->get_second_corpus();
// Report about return type differences.
if (d.priv_->return_type_diff_
&& d.priv_->return_type_diff_->to_be_reported())
{
out << indent << "return type changed:\n";
d.priv_->return_type_diff_->report(out, indent + " ");
}
// Report about the parameter types that have changed sub-types.
for (vector<fn_parm_diff_sptr>::const_iterator i =
d.priv_->sorted_subtype_changed_parms_.begin();
i != d.priv_->sorted_subtype_changed_parms_.end();
++i)
{
diff_sptr dif = *i;
if (dif && dif->to_be_reported())
dif->report(out, indent);
}
report_local_function_type_changes(d, out, indent);
}
/// Report a @ref array_diff in a serialized form.
///
/// @param d the @ref array_diff to consider.
@ -1352,7 +1442,7 @@ default_reporter::report(const distinct_diff& d, ostream& out,
type_base_sptr fs = strip_typedef(is_type(f)),
ss = strip_typedef(is_type(s));
if (diff_sptr diff = d.compatible_child_diff())
if (diff)
diff->report(out, indent + " ");
else
if (report_size_and_alignment_changes(f, s, d.context(), out, indent,
@ -1386,6 +1476,7 @@ default_reporter::report(const function_decl_diff& d, ostream& out,
corpus_sptr fc = ctxt->get_first_corpus();
corpus_sptr sc = ctxt->get_second_corpus();
string qn1 = ff->get_qualified_name(), qn2 = sf->get_qualified_name(),
linkage_names1, linkage_names2;
elf_symbol_sptr s1 = ff->get_symbol(), s2 = sf->get_symbol();

View File

@ -3800,6 +3800,20 @@ uint64_t
get_data_member_offset(const decl_base_sptr d)
{return get_data_member_offset(dynamic_pointer_cast<var_decl>(d));}
/// Get the size of a given variable.
///
/// @param v the variable to consider.
///
/// @return the size of variable @p v.
uint64_t
get_var_size_in_bits(const var_decl_sptr& v)
{
type_base_sptr t = v->get_type();
assert(t);
return t->get_size_in_bits();
}
/// Set a flag saying if a data member is laid out.
///
/// @param m the data member to consider.
@ -5278,13 +5292,15 @@ get_string_representation_of_cv_quals(const qualified_type_def::CV cv_quals)
///
/// @return the name of @p tod.
string
get_name(const type_or_decl_base_sptr& tod, bool qualified)
get_name(const type_or_decl_base *tod, bool qualified)
{
string result;
if (type_base_sptr t = dynamic_pointer_cast<type_base>(tod))
type_or_decl_base* a = const_cast<type_or_decl_base*>(tod);
if (type_base* t = dynamic_cast<type_base*>(a))
result = get_type_name(t, qualified);
else if (decl_base_sptr d = dynamic_pointer_cast<decl_base>(tod))
else if (decl_base *d = dynamic_cast<decl_base*>(a))
{
if (qualified)
result = d->get_qualified_name();
@ -5298,6 +5314,19 @@ get_name(const type_or_decl_base_sptr& tod, bool qualified)
return result;
}
/// Build and return a copy of the name of an ABI artifact that is
/// either a type of a decl.
///
/// @param tod the ABI artifact to get the name for.
///
/// @param qualified if yes, return the qualified name of @p tod;
/// otherwise, return the non-qualified name;
///
/// @return the name of @p tod.
string
get_name(const type_or_decl_base_sptr& tod, bool qualified)
{return get_name(tod.get(), qualified);}
/// Build and return a qualified name from a name and its scope.
///
/// The name is supposed to be for an entity that is part of the
@ -19584,6 +19613,44 @@ size_t
hash_type_or_decl(const type_or_decl_base_sptr& tod)
{return hash_type_or_decl(tod.get());}
/// Test if the pretty representation of a given @ref function_decl is
/// lexicographically less then the pretty representation of another
/// @ref function_decl.
///
/// @param f the first @ref function_decl to consider for comparison.
///
/// @param s the second @ref function_decl to consider for comparison.
///
/// @return true iff the pretty representation of @p f is
/// lexicographically less than the pretty representation of @p s.
bool
function_decl_is_less_than(const function_decl &f, const function_decl &s)
{
string fr = f.get_pretty_representation_of_declarator(),
sr = s.get_pretty_representation_of_declarator();
if (fr != sr)
return fr < sr;
fr = f.get_pretty_representation(),
sr = s.get_pretty_representation();
if (fr != sr)
return fr < sr;
if (f.get_symbol())
fr = f.get_symbol()->get_id_string();
else if (!f.get_linkage_name().empty())
fr = f.get_linkage_name();
if (s.get_symbol())
sr = s.get_symbol()->get_id_string();
else if (!s.get_linkage_name().empty())
sr = s.get_linkage_name();
return fr < sr;
}
bool
ir_traversable_base::traverse(ir_node_visitor&)
{return true;}

1107
src/abg-leaf-reporter.cc Normal file

File diff suppressed because it is too large Load Diff

View File

@ -127,7 +127,7 @@ represent_data_member(var_decl_sptr d,
///
/// @param out the output stream to emit the string to.
void
maybe_show_relative_offset_change(var_diff_sptr diff,
maybe_show_relative_offset_change(const var_diff_sptr &diff,
diff_context& ctxt,
ostream& out)
{
@ -158,6 +158,56 @@ maybe_show_relative_offset_change(var_diff_sptr diff,
out << " (by " << sign << change << " bits)";
}
/// If a given @ref var_diff node carries a hange in which the size of
/// the variable actually changed, then emit a string (to an output
/// stream) that represents that size change.
///
/// For instance, if the size of the variable increased by 32 bits
/// then the string emitted is going to be "by +32 bits".
///
/// If, on the other hand, the size of the variable decreased by 64
/// bits then the string emitted is going to be "by -64 bits".
///
/// This function is a sub-routine used by the reporting system.
///
/// @param diff the diff node that potentially carries the variable
/// change.
///
/// @param ctxt the context in which the diff is being reported.
///
/// @param out the output stream to emit the string to.
void
maybe_show_relative_size_change(const var_diff_sptr &diff,
diff_context& ctxt,
ostream& out)
{
if (!ctxt.show_relative_offset_changes())
return;
var_decl_sptr o = diff->first_var();
var_decl_sptr n = diff->second_var();
uint64_t first_size = get_var_size_in_bits(o),
second_size = get_var_size_in_bits(n);
string sign;
uint64_t change = 0;
if (first_size < second_size)
{
sign = "+";
change = second_size - first_size;
}
else if (first_size > second_size)
{
sign = "-";
change = first_size - second_size;
}
else
return;
out << " (by " << sign << change << " bits)";
}
/// Represent the changes carried by an instance of @ref var_diff that
/// represent a difference between two class data members.
///
@ -165,16 +215,19 @@ maybe_show_relative_offset_change(var_diff_sptr diff,
///
/// @param ctxt the diff context to use.
///
/// @param local_only if true, only display local changes.
///
/// @param out the output stream to send the representation to.
///
/// @param indent the indentation string to use for the change report.
void
represent(var_diff_sptr diff,
represent(const var_diff_sptr &diff,
diff_context_sptr ctxt,
ostream& out,
const string& indent)
const string& indent,
bool local_only)
{
if (!diff->to_be_reported())
if (!ctxt->get_reporter()->diff_to_be_reported(diff.get()))
return;
var_decl_sptr o = diff->first_var();
@ -186,21 +239,22 @@ represent(var_diff_sptr diff,
string name2 = n->get_qualified_name();
string pretty_representation = o->get_pretty_representation();
if (diff_sptr d = diff->type_diff())
{
if (d->to_be_reported())
{
out << indent
<< "type of '" << pretty_representation << "' changed:\n";
if (d->currently_reporting())
out << indent << " details are being reported\n";
else if (d->reported_once())
out << indent << " details were reported earlier\n";
else
d->report(out, indent + " ");
begin_with_and = true;
}
}
if (!local_only)
if (diff_sptr d = diff->type_diff())
{
if (ctxt->get_reporter()->diff_to_be_reported(d.get()))
{
out << indent
<< "type of '" << pretty_representation << "' changed:\n";
if (d->currently_reporting())
out << indent << " details are being reported\n";
else if (d->reported_once())
out << indent << " details were reported earlier\n";
else
d->report(out, indent + " ");
begin_with_and = true;
}
}
if (name1 != name2)
{
@ -240,27 +294,50 @@ represent(var_diff_sptr diff,
out << "now becomes laid out";
emitted = true;
}
if ((ctxt->get_allowed_category() & SIZE_OR_OFFSET_CHANGE_CATEGORY)
&& (get_data_member_offset(o)
!= get_data_member_offset(n)))
if ((ctxt->get_allowed_category() & SIZE_OR_OFFSET_CHANGE_CATEGORY))
{
if (begin_with_and)
if (get_data_member_offset(o) != get_data_member_offset(n))
{
out << indent << "and ";
begin_with_and = false;
if (begin_with_and)
{
out << indent << "and ";
begin_with_and = false;
}
else if (!emitted)
out << indent << "'" << pretty_representation << "' ";
else
out << ", ";
out << "offset changed from "
<< get_data_member_offset(o)
<< " to " << get_data_member_offset(n)
<< " (in bits)";
maybe_show_relative_offset_change(diff, *ctxt, out);
emitted = true;
}
else if (!emitted)
out << indent << "'" << pretty_representation << "' ";
else
out << ", ";
out << "offset changed from "
<< get_data_member_offset(o)
<< " to " << get_data_member_offset(n)
<< " (in bits)";
if (// If we are not displaying only local changes, we must
// have indicated the type size change already.
local_only
&& (get_var_size_in_bits(o) != get_var_size_in_bits(n)))
{
if (begin_with_and)
{
out << indent << "and ";
begin_with_and = false;
}
else if (!emitted)
out << indent << "'" << pretty_representation << "' ";
else
out << ", ";
maybe_show_relative_offset_change(diff, *ctxt, out);
emitted = true;
out << "size changed from "
<< get_var_size_in_bits(o)
<< " to " << get_var_size_in_bits(n)
<< " (in bits)";
maybe_show_relative_size_change(diff, *ctxt, out);
emitted = true;
}
}
if (o->get_binding() != n->get_binding())
{
@ -566,6 +643,8 @@ report_name_size_and_alignment_changes(decl_base_sptr first,
/// @param number the number of insertion/deletion to refer to in the
/// header.
///
/// @param num_filtered the number of filtered changes.
///
/// @param k the kind of diff (insertion/deletion/change) we want the
/// head to introduce.
///
@ -616,6 +695,45 @@ report_mem_header(ostream& out,
out << colon_or_semi_colon << '\n';
}
/// Output the header preceding the the report for
/// insertion/deletion/change of a part of a class. This is a
/// subroutine of class_diff::report.
///
/// @param out the output stream to output the report to.
///
/// @param k the kind of diff (insertion/deletion/change) we want the
/// head to introduce.
///
/// @param section_name the name of the sub-part of the class to
/// report about.
///
/// @param indent the string to use as indentation prefix in the
/// header.
void
report_mem_header(ostream& out,
diff_kind k,
const string& section_name,
const string& indent)
{
string change;
switch (k)
{
case del_kind:
change = "deletions";
break;
case ins_kind:
change = "insertions";
break;
case subtype_change_kind:
case change_kind:
change = "changes";
break;
}
out << indent << "there are " << section_name << " " << change << ":\n";
}
/// Report the differences in access specifiers and static-ness for
/// class members.
///
@ -813,5 +931,89 @@ show_linkage_name_and_aliases(ostream& out,
out << ", aliases " << aliases;
}
/// If a given diff node impacts some public interfaces, then report
/// about those impacted interfaces on standard output.
///
/// @param d the diff node to get the impacted interfaces for.
///
/// @param out the output stream to report to.
///
/// @param indent the white space string to use for indentation.
///
/// @param new_line_prefix if set to true, it means there is going to
/// be a new line emitted before the report.
void
maybe_report_interfaces_impacted_by_diff(const diff *d,
ostream &out,
const string &indent,
bool new_line_prefix)
{
const diff_context_sptr &ctxt = d->context();
const corpus_diff_sptr &corp_diff = ctxt->get_corpus_diff();
if (!corp_diff)
return;
if (!ctxt->show_impacted_interfaces())
return;
const diff_maps &maps = corp_diff->get_leaf_diffs();
artifact_sptr_set_type* impacted_artifacts =
maps.lookup_impacted_interfaces(d);
if (impacted_artifacts == 0)
return;
if (impacted_artifacts->empty())
return;
vector<type_or_decl_base_sptr> sorted_impacted_interfaces;
sort_artifacts_set(*impacted_artifacts, sorted_impacted_interfaces);
if (new_line_prefix)
out << '\n';
size_t num_impacted_interfaces = impacted_artifacts->size();
if (num_impacted_interfaces == 1)
out << indent << "one impacted interface:\n";
else
out << indent << num_impacted_interfaces << " impacted interfaces:\n";
string cur_indent = indent + " ";
vector<type_or_decl_base_sptr>::const_iterator it;
for (it = sorted_impacted_interfaces.begin();
it != sorted_impacted_interfaces.end();
++it)
{
if (it != sorted_impacted_interfaces.begin())
out << '\n';
out << cur_indent << get_pretty_representation(*it);
}
}
/// If a given diff node impacts some public interfaces, then report
/// about those impacted interfaces on standard output.
///
/// @param d the diff node to get the impacted interfaces for.
///
/// @param out the output stream to report to.
///
/// @param indent the white space string to use for indentation.
///
/// @param new_line_prefix if set to true, it means there is going to
/// be a new line emitted before the report.
void
maybe_report_interfaces_impacted_by_diff(const diff_sptr &d,
ostream &out,
const string &indent)
{return maybe_report_interfaces_impacted_by_diff(d.get(), out, indent);}
/// Tests if the diff node is to be reported.
///
/// @param p the diff to consider.
///
/// @return true iff the diff is to be reported.
bool
reporter_base::diff_to_be_reported(const diff *d) const
{return d && d->to_be_reported();}
} // namespace comparison
} // end namespace abigail

View File

@ -35,7 +35,7 @@
/// @param S1 the first diff subject to take in account.
///
/// @param S2 the second diff subject to take in account.
#define RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER(S1, S2) \
#define RETURN_IF_BEING_REPORTED_OR_WAS_REPORTED_EARLIER(S1, S2) \
do { \
if (diff_context_sptr ctxt = d.context()) \
if (diff_sptr _diff_ = ctxt->get_canonical_diff_for(S1, S2)) \
@ -45,7 +45,7 @@
out << indent << "details are being reported\n"; \
else \
out << indent << "details were reported earlier\n"; \
return ; \
return; \
} \
} while (false)
@ -119,15 +119,21 @@ represent_data_member(var_decl_sptr d,
ostream& out);
void
maybe_show_relative_offset_change(var_diff_sptr diff,
maybe_show_relative_offset_change(const var_diff_sptr &diff,
diff_context& ctxt,
ostream& out);
void
represent(var_diff_sptr diff,
maybe_show_relative_size_change(const var_diff_sptr &diff,
diff_context& ctxt,
ostream& out);
void
represent(const var_diff_sptr &diff,
diff_context_sptr ctxt,
ostream& out,
const string& indent = "");
const string& indent = "",
bool local_only = false);
bool
report_size_and_alignment_changes(type_or_decl_base_sptr first,
@ -160,6 +166,12 @@ enum diff_kind
change_kind
};
void
report_mem_header(ostream& out,
diff_kind k,
const string& section_name,
const string& indent);
void
report_mem_header(ostream& out,
size_t number,
@ -187,6 +199,18 @@ show_linkage_name_and_aliases(ostream& out,
const elf_symbol& symbol,
const string_elf_symbols_map_type& sym_map);
void
maybe_report_interfaces_impacted_by_diff(const diff *d,
ostream &out,
const string &indent,
bool new_line_prefix = true);
void
maybe_report_interfaces_impacted_by_diff(const diff_sptr &d,
ostream &out,
const string &indent,
bool new_line_prefix = false);
} // end namespace comparison
} // end namespace abigail

View File

@ -248,8 +248,8 @@ sonames_of_binaries_match(const suppression_base& suppr,
const diff_context& ctxt)
{
// Check if the sonames of the binaries match
string first_soname = ctxt.get_first_corpus()->get_soname(),
second_soname = ctxt.get_second_corpus()->get_soname();
string first_soname = ctxt.get_corpus_diff()->first_corpus()->get_soname(),
second_soname = ctxt.get_corpus_diff()->second_corpus()->get_soname();
if (!suppr.priv_->matches_soname(first_soname)
&& !suppr.priv_->matches_soname(second_soname))
@ -275,8 +275,8 @@ names_of_binaries_match(const suppression_base& suppr,
const diff_context &ctxt)
{
// Check if the file names of the binaries match
string first_binary_path = ctxt.get_first_corpus()->get_path(),
second_binary_path = ctxt.get_second_corpus()->get_path();
string first_binary_path = ctxt.get_corpus_diff()->first_corpus()->get_path(),
second_binary_path = ctxt.get_corpus_diff()->second_corpus()->get_path();
if (!suppr.priv_->matches_binary_name(first_binary_path)
&& !suppr.priv_->matches_binary_name(second_binary_path))

View File

@ -635,6 +635,11 @@ test-diff-filter/test40-v1.cc \
test-diff-filter/test41-PR21486-abg-writer.gcc.o \
test-diff-filter/test41-PR21486-abg-writer.llvm.o \
test-diff-filter/test41-report-0.txt \
test-diff-filter/libtest42-leaf-report-v0.so \
test-diff-filter/libtest42-leaf-report-v1.so \
test-diff-filter/test42-leaf-report-output-0.txt \
test-diff-filter/test42-leaf-report-v0.cc \
test-diff-filter/test42-leaf-report-v1.cc \
\
test-diff-suppr/test0-type-suppr-v0.cc \
test-diff-suppr/test0-type-suppr-v1.cc \
@ -1086,6 +1091,17 @@ test-diff-suppr/test34-pub-include-dir-v1/test34-pub-include-v1.h \
test-diff-suppr/test34-report-0.txt \
test-diff-suppr/test34-v0.c \
test-diff-suppr/test34-v1.c \
test-diff-suppr/libtest35-leaf-v0.so \
test-diff-suppr/libtest35-leaf-v1.so \
test-diff-suppr/test35-leaf-report-0.txt \
test-diff-suppr/test35-leaf-v0.cc \
test-diff-suppr/test35-leaf-v1.cc \
test-diff-suppr/test35-leaf.suppr \
test-diff-suppr/libtest36-leaf-v0.so \
test-diff-suppr/libtest36-leaf-v1.so \
test-diff-suppr/test36-leaf-report-0.txt \
test-diff-suppr/test36-leaf-v0.cc \
test-diff-suppr/test36-leaf-v1.cc \
\
test-diff-dwarf-abixml/test0-pr19026-libvtkIOSQL-6.1.so.1 \
test-diff-dwarf-abixml/test0-pr19026-libvtkIOSQL-6.1.so.1.abi \

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,11 @@
Leaf changes summary: 1 artifact changed
Added/removed functions summary: 0 Removed, 0 Added function
Added/removed variables summary: 0 Removed, 0 Added variable
'struct leaf' changed:
type size changed from 32 to 64 bits
1 data member insertion:
'char leaf::m1', at offset 32 (in bits) at test42-leaf-report-v1.cc:7:1
one impacted interface:
function void fn(C&)

View File

@ -0,0 +1,16 @@
// Compile this with:
// g++ -shared -g -o libtest42-leaf-report-v0.so test42-leaf-report-v0.cc
struct leaf
{
int m0;
};
struct C
{
leaf *m0;
};
void
fn(C&)
{}

View File

@ -0,0 +1,17 @@
// Compile this with:
// g++ -shared -g -o libtest42-leaf-report-v1.so test42-leaf-report-v1.cc
struct leaf
{
int m0;
char m1;
};
struct C
{
leaf *m0;
};
void
fn(C&)
{}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,11 @@
Leaf changes summary: 1 artifact changed (1 filtered out)
Added/removed functions summary: 0 Removed, 0 Added function
Added/removed variables summary: 0 Removed, 0 Added variable
'struct leaf' changed:
type size changed from 32 to 64 bits
1 data member insertion:
'char leaf::m1', at offset 32 (in bits) at test35-leaf-v1.cc:8:1
one impacted interface:
function void fn(C&)

View File

@ -0,0 +1,23 @@
// Compile this with:
//
// g++ -shared -g -o libtest35-leaf-v0.so test35-leaf-v0.cc
struct leaf
{
int m0;
};
struct leaf_to_filter
{
int member0;
};
struct C
{
leaf *m0;
leaf_to_filter *m1;
};
void
fn(C&)
{}

View File

@ -0,0 +1,25 @@
// Compile this with:
//
// g++ -shared -g -o libtest35-leaf-v1.so test35-leaf-v1.cc
struct leaf
{
int m0;
char m1;
};
struct leaf_to_filter
{
int member0;
int added;
};
struct C
{
leaf *m0;
leaf_to_filter *m1;
};
void
fn(C&)
{}

View File

@ -0,0 +1,8 @@
[suppress_type]
# Suppress the type that had a data member inserted after the data
# member named 'member0'. That would be the type named
# leaf_to_filter.
name_regexp = *.
has_data_member_inserted_between = {offset_after(member0), end}

View File

@ -0,0 +1,25 @@
Leaf changes summary: 2 artifacts changed
Added/removed functions summary: 0 Removed, 0 Added function
Added/removed variables summary: 0 Removed, 0 Added variable
'struct leaf2' changed:
type size changed from 64 to 96 bits
there are data member changes:
'leaf1 leaf2::member0' size changed from 32 to 64 (in bits) (by +32 bits)
'char leaf2::member1' offset changed from 32 to 64 (in bits) (by +32 bits)
3 impacted interfaces:
function void interface1(struct_type*)
function void interface2(struct_type&)
function void interface3(struct_type**)
'struct leaf1' changed:
type size changed from 32 to 64 bits
1 data member insertion:
'char leaf1::m1', at offset 32 (in bits) at test36-leaf-v1.cc:7:1
3 impacted interfaces:
function void interface1(struct_type*)
function void interface2(struct_type&)
function void interface3(struct_type**)

View File

@ -0,0 +1,30 @@
// Compile this with:
// g++ -g -shared -o libtest36-leaf-v0.so test36-leaf-v0.cc
struct leaf1
{
int m0;
}; // end struct leaf1
struct leaf2
{
leaf1 member0;
char member1;
}; // end struct leaf2
struct struct_type
{
leaf2* m0;
};
void
interface1(struct_type*)
{}
void
interface2(struct_type&)
{}
void
interface3(struct_type**)
{}

View File

@ -0,0 +1,31 @@
// Compile this with:
// g++ -g -shared -o libtest36-leaf-v1.so test36-leaf-v1.cc
struct leaf1
{
int m0;
char m1;
}; // end struct leaf1
struct leaf2
{
leaf1 member0;
char member1;
}; // end struct leaf2
struct struct_type
{
leaf2* m0;
};
void
interface1(struct_type*)
{}
void
interface2(struct_type&)
{}
void
interface3(struct_type**)
{}

View File

@ -478,6 +478,13 @@ InOutSpec in_out_specs[] =
"data/test-diff-filter/test41-report-0.txt",
"output/test-diff-filter/test41-report-0.txt",
},
{
"data/test-diff-filter/libtest42-leaf-report-v0.so",
"data/test-diff-filter/libtest42-leaf-report-v1.so",
"--no-default-suppression --leaf-changes-only --impacted-interfaces",
"data/test-diff-filter/test42-leaf-report-output-0.txt",
"output/test-diff-filter/test42-leaf-report-output-0.txt",
},
// This should be the last entry
{NULL, NULL, NULL, NULL, NULL}
};

View File

@ -1,6 +1,6 @@
// -*- Mode: C++ -*-
//
// Copyright (C) 2013-2016 Red Hat, Inc.
// Copyright (C) 2013-2017 Red Hat, Inc.
//
// This file is part of the GNU Application Binary Interface Generic
// Analysis and Instrumentation Library (libabigail). This library is
@ -1678,6 +1678,26 @@ InOutSpec in_out_specs[] =
"data/test-diff-suppr/test34-report-0.txt",
"output/test-diff-suppr/test34-report-0.txt"
},
{
"data/test-diff-suppr/libtest35-leaf-v0.so",
"data/test-diff-suppr/libtest35-leaf-v1.so",
"",
"",
"data/test-diff-suppr/test35-leaf.suppr",
"--no-default-suppression --leaf-changes-only --impacted-interfaces",
"data/test-diff-suppr/test35-leaf-report-0.txt",
"output/test-diff-suppr/test35-leaf-report-0.txt"
},
{
"data/test-diff-suppr/libtest36-leaf-v0.so",
"data/test-diff-suppr/libtest36-leaf-v1.so",
"",
"",
"",
"--no-default-suppression --leaf-changes-only --impacted-interfaces",
"data/test-diff-suppr/test36-leaf-report-0.txt",
"output/test-diff-suppr/test36-leaf-report-0.txt"
},
// This should be the last entry
{NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}
};

View File

@ -85,6 +85,7 @@ struct options
bool no_default_supprs;
bool no_arch;
bool no_corpus;
bool leaf_changes_only;
bool show_relative_offset_changes;
bool show_stats_only;
bool show_symtabs;
@ -103,6 +104,7 @@ struct options
bool show_harmless_changes;
bool show_redundant_changes;
bool show_symbols_not_referenced_by_debug_info;
bool show_impacted_interfaces;
bool dump_diff_tree;
bool show_stats;
bool do_log;
@ -118,6 +120,7 @@ struct options
no_default_supprs(),
no_arch(),
no_corpus(),
leaf_changes_only(),
show_relative_offset_changes(true),
show_stats_only(),
show_symtabs(),
@ -136,6 +139,7 @@ struct options
show_harmless_changes(),
show_redundant_changes(),
show_symbols_not_referenced_by_debug_info(true),
show_impacted_interfaces(),
dump_diff_tree(),
show_stats(),
do_log()
@ -166,6 +170,8 @@ display_usage(const string& prog_name, ostream& out)
"default suppression specification\n"
<< " --no-architecture do not take architecture in account\n"
<< " --no-corpus-path do not take the path to the corpora into account\n"
<< " --leaf-changes-only|-l only show leaf changes, "
"so no change impact analysis\n"
<< " --deleted-fns display deleted public functions\n"
<< " --changed-fns display changed public functions\n"
<< " --added-fns display added public functions\n"
@ -192,6 +198,8 @@ display_usage(const string& prog_name, ostream& out)
<< " --redundant display redundant changes\n"
<< " --no-redundant do not display redundant changes "
"(this is the default)\n"
<< " --impacted-interfaces do not display interfaces impacted"
" by leaf changes\n"
<< " --dump-diff-tree emit a debug dump of the internal diff tree to "
"the error output stream\n"
<< " --stats show statistics about various internal stuff\n"
@ -321,6 +329,9 @@ parse_command_line(int argc, char* argv[], options& opts)
opts.no_arch = true;
else if (!strcmp(argv[i], "--no-corpus-path"))
opts.no_corpus = true;
else if (!strcmp(argv[i], "--leaf-changes-only")
||!strcmp(argv[i], "-l"))
opts.leaf_changes_only = true;
else if (!strcmp(argv[i], "--deleted-fns"))
{
opts.show_deleted_fns = true;
@ -484,6 +495,8 @@ parse_command_line(int argc, char* argv[], options& opts)
opts.show_redundant_changes = true;
else if (!strcmp(argv[i], "--no-redundant"))
opts.show_redundant_changes = false;
else if (!strcmp(argv[i], "--impacted-interfaces"))
opts.show_impacted_interfaces = true;
else if (!strcmp(argv[i], "--dump-diff-tree"))
opts.dump_diff_tree = true;
else if (!strcmp(argv[i], "--stats"))
@ -577,6 +590,7 @@ set_diff_context_from_opts(diff_context_sptr ctxt,
{
ctxt->default_output_stream(&cout);
ctxt->error_output_stream(&cerr);
ctxt->show_leaf_changes_only(opts.leaf_changes_only);
ctxt->show_relative_offset_changes(opts.show_relative_offset_changes);
ctxt->show_stats_only(opts.show_stats_only);
ctxt->show_deleted_fns(opts.show_all_fns || opts.show_deleted_fns);
@ -592,6 +606,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_impacted_interfaces(opts.show_impacted_interfaces);
if (!opts.show_harmless_changes)
ctxt->switch_categories_off(get_default_harmless_categories_bitmap());