Take filtering in account in diff stats & better categorizing

* include/abg-comparison.h
	(diff_category::ACCESS_CHANGE_CATEGORY): Renamed
	ACCESS_CHANGED_CATEGORY into this.
	(diff_category::SIZE_OR_OFFSET_CHANGE_CATEGORY): Renamed
	SIZE_CHANGED_CATEGORY into this.  Changed its semantics to
	incorporate offset changes as well.
	* src/abg-comparison.cc (struct noop_deleter): Move this up.
	(represent): Do not report filtered out data members.
	(report_mem_header): Add a new num_filtered parameter to take
	filtered-out members in account in members report headers.
	Adjust.
	(class_diff::priv::{count_filtered_bases,
	count_filtered_data_members, count_filtered_member_functions}):
	New member functions.  When a member is filtered, do not report
	it all.
	({enum_diff, class_diff}::report): Adjust.  Take filtered members
	into account in headers.
	(corpus_diff::priv::apply_filters_and_compute_diff_stats): New
	member function.
	(corpus_diff::priv::emit_diff_stats): Renamed
	emit_corpus_diff_stats into this.  Change it to take the stats in
	parameter.
	(corpus_diff::report): Adjust to re-use the above.  Filter
	varibles as well.  Take the filtered functions & variables in
	account in the stats.  Do not report filtered-out functions &
	variables at all.
	* src/abg-comp-filter.cc (type_size_changed, access_changed)
	(data_member_offset_changed): New predicates.
	({harmless, harmful}_filter::visit): Adjust to use the new
	predicates above.  Update the harmful variant for the new
	SIZE_OR_OFFSET_CHANGE_CATEGORY category.
	* tools/bidiff.cc (set_diff_context_from_opts): Adjust for the
	categories name changes.
	* tests/data/test-diff-filter/test0-report.txt: New test input.
	* tests/data/test-diff-filter/test0-v0.cc: Likewise.
	* tests/data/test-diff-filter/test0-v0.o: Likewise.
	* tests/data/test-diff-filter/test0-v1.cc: Likewise.
	* tests/data/test-diff-filter/test0-v1.o: Likewise.
	* tests/test-diff-filter.cc: New test harness.
	* tests/Makefile.am: Add the new test files above to the
	distribution.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
This commit is contained in:
Dodji Seketeli 2014-03-29 06:44:13 +01:00
parent e55a7e3397
commit 67e6971005
11 changed files with 656 additions and 152 deletions

View File

@ -207,10 +207,11 @@ enum diff_category
/// This means the diff node (or at least one of its descendant
/// nodes) carries access related changes, e.g, a private member
/// that becomes public.
ACCESS_CHANGED_CATEGORY = 1,
ACCESS_CHANGE_CATEGORY = 1,
/// This means the diff node (or at least one of its descendant
/// nodes) carries a change that modifies the size of a type.
SIZE_CHANGED_CATEGORY = 1 << 1,
/// nodes) carries a change that modifies the size of a type or an
/// offset of a type member.
SIZE_OR_OFFSET_CHANGE_CATEGORY = 1 << 1,
/// A special enumerator that is the logical 'or' all the
/// enumerators above.
@ -218,8 +219,8 @@ enum diff_category
/// This one must stay the last enumerator. Please update it each
/// time you add a new enumerator above.
EVERYTHING_CATEGORY =
ACCESS_CHANGED_CATEGORY
| SIZE_CHANGED_CATEGORY
ACCESS_CHANGE_CATEGORY
| SIZE_OR_OFFSET_CHANGE_CATEGORY
};
diff_category
@ -817,7 +818,7 @@ public:
enum_diff_sptr
compute_diff(const enum_type_decl_sptr,
const enum_type_decl_sptr,
diff_context_sptr ctxt);
diff_context_sptr);
class class_diff;

View File

@ -34,6 +34,8 @@ namespace comparison
namespace filtering
{
using std::tr1::dynamic_pointer_cast;
/// Walk and categorize the nodes of a diff sub-tree.
///
/// @param filter the filter invoked on each node of the walked
@ -57,6 +59,80 @@ void
apply_filter(filter_base_sptr filter, diff_sptr d)
{apply_filter(*filter, d);}
/// Tests if the size of a given type changed.
///
/// @param f the declaration of the first version of the type to
/// consider.
///
/// @param s the declaration of the second version of the type to
/// consider.
///
/// @return true if the type size changed, false otherwise.
static bool
type_size_changed(decl_base_sptr f, decl_base_sptr s)
{
type_base_sptr t0 = is_type(f), t1 = is_type(s);
if (!t0 || !t1)
return false;
if (t0->get_size_in_bits() != t1->get_size_in_bits())
return true;
return false;
}
/// Tests if the access specifiers for a member declaration changed.
///
/// @param f the declaration for the first version of the member
/// declaration to consider.
///
/// @param s the declaration for the second version of the member
/// delcaration to consider.
///
/// @return true iff the access specifier changed.
static bool
access_changed(decl_base_sptr f, decl_base_sptr s)
{
if (!is_member_decl(f)
&& !is_member_decl(s))
return false;
access_specifier fa = get_member_access_specifier(f),
sa = get_member_access_specifier(s);
if (sa != fa)
return true;
return false;
}
/// Tests if the offset of a given data member changed.
///
/// @param f the declaration for the first version of the data member to
/// consider.
///
/// @param s the declaration for the second version of the data member
/// to consider.
///
/// @return true iff the offset of the data member changed.
static bool
data_member_offset_changed(decl_base_sptr f, decl_base_sptr s)
{
if (!is_member_decl(f)
&& !is_member_decl(s))
return false;
var_decl_sptr v0 = dynamic_pointer_cast<var_decl>(f),
v1 = dynamic_pointer_cast<var_decl>(s);
if (!v0 || !v1)
return false;
if (get_data_member_offset(v0) != get_data_member_offset(v1))
return true;
return false;
}
/// The visiting code of the harmless_filter.
///
/// @param d the diff node being visited.
@ -74,15 +150,8 @@ harmless_filter::visit(diff* d, bool pre)
decl_base_sptr f = d->first_subject(),
s = d->second_subject();
if (!is_member_decl(f)
&& !is_member_decl(s))
return true;
access_specifier fa = get_member_access_specifier(f),
sa = get_member_access_specifier(s);
if (sa != fa)
d->add_to_category(ACCESS_CHANGED_CATEGORY);
if (access_changed(f, s))
d->add_to_category(ACCESS_CHANGE_CATEGORY);
// Add non-virtual member function deletions and changes due to
// details that are being reported or got reported earlier.
}
@ -90,7 +159,7 @@ harmless_filter::visit(diff* d, bool pre)
// Propagate the categorization to the parent nodes.
if (d->get_parent())
d->get_parent()->add_to_category(d->get_category()
& ACCESS_CHANGED_CATEGORY);
& ACCESS_CHANGE_CATEGORY);
return true;
}
@ -111,23 +180,16 @@ harmful_filter::visit(diff* d, bool pre)
{
decl_base_sptr f = d->first_subject(),
s = d->second_subject();
bool size_changed = false;
type_base_sptr tf, ts;
if (is_type(f) && is_type(s))
{
tf= is_type(f), ts = is_type(s);
if (tf->get_size_in_bits() != ts->get_size_in_bits())
size_changed = true;
}
if (size_changed)
d->add_to_category(SIZE_CHANGED_CATEGORY);
if (type_size_changed(f, s)
|| data_member_offset_changed(f, s))
d->add_to_category(SIZE_OR_OFFSET_CHANGE_CATEGORY);
}
// Propagate the categorization to the parent nodes.
if (d->get_parent())
d->get_parent()->add_to_category(d->get_category() & SIZE_CHANGED_CATEGORY);
d->get_parent()->add_to_category(d->get_category()
& SIZE_OR_OFFSET_CHANGE_CATEGORY);
return true;
}

View File

@ -37,6 +37,16 @@ using std::vector;
using std::tr1::dynamic_pointer_cast;
using std::tr1::static_pointer_cast;
/// A deleter for shared pointers that ... doesn't delete the object
/// managed by the shared pointer.
struct noop_deleter
{
template<typename T>
void
operator()(T*)
{}
};
/// Convenience typedef for a pair of decls.
typedef std::pair<const decl_base_sptr, const decl_base_sptr> decls_type;
@ -902,6 +912,10 @@ represent(var_decl_sptr o,
ostream& out,
const string& indent = "")
{
diff_sptr diff = compute_diff_for_decls(o, n, ctxt);
if (!diff->to_be_reported())
return;
bool emitted = false;
string name = o->get_qualified_name();
string name2 = n->get_qualified_name();
@ -1093,11 +1107,16 @@ enum diff_kind
/// header.
static void
report_mem_header(ostream& out,
int number,
size_t number,
size_t num_filtered,
diff_kind k,
const string& section_name,
const string& indent)
{
size_t net_number = number;
if (k == change_kind)
net_number = number - num_filtered;
string change;
switch (k)
{
@ -1111,13 +1130,18 @@ report_mem_header(ostream& out,
change = (number > 1) ? "changes" : "change";
}
if (number == 0)
out << indent << "no " << section_name << " " << change << "\n";
else if (number == 1)
out << indent << "1 " << section_name << " " << change << ":\n";
if (net_number == 0)
out << indent << "no " << section_name << " " << change;
else if (net_number == 1)
out << indent << "1 " << section_name << " " << change;
else
out << indent << number << " " << section_name
<< " " << change << ":\n";
out << indent << net_number << " " << section_name
<< " " << change;
if (k == change_kind
&& num_filtered)
out << ", (" << num_filtered << " filtered)";
out << ":\n";
}
// <pointer_type_def stuff>
@ -2040,7 +2064,7 @@ enum_diff::report(ostream& out, const string& indent) const
if (numdels)
{
report_mem_header(out, numdels, del_kind, "enumerator", indent);
report_mem_header(out, numdels, 0, del_kind, "enumerator", indent);
for (string_enumerator_map::const_iterator i =
deleted_enumerators().begin();
i != deleted_enumerators().end();
@ -2062,7 +2086,7 @@ enum_diff::report(ostream& out, const string& indent) const
}
if (numins)
{
report_mem_header(out, numins, ins_kind, "enumerator", indent);
report_mem_header(out, numins, 0, ins_kind, "enumerator", indent);
for (string_enumerator_map::const_iterator i =
inserted_enumerators().begin();
i != inserted_enumerators().end();
@ -2084,7 +2108,7 @@ enum_diff::report(ostream& out, const string& indent) const
}
if (numchanges)
{
report_mem_header(out, numchanges, change_kind, "enumerator", indent);
report_mem_header(out, numchanges, 0, change_kind, "enumerator", indent);
for (string_changed_enumerator_map::const_iterator i =
changed_enumerators().begin();
i != changed_enumerators().end();
@ -2203,6 +2227,16 @@ struct class_diff::priv
decl_base_sptr
member_class_tmpl_has_changed(decl_base_sptr) const;
size_t
count_filtered_bases(const diff_context_sptr&);
size_t
count_filtered_data_members(const diff_context_sptr&);
size_t
count_filtered_member_functions(const diff_context_sptr&);
};//end struct class_diff::priv
/// Clear the lookup tables useful for reporting.
@ -2576,6 +2610,87 @@ class_diff::priv::member_class_tmpl_has_changed(decl_base_sptr d) const
: it->second.second);
}
/// Count the number of bases classes whose changes got filtered out.
///
/// @param ctxt the context to use to determine the filtering settings
/// from the user.
///
/// @return the number of bases classes whose changes got filtered
/// out.
size_t
class_diff::priv::count_filtered_bases(const diff_context_sptr& ctxt)
{
size_t num_filtered = 0;
for (string_changed_base_map::const_iterator i = changed_bases_.begin();
i != changed_bases_.end();
++i)
{
class_decl::base_spec_sptr o =
dynamic_pointer_cast<class_decl::base_spec>(i->second.first);
class_decl::base_spec_sptr n =
dynamic_pointer_cast<class_decl::base_spec>(i->second.second);
diff_sptr diff = compute_diff(o, n, ctxt);
ctxt->maybe_apply_filters(diff);
if (diff->is_filtered_out())
++num_filtered;
}
return num_filtered;
}
/// Count the number of data members whose changes got filtered out.
///
/// @param ctxt the diff context to use to get the filtering settings
/// from the user.
///
/// @return the number of data members whose changes got filtered out.
size_t
class_diff::priv::count_filtered_data_members(const diff_context_sptr& ctxt)
{
size_t num_filtered= 0;
for (string_changed_type_or_decl_map::const_iterator i =
changed_data_members_.begin();
i != changed_data_members_.end();
++i)
{
var_decl_sptr o =
dynamic_pointer_cast<var_decl>(i->second.first);
var_decl_sptr n =
dynamic_pointer_cast<var_decl>(i->second.second);
diff_sptr diff = compute_diff_for_decls(o, n, ctxt);
ctxt->maybe_apply_filters(diff);
if (diff->is_filtered_out())
++num_filtered;
}
return num_filtered;
}
/// Count the number of member functions whose changes got filtered
/// out.
///
/// @param ctxt the diff context to use to get the filtering settings
/// from the user.
///
/// @return the number of member functions whose changes got filtered
/// out.
size_t
class_diff::priv::count_filtered_member_functions(const diff_context_sptr& ctxt)
{
size_t num_filtered = 0;
for (string_changed_member_function_sptr_map::const_iterator i =
changed_member_functions_.begin();
i != changed_member_functions_.end();
++i)
{
class_decl::method_decl_sptr f = i->second.first;
class_decl::method_decl_sptr s = i->second.second;
diff_sptr diff = compute_diff_for_decls(f, s, ctxt);
ctxt->maybe_apply_filters(diff);
if (diff->is_filtered_out())
++num_filtered;
}
return num_filtered;
}
/// Constructor of class_diff
///
/// @param first_scope the first class of the diff.
@ -2721,11 +2836,11 @@ class_diff::report(ostream& out, const string& indent) const
{
// Report deletions.
int numdels = priv_->deleted_bases_.size();
int numchanges = priv_->changed_bases_.size();
size_t numchanges = priv_->changed_bases_.size();
if (numdels)
{
report_mem_header(out, numdels, del_kind,
report_mem_header(out, numdels, 0, del_kind,
"base class", indent);
for (string_base_sptr_map::const_iterator i
@ -2745,10 +2860,12 @@ class_diff::report(ostream& out, const string& indent) const
}
out << "\n\n";
}
// Report changes.
size_t num_filtered = priv_->count_filtered_bases(context());
if (numchanges)
{
report_mem_header(out, numchanges, change_kind,
report_mem_header(out, numchanges, num_filtered, change_kind,
"base class", indent);
for (string_changed_base_map::const_iterator it =
priv_->changed_bases_.begin();
@ -2772,7 +2889,7 @@ class_diff::report(ostream& out, const string& indent) const
int numins = priv_->inserted_bases_.size();
if (numins)
{
report_mem_header(out, numins, ins_kind,
report_mem_header(out, numins, 0, ins_kind,
"base class", indent);
bool emitted = false;
@ -2800,7 +2917,7 @@ class_diff::report(ostream& out, const string& indent) const
// report deletions
if (numdels)
{
report_mem_header(out, numdels, del_kind,
report_mem_header(out, numdels, 0, del_kind,
"member type", indent);
for (string_decl_base_sptr_map::const_iterator i =
@ -2820,7 +2937,7 @@ class_diff::report(ostream& out, const string& indent) const
// report changes
if (numchanges)
{
report_mem_header(out, numchanges, change_kind,
report_mem_header(out, numchanges, 0, change_kind,
"member type", indent);
for (string_changed_type_or_decl_map::const_iterator it =
@ -2846,7 +2963,7 @@ class_diff::report(ostream& out, const string& indent) const
if (numins)
{
report_mem_header(out, numins, ins_kind,
report_mem_header(out, numins, 0, ins_kind,
"member type", indent);
bool emitted = false;
@ -2883,11 +3000,9 @@ class_diff::report(ostream& out, const string& indent) const
{
// report deletions
int numdels = priv_->deleted_data_members_.size();
int numchanges = priv_->changed_data_members_.size();
if (numdels)
{
report_mem_header(out, numdels, del_kind,
report_mem_header(out, numdels, 0, del_kind,
"data member", indent);
bool emitted = false;
for (string_decl_base_sptr_map::const_iterator i =
@ -2907,10 +3022,12 @@ class_diff::report(ostream& out, const string& indent) const
}
// report change
size_t numchanges = priv_->changed_data_members_.size();
size_t num_filtered = priv_->count_filtered_data_members(context());
if (numchanges)
{
report_mem_header(out, numchanges, change_kind,
"data member", indent);
report_mem_header(out, numchanges, num_filtered,
change_kind, "data member", indent);
for (string_changed_type_or_decl_map::const_iterator it =
priv_->changed_data_members_.begin();
@ -2930,7 +3047,7 @@ class_diff::report(ostream& out, const string& indent) const
int numins = priv_->inserted_data_members_.size();
if (numins)
{
report_mem_header(out, numins, ins_kind,
report_mem_header(out, numins, 0, ins_kind,
"data member", indent);
bool emitted = false;
for (string_decl_base_sptr_map::const_iterator i =
@ -2958,7 +3075,7 @@ class_diff::report(ostream& out, const string& indent) const
// report deletions
int numdels = priv_->deleted_member_functions_.size();
if (numdels)
report_mem_header(out, numdels, del_kind,
report_mem_header(out, numdels, 0, del_kind,
"member function", indent);
for (string_member_function_sptr_map::const_iterator i =
priv_->deleted_member_functions_.begin();
@ -2977,7 +3094,7 @@ class_diff::report(ostream& out, const string& indent) const
// report insertions;
int numins = priv_->inserted_member_functions_.size();
if (numins)
report_mem_header(out, numins, ins_kind,
report_mem_header(out, numins, 0, ins_kind,
"member function", indent);
bool emitted = false;
for (string_member_function_sptr_map::const_iterator i =
@ -2997,8 +3114,9 @@ class_diff::report(ostream& out, const string& indent) const
// report function sub-types changes
int numchanges = priv_->changed_member_functions_.size();
size_t num_filtered = priv_->count_filtered_member_functions(context());
if (numchanges)
report_mem_header(out, numchanges, change_kind,
report_mem_header(out, numchanges, num_filtered, change_kind,
"member function", indent);
for (string_changed_member_function_sptr_map::const_iterator i =
priv_->changed_member_functions_.begin();
@ -3008,21 +3126,13 @@ class_diff::report(ostream& out, const string& indent) const
class_decl::method_decl_sptr f = i->second.first;
class_decl::method_decl_sptr s = i->second.second;
diff_sptr diff = compute_diff_for_decls(f, s, context());
if (!diff)
if (!diff || !diff->to_be_reported())
continue;
string repr = f->get_pretty_representation();
if (i !=priv_->changed_member_functions_.begin())
out << "\n";
if (!diff->to_be_reported())
{
out << indent << " " << repr
<< " has some filtered out sub-type changes\n";
continue;
}
out << indent << " '"
<< repr << "' has some sub-type changes:\n";
out << indent << " '" << repr << "' has some sub-type changes:\n";
diff->report(out, indent + " ");
emitted = true;
}
@ -3036,7 +3146,7 @@ class_diff::report(ostream& out, const string& indent) const
// report deletions
int numdels = e.num_deletions();
if (numdels)
report_mem_header(out, numdels, del_kind,
report_mem_header(out, numdels, 0, del_kind,
"member function template", indent);
for (vector<deletion>::const_iterator i = e.deletions().begin();
i != e.deletions().end();
@ -3056,7 +3166,7 @@ class_diff::report(ostream& out, const string& indent) const
// report insertions
int numins = e.num_insertions();
if (numins)
report_mem_header(out, numins, ins_kind,
report_mem_header(out, numins, 0, ins_kind,
"member function template", indent);
bool emitted = false;
for (vector<insertion>::const_iterator i = e.insertions().begin();
@ -3089,7 +3199,7 @@ class_diff::report(ostream& out, const string& indent) const
// report deletions
int numdels = e.num_deletions();
if (numdels)
report_mem_header(out, numdels, del_kind,
report_mem_header(out, numdels, 0, del_kind,
"member class template", indent);
for (vector<deletion>::const_iterator i = e.deletions().begin();
i != e.deletions().end();
@ -3109,7 +3219,7 @@ class_diff::report(ostream& out, const string& indent) const
// report insertions
int numins = e.num_insertions();
if (numins)
report_mem_header(out, numins, ins_kind,
report_mem_header(out, numins, 0, ins_kind,
"member class template", indent);
bool emitted = false;
for (vector<insertion>::const_iterator i = e.insertions().begin();
@ -4890,7 +5000,7 @@ compute_diff(const translation_unit_sptr first,
// <corpus stuff>
struct corpus_diff::priv
{
diff_context_sptr ctxt_;
diff_context_sptr ctxt_;
corpus_sptr first_;
corpus_sptr second_;
edit_script fns_edit_script_;
@ -4911,8 +5021,36 @@ struct corpus_diff::priv
void
ensure_lookup_tables_populated();
struct diff_stats
{
size_t num_func_removed;
size_t num_func_added;
size_t num_func_changed;
size_t num_func_filtered_out;
size_t num_vars_removed;
size_t num_vars_added;
size_t num_vars_changed;
size_t num_vars_filtered_out;
diff_stats()
: num_func_removed(0),
num_func_added(0),
num_func_changed(0),
num_func_filtered_out(0),
num_vars_removed(0),
num_vars_added(0),
num_vars_changed(0),
num_vars_filtered_out(0)
{}
};
void
emit_corpus_diff_stats(ostream& out, const string& indent);
apply_filters_and_compute_diff_stats(diff_stats&);
void
emit_diff_stats(diff_stats& stats,
ostream& out,
const string& indent);
}; // end corpus::priv
/// Tests if the lookup tables are empty.
@ -5049,6 +5187,60 @@ corpus_diff::priv::ensure_lookup_tables_populated()
}
}
/// Compute the diff stats.
///
/// To know the number of functions that got filtered out, this
/// function applies the categorizing filters to the diff sub-trees of
/// each function changes diff, prior to calculating the stats.
///
/// @param num_removed the number of removed functions.
///
/// @param num_added the number of added functions.
///
/// @param num_changed the number of changed functions.
///
/// @param num_filtered_out the number of changed functions that are
/// 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_func_added = added_fns_.size();
stat.num_func_changed = changed_fns_.size();
stat.num_vars_removed = deleted_vars_.size();
stat.num_vars_added = added_vars_.size();
stat.num_vars_changed = changed_vars_.size();
// Calculate number of filtered functions & variables.
diff_sptr diff;
for (string_changed_function_ptr_map::const_iterator i =
changed_fns_.begin();
i != changed_fns_.end();
++i)
{
function_decl_sptr f(i->second.first, noop_deleter());
function_decl_sptr s(i->second.second, noop_deleter());
diff = compute_diff_for_decls(f, s, ctxt_);
ctxt_->maybe_apply_filters(diff);
if (diff->is_filtered_out())
++stat.num_func_filtered_out;
}
for (string_changed_var_ptr_map::const_iterator i = changed_vars_.begin();
i != changed_vars_.end();
++i)
{
var_decl_sptr f(i->second.first, noop_deleter());
var_decl_sptr s(i->second.second, noop_deleter());
diff = compute_diff_for_decls(f, s, ctxt_);
ctxt_->maybe_apply_filters(diff);
if (diff->is_filtered_out())
++stat.num_vars_filtered_out;
}
}
/// Emit the summary of the functions & variables that got
/// removed/changed/added.
///
@ -5056,37 +5248,38 @@ corpus_diff::priv::ensure_lookup_tables_populated()
///
/// @param indent the indentation string to use in the summary.
void
corpus_diff::priv::emit_corpus_diff_stats(ostream& out, const string& indent)
corpus_diff::priv::emit_diff_stats(diff_stats& s,
ostream& out,
const string& indent)
{
unsigned total = 0, num_removed = 0, num_added = 0, num_changed = 0;
/// Report added/removed/changed functions.
num_removed = deleted_fns_.size();
num_added = added_fns_.size();
num_changed = changed_fns_.size();
total = num_removed + num_added + num_changed;
size_t total = s.num_func_removed + s.num_func_added +
s.num_func_changed - s.num_func_filtered_out;
// function changes summary
out << indent << "Functions changes summary: ";
out << num_removed << " Removed, ";
out << num_changed << " Changed, ";
out << num_added << " Added ";
out << s.num_func_removed << " Removed, ";
out << s.num_func_changed - s.num_func_filtered_out << " Changed";
if (s.num_func_filtered_out)
out << " (" << s.num_func_filtered_out << " filtered out)";
out << ", ";
out << s.num_func_added << " Added ";
if (total <= 1)
out << "function\n";
else
out << "functions\n";
// Report added/removed/changed variables.
num_removed = deleted_vars_.size();
num_added = added_vars_.size();
num_changed = changed_vars_.size();
total = num_removed + num_added + num_changed;
total = s.num_vars_removed + s.num_vars_added +
s.num_vars_changed - s.num_vars_filtered_out;
// variables changes summary
out << indent << "Variables changes summary: ";
out << num_removed << " Removed, ";
out << num_changed << " Changed, ";
out << num_added << " Added ";
out << s.num_vars_removed << " Removed, ";
out << s.num_vars_changed - s.num_vars_filtered_out<< " Changed";
if (s.num_vars_filtered_out)
out << " (" << s.num_vars_filtered_out << " filtered out)";
out << ", ";
out << s.num_vars_added << " Added ";
if (total <= 1)
out << "variable\n";
else
@ -5171,16 +5364,6 @@ corpus_diff::length() const
- priv_->changed_fns_.size());
}
/// A deleter for shared pointers that ... doesn't delete the object
/// managed by the shared pointer.
struct noop_deleter
{
template<typename T>
void
operator()(T*)
{}
};
/// Report the diff in a serialized form.
///
/// @param out the stream to serialize the diff to.
@ -5190,28 +5373,26 @@ struct noop_deleter
void
corpus_diff::report(ostream& out, const string& indent) const
{
unsigned total = 0, num_removed = 0, num_added = 0, num_changed = 0;
unsigned removed = 0, added = 0;
size_t total = 0, removed = 0, added = 0;
priv::diff_stats s;
/// Report added/removed/changed functions.
num_removed = priv_->deleted_fns_.size();
num_added = priv_->added_fns_.size();
num_changed = priv_->changed_fns_.size();
total = num_removed + num_added + num_changed;
priv_->apply_filters_and_compute_diff_stats(s);
total = s.num_func_removed + s.num_func_added +
s.num_func_changed - s.num_func_filtered_out;
const unsigned large_num = 100;
priv_->emit_corpus_diff_stats(out, indent);
priv_->emit_diff_stats(s, out, indent);
if (context()->show_stats_only())
return;
out << "\n";
if (context()->show_deleted_fns())
{
if (num_removed == 1)
if (s.num_func_removed == 1)
out << indent << "1 Removed function:\n\n";
else if (num_removed > 1)
out << indent << num_removed << " Removed functions:\n\n";
else if (s.num_func_removed > 1)
out << indent << s.num_func_removed << " Removed functions:\n\n";
for (string_function_ptr_map::const_iterator i =
priv_->deleted_fns_.begin();
@ -5232,6 +5413,7 @@ corpus_diff::report(ostream& out, const string& indent) const
if (context()->show_changed_fns())
{
size_t num_changed = s.num_func_changed - s.num_func_filtered_out;
if (num_changed == 1)
out << indent << "1 function with some indirect sub-type change:\n\n";
else if (num_changed > 1)
@ -5250,7 +5432,6 @@ corpus_diff::report(ostream& out, const string& indent) const
if (!diff)
continue;
context()->maybe_apply_filters(diff);
if (diff->to_be_reported())
{
out << indent << " [C]'"
@ -5258,23 +5439,16 @@ corpus_diff::report(ostream& out, const string& indent) const
<< "' has some indirect sub-type changes:\n";
diff->report(out, indent + " ");
}
else if (diff->is_filtered_out())
{
out << indent << " [C]{F}'"
<< i->second.first->get_pretty_representation()
<< "' has some filtered out sub-type changes\n";
}
}
if (priv_->changed_fns_.size())
out << "\n";
out << "\n";
}
if (context()->show_added_fns())
{
if (num_added == 1)
if (s.num_func_added == 1)
out << indent << "1 Added function:\n";
else if (num_added > 1)
out << indent << num_added
else if (s.num_func_added > 1)
out << indent << s.num_func_added
<< " Added functions:\n\n";
for (string_function_ptr_map::const_iterator i =
priv_->added_fns_.begin();
@ -5295,17 +5469,15 @@ corpus_diff::report(ostream& out, const string& indent) const
}
// Report added/removed/changed variables.
num_removed = priv_->deleted_vars_.size();
num_added = priv_->added_vars_.size();
num_changed = priv_->changed_vars_.size();
total = num_removed + num_added + num_changed;
total = s.num_vars_removed + s.num_vars_added +
s.num_vars_changed - s.num_vars_filtered_out;
if (context()->show_deleted_vars())
{
if (num_removed == 1)
if (s.num_vars_removed == 1)
out << indent << "1 Deleted variable:\n";
else if (num_removed > 1)
out << indent << num_removed
else if (s.num_vars_removed > 1)
out << indent << s.num_vars_removed
<< " Deleted variables:\n\n";
string n;
for (string_var_ptr_map::const_iterator i =
@ -5331,6 +5503,7 @@ corpus_diff::report(ostream& out, const string& indent) const
if (context()->show_changed_vars())
{
size_t num_changed = s.num_vars_changed - s.num_vars_filtered_out;
if (num_changed == 1)
out << indent << "1 Changed variable:\n";
else if (num_changed > 1)
@ -5342,42 +5515,39 @@ corpus_diff::report(ostream& out, const string& indent) const
i != priv_->changed_vars_.end();
++i)
{
if (!i->second.first->get_mangled_name().empty())
n1 =
demangle_cplus_mangled_name(i->second.first->get_mangled_name());
else
n1 = i->second.first->get_pretty_representation();
var_decl_sptr f(i->second.first, noop_deleter());
var_decl_sptr s(i->second.second, noop_deleter());
diff_sptr diff = compute_diff_for_decls(f, s, context());
if (!diff || !diff->to_be_reported())
continue;
if (!i->second.second->get_mangled_name().empty())
n2 =
demangle_cplus_mangled_name(i->second.second->get_mangled_name());
if (!f->get_mangled_name().empty())
n1 = demangle_cplus_mangled_name(f->get_mangled_name());
else
n2 = i->second.second->get_pretty_representation();
n1 = f->get_pretty_representation();
if (!s->get_mangled_name().empty())
n2 = demangle_cplus_mangled_name(s->get_mangled_name());
else
n2 = s->get_pretty_representation();
out << indent << " '"
<< n1
<< "' was changed to '"
<< n2
<< "':\n";
{
var_decl_sptr f(i->second.first, noop_deleter());
var_decl_sptr s(i->second.second, noop_deleter());
diff_sptr diff = compute_diff_for_decls(f, s, context());
if (diff)
diff->report(out, indent + " ");
}
diff->report(out, indent + " ");
}
if (priv_->changed_vars_.size())
if (num_changed)
out << "\n";
}
if (context()->show_added_vars())
{
if (num_added == 1)
if (s.num_vars_added == 1)
out << indent << "1 Added variable:\n";
else if (num_added > 1)
out << indent << num_added
else if (s.num_vars_added > 1)
out << indent << s.num_vars_added
<< " Added variables:\n";
string n;
for (string_var_ptr_map::const_iterator i =

View File

@ -7,6 +7,7 @@ runtestreaddwarf \
runtestcorediff \
runtestbidiff \
runtestdiffdwarf \
runtestdifffilter \
runtestsvg \
runtestdot
@ -43,6 +44,9 @@ runtestbidiff_LDADD = libtestutils.la $(top_builddir)/src/libabigail.la
runtestdiffdwarf_SOURCES = $(h)/test-diff-dwarf.cc
runtestdiffdwarf_LDADD = libtestutils.la $(top_builddir)/src/libabigail.la
runtestdifffilter_SOURCES = $(h)/test-diff-filter.cc
runtestdifffilter_LDADD = libtestutils.la $(top_builddir)/src/libabigail.la
runtestsvg_SOURCES=$(h)/test-svg.cc
runtestsvg_LDADD=$(top_builddir)/src/libabigail.la
@ -124,7 +128,14 @@ data/test-read-dwarf/test0.abi \
data/test-read-dwarf/test0.cc \
data/test-read-dwarf/test1 \
data/test-read-dwarf/test1.abi \
data/test-read-dwarf/test1.cc
data/test-read-dwarf/test1.cc \
\
data/test-diff-filter/test0-v0.cc \
data/test-diff-filter/test0-v1.cc \
data/test-diff-filter/test0-v0.o \
data/test-diff-filter/test0-v1.o \
data/test-diff-filter/test0-report.txt
clean-local: clean-local-check
.PHONY: clean-local-check

View File

@ -0,0 +1,32 @@
Functions changes summary: 0 Removed, 1 Changed (2 filtered out), 1 Added functions
Variables changes summary: 0 Removed, 0 Changed, 0 Added variable
1 function with some indirect sub-type change:
[C]'function void foo(const S0&, S1*)' has some indirect sub-type changes:
parameter 0 of type 'const S0&' changed:
in unqualified underlying type 'S0&':
in referenced type 'class S0':
size changed from 96 to 128 bits
1 base class change:
'class B0S0' changed:
size changed from 64 to 96 bits
1 data member change:
'char B0S0::m1' offset changed from 32 to 64
1 data member insertion:
'unsigned int B0S0::m2', at offset 32 (in bits)
1 data member change:
'int S0::m0' offset changed from 64 to 96, access changed from 'private' to 'protected'
1 member function insertion:
'method int S0::get_member0()'
no member function change, (1 filtered):
1 Added function:
'method int S0::get_member0()

View File

@ -0,0 +1,50 @@
class B0S0
{
int m0;
char m1;
public:
void
member0() const
{}
static void
static_member0();
};
void
B0S0::static_member0()
{
}
class B0S1
{
int m0;
public:
B0S1()
{}
};
class S0 : public B0S0
{
int m0;
void
member0() const
{}
};
class S1 : public B0S1
{
int m0;
void member0()
{}
};
void
foo(S0&, S1*)
{}
void
bar(S1&)
{}

Binary file not shown.

View File

@ -0,0 +1,60 @@
class B0S0
{
int m0;
unsigned m2;
char m1;
public:
void
member0() const
{}
static void
static_member0();
};
void
B0S0::static_member0()
{
}
class B0S1
{
int m0;
public:
B0S1()
{}
};
class S0 : public B0S0
{
protected:
int m0;
public:
void
member0() const
{}
int
get_member0() const
{return m0;}
};
class S1 : public B0S1
{
int m0;
public:
void member0()
{}
};
void
foo(S0&, S1*)
{}
void
bar(S1&)
{}

Binary file not shown.

118
tests/test-diff-filter.cc Normal file
View File

@ -0,0 +1,118 @@
// -*- Mode: C++ -*-
//
// Copyright (C) 2013 Red Hat, Inc.
//
// This file is part of the GNU Application Binary Interface Generic
// Analysis and Instrumentation Library (libabigail). This library is
// free software; you can redistribute it and/or modify it under the
// terms of the GNU Lesser General Public License as published by the
// Free Software Foundation; either version 3, or (at your option) any
// later version.
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Lesser Public License for more details.
// You should have received a copy of the GNU Lesser General Public
// License along with this program; see the file COPYING-LGPLV3. If
// not, see <http://www.gnu.org/licenses/>.
// Author: Dodji Seketeli
/// @file
///
/// This program runs a diff between input ELF files containing DWARF
/// debugging information and compares the resulting report with a
/// reference report. If the resulting report is different from the
/// reference report, the test has failed. Note that the comparison
/// is done using the bidiff command line comparison tool.
///
/// The set of input files and reference reports to consider should be
/// present in the source distribution.
#include <string>
#include <fstream>
#include <iostream>
#include <cstdlib>
#include "abg-tools-utils.h"
#include "test-utils.h"
using std::string;
using std::cerr;
/// This is an aggregate that specifies where a test shall get its
/// input from and where it shall write its ouput to.
struct InOutSpec
{
const char* in_elfv0_path;
const char* in_elfv1_path;
const char* bidiff_options;
const char* in_report_path;
const char* out_report_path;
}; // end struct InOutSpec;
InOutSpec in_out_specs[] =
{
{
"data/test-diff-filter/test0-v0.o",
"data/test-diff-filter/test0-v1.o",
"--no-harmless",
"data/test-diff-filter/test0-report.txt",
"output/test-diff-filter/test0-report.txt",
},
// This should be the last entry
{NULL, NULL, NULL, NULL, NULL}
};
int
main()
{
using abigail::tests::get_src_dir;
using abigail::tests::get_build_dir;
using abigail::tools::ensure_parent_dir_created;
bool is_ok = true;
string in_elfv0_path, in_elfv1_path,
bidiff_options, bidiff, cmd,
ref_diff_report_path, out_diff_report_path;
for (InOutSpec* s = in_out_specs; s->in_elfv0_path; ++s)
{
in_elfv0_path = get_src_dir() + "/tests/" + s->in_elfv0_path;
in_elfv1_path = get_src_dir() + "/tests/" + s->in_elfv1_path;
bidiff_options = s->bidiff_options;
ref_diff_report_path = get_src_dir() + "/tests/" + s->in_report_path;
out_diff_report_path = get_build_dir() + "/tests/" + s->out_report_path;
if (!ensure_parent_dir_created(out_diff_report_path))
{
cerr << "could not create parent directory for "
<< out_diff_report_path;
is_ok = false;
continue;
}
bidiff = get_build_dir() + "/tools/bidiff";
bidiff += " " + bidiff_options;
cmd = bidiff + " " + in_elfv0_path + " " + in_elfv1_path;
cmd += " > " + out_diff_report_path;
bool bidiff_ok = true;
if (system(cmd.c_str()))
bidiff_ok = false;
if (bidiff_ok)
{
cmd = "diff -u " + ref_diff_report_path
+ " " + out_diff_report_path;
if (system(cmd.c_str()))
is_ok = false;
}
else
is_ok = false;
}
return !is_ok;
}

View File

@ -293,9 +293,9 @@ set_diff_context_from_opts(diff_context_sptr ctxt,
ctxt->show_added_vars(opts.show_all_vars || opts.show_added_vars);
if (!opts.show_harmless_changes)
ctxt->switch_categories_off(abigail::comparison::ACCESS_CHANGED_CATEGORY);
ctxt->switch_categories_off(abigail::comparison::ACCESS_CHANGE_CATEGORY);
if (!opts.show_harmful_changes)
ctxt->switch_categories_off(abigail::comparison::SIZE_CHANGED_CATEGORY);
ctxt->switch_categories_off(abigail::comparison::SIZE_OR_OFFSET_CHANGE_CATEGORY);
}
/// Set the regex patterns describing the functions to drop from the