mirror of
git://sourceware.org/git/libabigail.git
synced 2024-12-15 22:44:41 +00:00
Initial support for diffing ABI corpus files
* include/abg-comparison.h (string_function_ptr_map) (changed_function_ptr, string_changed_function_ptr_map) (corpus_diff_sptr): New convenience typedefs. (translation_unit_diff): Add comments. (class corpus_diff): New type. (compute_diff): New overload for corpus_diff. * include/abg-corpus.h (corpus::{functions, variables}): New typedefs. (corpus::{operator==, get_functions, get_variables}): New members. * include/abg-diff-utils.h (struct deep_ptr_eq_functor): New functor. * include/abg-ir.h (translation_unit::operator==): New member equality operator. * src/abg-comparison.cc (struct corpus_diff::priv): New private struct holding the private members of corpus_diff. (corpus_diff::priv::{lookup_tables_empty, clear_lookup_tables, ensure_lookup_tables_populated}): Define new private member functions. (corpus_diff::{corpus_diff, first_corpus, second_corpus, function_changes, variable_changes, length, report}): New public members. (struct noop_deleter): New struct. (compute_diff): New implementation for corpus_diff. * src/abg-corpus.cc (struct corpus::priv): Renamed corpus::impl into this. Add new fns, vars and is_symbol_table_built data members. (corpus::priv::build_symbol_table): New member function. (class symtab_build_visitor_type): New visitor type to build the symbol table. (struct func_comp, struct var_comp): New comparison functors. (corpus::priv::build_symbol_table): Define new member function. (corpus::{corpus, add, get_translation_units, operator==, get_functions, get_variables}): Define new members. * src/abg-ir.cc (translation_unit::operator==): Define new member equality operator. (operator==(translation_unit_sptr l, translation_unit_sptr r)): Define new equality operator. * tools/abg-tools-utils.h (enum file_type): New enum. (guess_file_type): Declare new function. * tools/abg-tools-utils.cc (guess_file_type): define new function. * tools/bidiff.cc (main): Guess the type of the files given in input and support elf files reading and diffing. Signed-off-by: Dodji Seketeli <dodji@redhat.com>
This commit is contained in:
parent
dbc0225415
commit
fbb6b1bc73
@ -23,7 +23,7 @@
|
||||
/// @file
|
||||
|
||||
#include <tr1/unordered_map>
|
||||
#include "abg-ir.h"
|
||||
#include "abg-corpus.h"
|
||||
#include "abg-diff-utils.h"
|
||||
|
||||
namespace abigail
|
||||
@ -91,6 +91,21 @@ typedef std::pair<enum_type_decl::enumerator,
|
||||
/// The key is the name of the changed enumerator.
|
||||
typedef unordered_map<string, changed_enumerator> string_changed_enumerator_map;
|
||||
|
||||
/// Convenience typedef for a map which key is a string and which
|
||||
/// value is a pointer to @ref decl_base.
|
||||
typedef unordered_map<string, function_decl*> string_function_ptr_map;
|
||||
|
||||
/// Convenience typedef for a pair of pointer to @ref function_decl
|
||||
/// representing a change a @ref function_decl change. The first
|
||||
/// member of the pair represent the initial function and the second
|
||||
/// member represents the the changed function.
|
||||
typedef std::pair<function_decl*, function_decl*> changed_function_ptr;
|
||||
|
||||
/// Convenience typedef for a map which key is a string and which
|
||||
/// value is a @ref changed_function_ptr.
|
||||
typedef unordered_map<string,
|
||||
changed_function_ptr> string_changed_function_ptr_map;
|
||||
|
||||
/// This type encapsulates an edit script (a set of insertions and
|
||||
/// deletions) for two constructs that are to be diff'ed. The two
|
||||
/// constructs are called the "subjects" of the diff.
|
||||
@ -690,6 +705,7 @@ class translation_unit_diff;
|
||||
/// @ref translation_unit_diff type.
|
||||
typedef shared_ptr<translation_unit_diff> translation_unit_diff_sptr;
|
||||
|
||||
/// An abstraction of a diff between two translation units.
|
||||
class translation_unit_diff : public scope_diff
|
||||
{
|
||||
protected:
|
||||
@ -706,12 +722,54 @@ public:
|
||||
|
||||
virtual void
|
||||
report(ostream& out, const string& indent = "") const;
|
||||
};//end clss translation_unit_diff
|
||||
};//end class translation_unit_diff
|
||||
|
||||
translation_unit_diff_sptr
|
||||
compute_diff(const translation_unit_sptr first,
|
||||
const translation_unit_sptr second);
|
||||
|
||||
class corpus_diff;
|
||||
|
||||
/// A convenience typedef for a shared pointer to @ref corpus_diff.
|
||||
typedef shared_ptr<corpus_diff> corpus_diff_sptr;
|
||||
|
||||
/// An abstraction of a diff between between two abi corpus.
|
||||
class corpus_diff
|
||||
{
|
||||
struct priv;
|
||||
typedef shared_ptr<priv> priv_sptr;
|
||||
priv_sptr priv_;
|
||||
|
||||
protected:
|
||||
corpus_diff(corpus_sptr first,
|
||||
corpus_sptr second);
|
||||
|
||||
public:
|
||||
|
||||
corpus_sptr
|
||||
first_corpus() const;
|
||||
|
||||
corpus_sptr
|
||||
second_corpus() const;
|
||||
|
||||
edit_script&
|
||||
function_changes() const;
|
||||
|
||||
edit_script&
|
||||
variable_changes() const;
|
||||
|
||||
unsigned
|
||||
length() const;
|
||||
|
||||
void
|
||||
report(ostream& out, const string& indent = "") const;
|
||||
|
||||
friend corpus_diff_sptr
|
||||
compute_diff(const corpus_sptr, const corpus_sptr);
|
||||
}; // end class corpus_diff
|
||||
|
||||
corpus_diff_sptr
|
||||
compute_diff(const corpus_sptr, const corpus_sptr);
|
||||
}// end namespace comparison
|
||||
|
||||
}// end namespace abigail
|
||||
|
@ -40,11 +40,14 @@ typedef shared_ptr<corpus> corpus_sptr;
|
||||
class corpus
|
||||
{
|
||||
public:
|
||||
struct impl;
|
||||
typedef std::string string;
|
||||
struct priv;
|
||||
typedef shared_ptr<priv> priv_sptr;
|
||||
typedef std::string string;
|
||||
typedef vector<function_decl*> functions;
|
||||
typedef vector<var_decl*> variables;
|
||||
|
||||
private:
|
||||
shared_ptr<impl> m_priv;
|
||||
shared_ptr<priv> priv_;
|
||||
|
||||
corpus();
|
||||
|
||||
@ -69,6 +72,15 @@ public:
|
||||
|
||||
bool
|
||||
is_empty() const;
|
||||
|
||||
bool
|
||||
operator==(const corpus&) const;
|
||||
|
||||
const functions&
|
||||
get_functions() const;
|
||||
|
||||
const variables&
|
||||
get_variables() const;
|
||||
};// end class corpus.
|
||||
|
||||
}//end namespace abigail
|
||||
|
@ -733,6 +733,34 @@ struct default_eq_functor
|
||||
{return a == b;}
|
||||
};
|
||||
|
||||
|
||||
/// An equality functor to deeply compare pointers.
|
||||
struct deep_ptr_eq_functor
|
||||
{
|
||||
/// This equality operator compares pointers by comparing the
|
||||
/// pointed-to objects.
|
||||
///
|
||||
/// @param first the first comparison argument.
|
||||
///
|
||||
/// @param second the second comparison argument.
|
||||
///
|
||||
/// @return true if the objects pointed to by the pointers are
|
||||
/// equal, false otherwise.
|
||||
template<typename T>
|
||||
bool
|
||||
operator()(const T* first,
|
||||
const T* second)
|
||||
{
|
||||
if (!!first != !!second)
|
||||
return false;
|
||||
|
||||
if (!first)
|
||||
return true;
|
||||
|
||||
return *first == *second;
|
||||
}
|
||||
};
|
||||
|
||||
/// Find the end of the furthest reaching d-path on diagonal k, for
|
||||
/// two sequences. In the paper This is referred to as "the basic
|
||||
/// algorithm".
|
||||
|
@ -171,10 +171,16 @@ public:
|
||||
void
|
||||
set_address_size(char);
|
||||
|
||||
bool
|
||||
operator==(const translation_unit&) const;
|
||||
|
||||
virtual void
|
||||
traverse(ir_node_visitor& v);
|
||||
};//end class translation_unit
|
||||
|
||||
bool
|
||||
operator==(translation_unit_sptr, translation_unit_sptr);
|
||||
|
||||
/// The base type of all declarations.
|
||||
class decl_base : public ir_traversable_base
|
||||
{
|
||||
|
@ -3296,5 +3296,262 @@ compute_diff(const translation_unit_sptr first,
|
||||
}
|
||||
|
||||
// </translation_unit_diff stuff>
|
||||
|
||||
// <corpus stuff>
|
||||
struct corpus_diff::priv
|
||||
{
|
||||
corpus_sptr first_;
|
||||
corpus_sptr second_;
|
||||
edit_script fns_edit_script_;
|
||||
edit_script vars_edit_script_;
|
||||
string_function_ptr_map deleted_fns_;
|
||||
string_function_ptr_map added_fns_;
|
||||
string_changed_function_ptr_map changed_fns_;
|
||||
|
||||
bool
|
||||
lookup_tables_empty() const;
|
||||
|
||||
void
|
||||
clear_lookup_tables();
|
||||
|
||||
void
|
||||
ensure_lookup_tables_populated();
|
||||
}; // end corpus::priv
|
||||
|
||||
/// Tests if the lookup tables are empty.
|
||||
///
|
||||
/// @return true if the lookup tables are empty, false otherwise.
|
||||
bool
|
||||
corpus_diff::priv::lookup_tables_empty() const
|
||||
{
|
||||
return (deleted_fns_.empty()
|
||||
&& added_fns_.empty()
|
||||
&& changed_fns_.empty());
|
||||
}
|
||||
|
||||
/// Clear the lookup tables useful for reporting an enum_diff.
|
||||
void
|
||||
corpus_diff::priv::clear_lookup_tables()
|
||||
{
|
||||
deleted_fns_.clear();
|
||||
added_fns_.clear();
|
||||
changed_fns_.clear();
|
||||
}
|
||||
|
||||
/// If the lookup tables are not yet built, walk the differences and
|
||||
/// fill the lookup tables.
|
||||
void
|
||||
corpus_diff::priv::ensure_lookup_tables_populated()
|
||||
{
|
||||
if (!lookup_tables_empty())
|
||||
return;
|
||||
|
||||
{
|
||||
edit_script& e = fns_edit_script_;
|
||||
|
||||
for (vector<deletion>::const_iterator it = e.deletions().begin();
|
||||
it != e.deletions().end();
|
||||
++it)
|
||||
{
|
||||
unsigned i = it->index();
|
||||
assert(i < first_->get_functions().size());
|
||||
|
||||
function_decl* deleted_fn = first_->get_functions()[i];
|
||||
string n = deleted_fn->get_mangled_name();
|
||||
if (n.empty())
|
||||
n = deleted_fn->get_name();
|
||||
assert(!n.empty());
|
||||
assert(deleted_fns_.find(n) == deleted_fns_.end());
|
||||
deleted_fns_[n] = deleted_fn;
|
||||
}
|
||||
|
||||
for (vector<insertion>::const_iterator it = e.insertions().begin();
|
||||
it != e.insertions().end();
|
||||
++it)
|
||||
{
|
||||
for (vector<unsigned>::const_iterator iit =
|
||||
it->inserted_indexes().begin();
|
||||
iit != it->inserted_indexes().end();
|
||||
++iit)
|
||||
{
|
||||
unsigned i = *iit;
|
||||
function_decl* added_fn = second_->get_functions()[i];
|
||||
string n = added_fn->get_mangled_name();
|
||||
if (n.empty())
|
||||
n = added_fn->get_name();
|
||||
assert(!n.empty());
|
||||
assert(added_fns_.find(n) == added_fns_.end());
|
||||
added_fns_[n] = added_fn;
|
||||
}
|
||||
}
|
||||
|
||||
for (string_function_ptr_map::const_iterator it = deleted_fns_.begin();
|
||||
it != deleted_fns_.end();
|
||||
++it)
|
||||
{
|
||||
string_function_ptr_map::const_iterator it2 =
|
||||
added_fns_.find(it->first);
|
||||
if (it2 != added_fns_.end())
|
||||
changed_fns_[it->first] = std::make_pair(it->second,
|
||||
it2->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructor for @ref corpus_diff.
|
||||
///
|
||||
/// @param first the first corpus of the diff.
|
||||
///
|
||||
/// @param second the second corpus of the diff.
|
||||
corpus_diff::corpus_diff(corpus_sptr first,
|
||||
corpus_sptr second)
|
||||
: priv_(new priv)
|
||||
{
|
||||
priv_->first_ = first;
|
||||
priv_->second_ = second;
|
||||
}
|
||||
|
||||
/// @return the first corpus of the diff.
|
||||
corpus_sptr
|
||||
corpus_diff::first_corpus() const
|
||||
{return priv_->first_;}
|
||||
|
||||
/// @return the second corpus of the diff.
|
||||
corpus_sptr
|
||||
corpus_diff::second_corpus() const
|
||||
{return priv_->second_;}
|
||||
|
||||
/// @return the bare edit script of the functions changed as recorded
|
||||
/// by the diff.
|
||||
edit_script&
|
||||
corpus_diff::function_changes() const
|
||||
{return priv_->fns_edit_script_;}
|
||||
|
||||
/// @return the bare edit script of the variables changed as recorded
|
||||
/// by the diff.
|
||||
edit_script&
|
||||
corpus_diff::variable_changes() const
|
||||
{return priv_->vars_edit_script_;}
|
||||
|
||||
/// @return the length of the changes as recorded by the diff.
|
||||
unsigned
|
||||
corpus_diff::length() const
|
||||
{
|
||||
return (priv_->deleted_fns_.size()
|
||||
+ priv_->added_fns_.size()
|
||||
- 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 serialieed form.
|
||||
///
|
||||
/// @param out the stream to serialize the diff to.
|
||||
///
|
||||
/// @param the prefix to use for the indentation of this
|
||||
/// serialization.
|
||||
void
|
||||
corpus_diff::report(ostream& out, const string& indent) const
|
||||
{
|
||||
unsigned removed = 0, added = 0;
|
||||
for (string_function_ptr_map::const_iterator i =
|
||||
priv_->deleted_fns_.begin();
|
||||
i != priv_->deleted_fns_.end();
|
||||
++i)
|
||||
{
|
||||
if (priv_->added_fns_.find(i->first) == priv_->added_fns_.end())
|
||||
{
|
||||
out << indent
|
||||
<< " '"
|
||||
<< i->second->get_pretty_representation()
|
||||
<< "' was removed\n";
|
||||
++removed;
|
||||
}
|
||||
}
|
||||
if (removed)
|
||||
out << "\n";
|
||||
|
||||
for (string_function_ptr_map::const_iterator i =
|
||||
priv_->added_fns_.begin();
|
||||
i != priv_->added_fns_.end();
|
||||
++i)
|
||||
{
|
||||
if (priv_->deleted_fns_.find(i->first) == priv_->deleted_fns_.end())
|
||||
{
|
||||
out << indent
|
||||
<< " '"
|
||||
<< i->second->get_pretty_representation()
|
||||
<< "' was added\n";
|
||||
++added;
|
||||
}
|
||||
}
|
||||
if (added)
|
||||
out << "\n";
|
||||
|
||||
for (string_changed_function_ptr_map::const_iterator i =
|
||||
priv_->changed_fns_.begin();
|
||||
i != priv_->changed_fns_.end();
|
||||
++i)
|
||||
{
|
||||
out << indent << " '"
|
||||
<< i->second.first->get_pretty_representation()
|
||||
<< "' was changed to '"
|
||||
<< i->second.second->get_pretty_representation()
|
||||
<< "':\n";
|
||||
{
|
||||
function_decl_sptr f(i->second.first, noop_deleter());
|
||||
function_decl_sptr s(i->second.second, noop_deleter());
|
||||
|
||||
diff_sptr diff = compute_diff_for_decls(f, s);
|
||||
if (diff)
|
||||
diff->report(out, indent + " ");
|
||||
}
|
||||
}
|
||||
if (priv_->changed_fns_.size())
|
||||
out << "\n";
|
||||
}
|
||||
|
||||
/// Compute the diff between two instances fo the @ref corpus
|
||||
///
|
||||
/// @param f the first @ref corpus to consider for the diff.
|
||||
///
|
||||
/// @param s the second @ref corpus to consider for the diff.
|
||||
///
|
||||
/// @return the resulting diff between the two @ref corpus.
|
||||
corpus_diff_sptr
|
||||
compute_diff(const corpus_sptr f, const corpus_sptr s)
|
||||
{
|
||||
typedef corpus::functions::const_iterator fns_it_type;
|
||||
typedef corpus::variables::const_iterator vars_it_type;
|
||||
typedef diff_utils::deep_ptr_eq_functor eq_type;
|
||||
|
||||
corpus_diff_sptr r(new corpus_diff(f, s));
|
||||
|
||||
diff_utils::compute_diff<fns_it_type, eq_type>(f->get_functions().begin(),
|
||||
f->get_functions().end(),
|
||||
s->get_functions().begin(),
|
||||
s->get_functions().end(),
|
||||
r->priv_->fns_edit_script_);
|
||||
|
||||
diff_utils::compute_diff<vars_it_type, eq_type>(f->get_variables().begin(),
|
||||
f->get_variables().end(),
|
||||
s->get_variables().begin(),
|
||||
s->get_variables().end(),
|
||||
r->priv_->vars_edit_script_);
|
||||
|
||||
r->priv_->ensure_lookup_tables_populated();
|
||||
|
||||
return r;
|
||||
}
|
||||
// </corpus stuff>
|
||||
|
||||
}// end namespace comparison
|
||||
} // end namespace abigail
|
||||
|
@ -22,9 +22,11 @@
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
#include <ext/stdio_filebuf.h>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
#include "abg-ir.h"
|
||||
#include "abg-corpus.h"
|
||||
#include "abg-reader.h"
|
||||
@ -52,46 +54,238 @@ struct array_deleter
|
||||
}
|
||||
};//end array_deleter
|
||||
|
||||
struct corpus::impl
|
||||
struct corpus::priv
|
||||
{
|
||||
string path;
|
||||
translation_units members;
|
||||
vector<function_decl*> fns;
|
||||
vector<var_decl*> vars;
|
||||
bool is_symbol_table_built;
|
||||
|
||||
private:
|
||||
impl();
|
||||
priv();
|
||||
|
||||
public:
|
||||
impl(const string &p)
|
||||
: path(p)
|
||||
{}
|
||||
};
|
||||
priv(const string &p)
|
||||
: path(p),
|
||||
is_symbol_table_built(false)
|
||||
{}
|
||||
|
||||
/// @param path the path to the file containing the ABI corpus.
|
||||
corpus::corpus(const string& path)
|
||||
{
|
||||
m_priv.reset(new impl(path));
|
||||
}
|
||||
void
|
||||
build_symbol_table();
|
||||
};
|
||||
|
||||
/// Add a translation unit to the current ABI Corpus. Next time
|
||||
/// corpus::save is called, all the translation unit that got added
|
||||
/// to the corpus are going to be serialized on disk in the file
|
||||
/// associated to the current corpus.
|
||||
///
|
||||
/// @param tu
|
||||
void
|
||||
corpus::add(const translation_unit_sptr tu)
|
||||
{
|
||||
m_priv->members.push_back(tu);
|
||||
}
|
||||
/// A visitor type to be used while traversing functions and variables
|
||||
/// of the translations units of the corpus. The goal of this visitor
|
||||
/// is to build a symbol table containing all the public functions and
|
||||
/// global variables of the all the translation units of the the
|
||||
/// corpus.
|
||||
class symtab_build_visitor_type : public ir_node_visitor
|
||||
{
|
||||
vector<function_decl*>& functions;
|
||||
vector<var_decl*>& variables;
|
||||
list<function_decl*> wip_fns;
|
||||
int wip_fns_size;
|
||||
list<var_decl*> wip_vars;
|
||||
int wip_vars_size;
|
||||
|
||||
/// Return the list of translation units of the current corpus.
|
||||
///
|
||||
/// @return the list of translation units of the current corpus.
|
||||
const translation_units&
|
||||
corpus::get_translation_units() const
|
||||
{
|
||||
return m_priv->members;
|
||||
}
|
||||
symtab_build_visitor_type();
|
||||
|
||||
friend class corpus::priv;
|
||||
|
||||
public:
|
||||
symtab_build_visitor_type(vector<function_decl*>&fns,
|
||||
vector<var_decl*>&vars)
|
||||
: functions(fns),
|
||||
variables(vars),
|
||||
wip_fns_size(0),
|
||||
wip_vars_size(0)
|
||||
{}
|
||||
|
||||
/// This function is called while visiting a @ref function_decl IR
|
||||
/// node of a translation unit.
|
||||
///
|
||||
/// Add the function to the symbol table being constructed (WIP
|
||||
/// meaning work in progress).
|
||||
///
|
||||
/// @param fn the function being visited.
|
||||
void
|
||||
visit(function_decl* fn)
|
||||
{
|
||||
wip_fns.push_back(fn);
|
||||
++wip_fns_size;
|
||||
}
|
||||
|
||||
/// This function is called while visiting a
|
||||
/// class_decl::function_decl IR node of a translation unit.
|
||||
///
|
||||
/// Add the member function to the symbol table being constructed
|
||||
/// (WIP meaning work in progress).
|
||||
///
|
||||
/// @param fn the member function being visited.
|
||||
void
|
||||
visit(class_decl::member_function* fn)
|
||||
{
|
||||
wip_fns.push_back(fn);
|
||||
++wip_fns_size;
|
||||
}
|
||||
|
||||
/// This function is called while visiting a @ref var_decl IR node
|
||||
/// of a translation unit.
|
||||
///
|
||||
/// Add the variable to the symbol table being constructed (WIP
|
||||
/// meaning work in progress).
|
||||
///
|
||||
/// @param var the variable being visited.
|
||||
void
|
||||
visit(var_decl* var)
|
||||
{
|
||||
wip_vars.push_back(var);
|
||||
++wip_vars_size;
|
||||
}
|
||||
|
||||
/// This function is called while visiting a @ref var_decl IR node
|
||||
/// of a translation unit.
|
||||
///
|
||||
/// Add the variable to the symbol table being constructed (WIP
|
||||
/// meaning work in progress).
|
||||
///
|
||||
/// @param var the variable being visited.
|
||||
void
|
||||
visit(class_decl::data_member* var)
|
||||
{
|
||||
if (var->is_static())
|
||||
{
|
||||
wip_vars.push_back(var);
|
||||
++wip_vars_size;
|
||||
}
|
||||
}
|
||||
|
||||
};// end struct symtab_build_visitor_type
|
||||
|
||||
/// This is a comparison functor for comparing pointers to @ref
|
||||
/// function_decl.
|
||||
struct func_comp
|
||||
{
|
||||
/// The comparisong operator for pointers to @ref function_decl. It
|
||||
/// performs a string comparison of the mangled names of the
|
||||
/// functions. If the functions don't have mangled names, it
|
||||
/// compares their names instead.
|
||||
///
|
||||
/// @param first the first function to consider in the comparison.
|
||||
///
|
||||
/// @param second the second function to consider in the comparison.
|
||||
///
|
||||
/// @return true if the (mangled) name of the first function is less
|
||||
/// than the (mangled)name of the second one, false otherwise.
|
||||
bool
|
||||
operator()(const function_decl* first,
|
||||
const function_decl* second) const
|
||||
{
|
||||
assert(first != 0 && second != 0);
|
||||
|
||||
string first_name, second_name;
|
||||
first_name = first->get_mangled_name();
|
||||
if (first_name.empty())
|
||||
first_name = first->get_name();
|
||||
assert(!first_name.empty());
|
||||
|
||||
second_name = second->get_mangled_name();
|
||||
if (second_name.empty())
|
||||
second_name = second->get_name();
|
||||
assert(!second_name.empty());
|
||||
|
||||
return first_name < second_name;
|
||||
}
|
||||
};
|
||||
|
||||
/// This is a comparison functor for comparing pointers to @ref
|
||||
/// var_decl.
|
||||
struct var_comp
|
||||
{
|
||||
/// The comparison operator for pointers to @ref var_decl.
|
||||
///
|
||||
/// It perform a string comparison on the names of the variables.
|
||||
///
|
||||
/// @param first the first variable to consider for the comparison.
|
||||
///
|
||||
/// @param second the second variable to consider for the comparison.
|
||||
///
|
||||
/// @return true if first is less than second, false otherwise.
|
||||
bool
|
||||
operator()(const var_decl* first,
|
||||
const var_decl* second) const
|
||||
{
|
||||
assert(first != 0 && second != 0);
|
||||
|
||||
string first_name, second_name;
|
||||
first_name = first->get_qualified_name();
|
||||
assert(!first_name.empty());
|
||||
second_name = second->get_qualified_name();
|
||||
assert(!second_name.empty());
|
||||
|
||||
return first_name < second_name;
|
||||
}
|
||||
};
|
||||
|
||||
/// Build the symbol tables for the corpus. That is, walk all the
|
||||
/// functions of all translation units of the corpus, stuff them in a
|
||||
/// vector and sort the vector. Likewise for the variables.
|
||||
void
|
||||
corpus::priv::build_symbol_table()
|
||||
{
|
||||
symtab_build_visitor_type v(fns, vars);
|
||||
|
||||
for (translation_units::iterator i = members.begin();
|
||||
i != members.end();
|
||||
++i)
|
||||
(*i)->traverse(v);
|
||||
|
||||
fns.reserve(v.wip_fns_size);
|
||||
for (list<function_decl*>::iterator i = v.wip_fns.begin();
|
||||
i != v.wip_fns.end();
|
||||
++i)
|
||||
fns.push_back(*i);
|
||||
v.wip_fns.clear();
|
||||
v.wip_fns_size = 0;
|
||||
|
||||
vars.reserve(v.wip_vars_size);
|
||||
for (list<var_decl*>::iterator i = v.wip_vars.begin();
|
||||
i != v.wip_vars.end();
|
||||
++i)
|
||||
vars.push_back(*i);
|
||||
v.wip_vars.clear();
|
||||
v.wip_vars_size = 0;
|
||||
|
||||
func_comp fc;
|
||||
std::sort(fns.begin(), fns.end(), fc);
|
||||
|
||||
var_comp vc;
|
||||
std::sort(vars.begin(), vars.end(), vc);
|
||||
|
||||
is_symbol_table_built = true;
|
||||
}
|
||||
|
||||
/// @param path the path to the file containing the ABI corpus.
|
||||
corpus::corpus(const string& path)
|
||||
{priv_.reset(new priv(path));}
|
||||
|
||||
/// Add a translation unit to the current ABI Corpus. Next time
|
||||
/// corpus::save is called, all the translation unit that got added
|
||||
/// to the corpus are going to be serialized on disk in the file
|
||||
/// associated to the current corpus.
|
||||
///
|
||||
/// @param tu the new translation unit to add.
|
||||
void
|
||||
corpus::add(const translation_unit_sptr tu)
|
||||
{priv_->members.push_back(tu);}
|
||||
|
||||
/// Return the list of translation units of the current corpus.
|
||||
///
|
||||
/// @return the list of translation units of the current corpus.
|
||||
const translation_units&
|
||||
corpus::get_translation_units() const
|
||||
{return priv_->members;}
|
||||
|
||||
/// Erase the translation units contained in this in-memory object.
|
||||
///
|
||||
@ -99,23 +293,19 @@ public:
|
||||
/// representation of this object is not modified.
|
||||
void
|
||||
corpus::drop_translation_units()
|
||||
{
|
||||
m_priv->members.clear();
|
||||
}
|
||||
{priv_->members.clear();}
|
||||
|
||||
/// Get the file path associated to the corpus file.
|
||||
///
|
||||
/// A subsequent call to corpus::read will deserialize the content of
|
||||
/// the abi file expected at this path; likewise, a call to
|
||||
/// corpus::write will serialize the translation units contained in
|
||||
/// the corpus object into the on-disk file at this path.
|
||||
/// Get the file path associated to the corpus file.
|
||||
///
|
||||
/// A subsequent call to corpus::read will deserialize the content of
|
||||
/// the abi file expected at this path; likewise, a call to
|
||||
/// corpus::write will serialize the translation units contained in
|
||||
/// the corpus object into the on-disk file at this path.
|
||||
|
||||
/// @return the file path associated to the current corpus.
|
||||
string&
|
||||
corpus::get_path() const
|
||||
{
|
||||
return m_priv->path;
|
||||
}
|
||||
/// @return the file path associated to the current corpus.
|
||||
string&
|
||||
corpus::get_path() const
|
||||
{return priv_->path;}
|
||||
|
||||
/// Set the file path associated to the corpus file.
|
||||
///
|
||||
@ -126,17 +316,59 @@ corpus::drop_translation_units()
|
||||
/// @param the new file path to assciate to the current corpus.
|
||||
void
|
||||
corpus::set_path(const string& path)
|
||||
{
|
||||
m_priv->path = path;
|
||||
}
|
||||
{priv_->path = path;}
|
||||
|
||||
/// Tests if the corpus contains no translation unit.
|
||||
///
|
||||
/// @return true if the corpus contains no translation unit.
|
||||
bool
|
||||
corpus::is_empty() const
|
||||
{
|
||||
return m_priv->members.empty();
|
||||
}
|
||||
bool
|
||||
corpus::is_empty() const
|
||||
{return priv_->members.empty();}
|
||||
|
||||
/// Compare the current @ref corpus against another one.
|
||||
///
|
||||
/// @param other the other corpus to compare against.
|
||||
///
|
||||
/// @return true if the two corpus are equal, false otherwise.
|
||||
bool
|
||||
corpus::operator==(const corpus& other) const
|
||||
{
|
||||
translation_units::const_iterator i, j;
|
||||
for (i = get_translation_units().begin(),
|
||||
j = other.get_translation_units().begin();
|
||||
(i != get_translation_units().end()
|
||||
&& j != other.get_translation_units().end());
|
||||
++i, ++j)
|
||||
if ((**i) != (**j))
|
||||
return false;
|
||||
|
||||
return (i == get_translation_units().end()
|
||||
&& j == other.get_translation_units().end());
|
||||
}
|
||||
|
||||
/// @return the vector of functions of the symbol table. The
|
||||
/// functions are sorted using their mangled name or name if they
|
||||
/// don't have mangle names.
|
||||
const corpus::functions&
|
||||
corpus::get_functions() const
|
||||
{
|
||||
if (!priv_->is_symbol_table_built)
|
||||
priv_->build_symbol_table();
|
||||
assert(priv_->is_symbol_table_built);
|
||||
|
||||
return priv_->fns;
|
||||
}
|
||||
|
||||
/// @return the vector of variables of the symbol table. The
|
||||
/// variables are sorted using their name.
|
||||
const corpus::variables&
|
||||
corpus::get_variables() const
|
||||
{
|
||||
if (!priv_->is_symbol_table_built)
|
||||
priv_->build_symbol_table();
|
||||
assert(priv_->is_symbol_table_built);
|
||||
|
||||
return priv_->vars;
|
||||
}
|
||||
|
||||
}// end namespace abigail
|
||||
|
@ -298,7 +298,22 @@ void
|
||||
translation_unit::set_address_size(char a)
|
||||
{priv_->address_size_= a;}
|
||||
|
||||
/// This implements the traversable_base::traverse pure virtual
|
||||
/// Compare the current translation unit against another one.
|
||||
///
|
||||
/// @param other the other tu to compare against.
|
||||
///
|
||||
/// @return true if the two translation units are equal, false
|
||||
/// otherwise.
|
||||
bool
|
||||
translation_unit::operator==(const translation_unit& other)const
|
||||
{
|
||||
if (get_address_size() != other.get_address_size())
|
||||
return false;
|
||||
|
||||
return *get_global_scope() == *other.get_global_scope();
|
||||
}
|
||||
|
||||
/// This implements the ir_traversable_base::traverse virtual
|
||||
/// function.
|
||||
///
|
||||
/// @param v the visitor used on the member nodes of the translation
|
||||
@ -310,6 +325,25 @@ translation_unit::traverse(ir_node_visitor& v)
|
||||
translation_unit::~translation_unit()
|
||||
{}
|
||||
|
||||
/// A deep comparison operator for pointers to translation units.
|
||||
///
|
||||
/// @param l the first translation unit to consider for the comparison.
|
||||
///
|
||||
/// @param r the second translation unit to consider for the comparison.
|
||||
///
|
||||
/// @return true if the two translation units are equal, false otherwise.
|
||||
bool
|
||||
operator==(translation_unit_sptr l, translation_unit_sptr r)
|
||||
{
|
||||
if (l.get() == r.get())
|
||||
return true;
|
||||
|
||||
if (!!l != !!r)
|
||||
return false;
|
||||
|
||||
return *l == *r;
|
||||
}
|
||||
|
||||
// </translation_unit stuff>
|
||||
|
||||
// <Decl definition>
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <libgen.h>
|
||||
#include <fstream>
|
||||
#include "abg-tools-utils.h"
|
||||
|
||||
using std::string;
|
||||
@ -35,6 +36,8 @@ namespace tools
|
||||
{
|
||||
|
||||
using std::ostream;
|
||||
using std::istream;
|
||||
using std::ifstream;
|
||||
using std::string;
|
||||
|
||||
#define DECLARE_STAT(st) \
|
||||
@ -213,5 +216,62 @@ check_file(const string& path,
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Guess the type of the content of an input stream.
|
||||
///
|
||||
/// @param in the input stream to guess the content type for.
|
||||
///
|
||||
/// @return the type of content guessed.
|
||||
file_type
|
||||
guess_file_type(istream& in)
|
||||
{
|
||||
const unsigned BUF_LEN = 12;
|
||||
const unsigned NB_BYTES_TO_READ = 11;
|
||||
|
||||
char buf[BUF_LEN];
|
||||
memset(buf, 0, BUF_LEN);
|
||||
|
||||
std::streampos initial_pos = in.tellg();
|
||||
in.read(buf, NB_BYTES_TO_READ);
|
||||
in.seekg(initial_pos);
|
||||
|
||||
if (in.gcount() < 4 || in.bad())
|
||||
return FILE_TYPE_UNKNOWN;
|
||||
|
||||
if (buf[0] == 0x7f
|
||||
&& buf[1] == 'E'
|
||||
&& buf[2] == 'L'
|
||||
&& buf[3] == 'F')
|
||||
return FILE_TYPE_ELF;
|
||||
|
||||
if (buf[0] == '<'
|
||||
&& buf[1] == 'a'
|
||||
&& buf[2] == 'b'
|
||||
&& buf[3] == 'i'
|
||||
&& buf[4] == '-'
|
||||
&& buf[5] == 'i'
|
||||
&& buf[6] == 'n'
|
||||
&& buf[7] == 's'
|
||||
&& buf[8] == 't'
|
||||
&& buf[9] == 'r'
|
||||
&& buf[10] == ' ')
|
||||
return FILE_TYPE_NATIVE_BI;
|
||||
|
||||
return FILE_TYPE_UNKNOWN;
|
||||
}
|
||||
|
||||
/// Guess the type of the content of an file.
|
||||
///
|
||||
/// @param file_path the path to the file to consider.
|
||||
///
|
||||
/// @return the type of content guessed.
|
||||
file_type
|
||||
guess_file_type(const std::string& file_path)
|
||||
{
|
||||
ifstream in(file_path.c_str(), ifstream::binary);
|
||||
file_type r = guess_file_type(in);
|
||||
in.close();
|
||||
return r;
|
||||
}
|
||||
|
||||
}//end namespace tools
|
||||
}//end namespace abigail
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
#include <string>
|
||||
#include <ostream>
|
||||
#include <istream>
|
||||
|
||||
namespace abigail
|
||||
{
|
||||
@ -40,5 +41,20 @@ bool ensure_dir_path_created(const std::string&);
|
||||
bool ensure_parent_dir_created(const std::string&);
|
||||
bool check_file(const std::string& path, std::ostream& out);
|
||||
|
||||
/// The different types of files understood the bi* suite of tools.
|
||||
enum file_type
|
||||
{
|
||||
/// A file type we don't know about.
|
||||
FILE_TYPE_UNKNOWN,
|
||||
/// The native xml file format representing a translation unit.
|
||||
FILE_TYPE_NATIVE_BI,
|
||||
/// An elf file. Read this kind of file should yield an
|
||||
/// abigail::corpus type.
|
||||
FILE_TYPE_ELF,
|
||||
};
|
||||
|
||||
file_type guess_file_type(std::istream& in);
|
||||
file_type guess_file_type(const std::string& file_path);
|
||||
|
||||
}// end namespace tools
|
||||
}//end namespace abigail
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "abg-comparison.h"
|
||||
#include "abg-tools-utils.h"
|
||||
#include "abg-reader.h"
|
||||
#include "abg-dwarf-reader.h"
|
||||
|
||||
using std::string;
|
||||
using std::ostream;
|
||||
@ -35,9 +36,12 @@ using std::cout;
|
||||
using std::cerr;
|
||||
using abigail::translation_unit;
|
||||
using abigail::translation_unit_sptr;
|
||||
using abigail::corpus_sptr;
|
||||
using abigail::comparison::translation_unit_diff_sptr;
|
||||
using abigail::comparison::corpus_diff_sptr;
|
||||
using abigail::comparison::compute_diff;
|
||||
using abigail::tools::check_file;
|
||||
using abigail::tools::guess_file_type;
|
||||
|
||||
struct options
|
||||
{
|
||||
@ -108,26 +112,82 @@ main(int argc, char* argv[])
|
||||
if (!check_file(opts.file2, cerr))
|
||||
return true;
|
||||
|
||||
translation_unit_sptr t1 =
|
||||
abigail::xml_reader::read_translation_unit_from_file(opts.file1);
|
||||
abigail::tools::file_type t1_type, t2_type;
|
||||
|
||||
translation_unit_sptr t2 =
|
||||
abigail::xml_reader::read_translation_unit_from_file(opts.file2);
|
||||
t1_type = guess_file_type(opts.file1);
|
||||
if (t1_type == abigail::tools::FILE_TYPE_UNKNOWN)
|
||||
{
|
||||
cerr << "Unknown content type for file " << opts.file1 << "\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!t1)
|
||||
t2_type = guess_file_type(opts.file2);
|
||||
if (t2_type == abigail::tools::FILE_TYPE_UNKNOWN)
|
||||
{
|
||||
cerr << "Unknown content type for file " << opts.file2 << "\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
translation_unit_sptr t1, t2;
|
||||
corpus_sptr c1, c2;
|
||||
|
||||
switch (t1_type)
|
||||
{
|
||||
case abigail::tools::FILE_TYPE_UNKNOWN:
|
||||
cerr << "Unknown content type for file " << opts.file1 << "\n";
|
||||
return true;
|
||||
break;
|
||||
case abigail::tools::FILE_TYPE_NATIVE_BI:
|
||||
t1 = abigail::xml_reader::read_translation_unit_from_file(opts.file1);
|
||||
break;
|
||||
case abigail::tools::FILE_TYPE_ELF:
|
||||
c1 = abigail::dwarf_reader::read_corpus_from_elf(opts.file1);
|
||||
break;
|
||||
}
|
||||
|
||||
switch (t2_type)
|
||||
{
|
||||
case abigail::tools::FILE_TYPE_UNKNOWN:
|
||||
cerr << "Unknown content type for file " << opts.file2 << "\n";
|
||||
return true;
|
||||
break;
|
||||
case abigail::tools::FILE_TYPE_NATIVE_BI:
|
||||
t2 = abigail::xml_reader::read_translation_unit_from_file(opts.file2);
|
||||
break;
|
||||
case abigail::tools::FILE_TYPE_ELF:
|
||||
c2 = abigail::dwarf_reader::read_corpus_from_elf(opts.file2);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!t1 && !c1)
|
||||
{
|
||||
cerr << "failed to read input file " << opts.file1 << "\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!t2)
|
||||
if (!t2 && !c2)
|
||||
{
|
||||
cerr << "failed to read input file" << opts.file2 << "\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
translation_unit_diff_sptr changes = compute_diff(t1, t2);
|
||||
changes->report(cout);
|
||||
if (!!c1 != !!c2
|
||||
|| !!t1 != !!t2)
|
||||
{
|
||||
cerr << "the two input should be of the same kind\n";
|
||||
return true;
|
||||
}
|
||||
|
||||
if (t1)
|
||||
{
|
||||
translation_unit_diff_sptr changes = compute_diff(t1, t2);
|
||||
changes->report(cout);
|
||||
}
|
||||
else if (c1)
|
||||
{
|
||||
corpus_diff_sptr changes = compute_diff(c1, c2);
|
||||
changes->report(cout);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user