X-Git-Url: https://oss.titaniummirror.com/gitweb?a=blobdiff_plain;f=libstdc%2B%2B-v3%2Ftestsuite%2Futil%2Ftestsuite_abi.cc;fp=libstdc%2B%2B-v3%2Ftestsuite%2Futil%2Ftestsuite_abi.cc;h=ae146babb877d24f435151db70e7cc01c1db056f;hb=6fed43773c9b0ce596dca5686f37ac3fc0fa11c0;hp=0000000000000000000000000000000000000000;hpb=27b11d56b743098deb193d510b337ba22dc52e5c;p=msp430-gcc.git diff --git a/libstdc++-v3/testsuite/util/testsuite_abi.cc b/libstdc++-v3/testsuite/util/testsuite_abi.cc new file mode 100644 index 00000000..ae146bab --- /dev/null +++ b/libstdc++-v3/testsuite/util/testsuite_abi.cc @@ -0,0 +1,548 @@ +// -*- C++ -*- + +// Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. + +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU 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 Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this library; see the file COPYING3. If not see +// . + + +// Benjamin Kosnik + +#include "testsuite_abi.h" +#include +#include +#include +#include +#include +#include + +using namespace std; + +void +symbol::init(string& data) +{ + const char delim = ':'; + const char version_delim = '@'; + const string::size_type npos = string::npos; + string::size_type n = 0; + + // Set the type. + if (data.find("FUNC") == 0) + type = symbol::function; + else if (data.find("OBJECT") == 0) + type = symbol::object; + + n = data.find_first_of(delim); + if (n != npos) + data.erase(data.begin(), data.begin() + n + 1); + + // Iff object, get size info. + if (type == symbol::object) + { + n = data.find_first_of(delim); + if (n != npos) + { + string objectsize(data.begin(), data.begin() + n); + istringstream iss(objectsize); + int x; + iss >> x; + if (!iss.fail()) + size = x; + data.erase(data.begin(), data.begin() + n + 1); + } + } + + // Set the name and raw_name. + raw_name = string(data.begin(), data.end()); + n = data.find_first_of(version_delim); + if (n != npos) + { + // Found version string. + name = string(data.begin(), data.begin() + n); + n = data.find_last_of(version_delim); + data.erase(data.begin(), data.begin() + n + 1); + + // Set version name. + version_name = data; + } + else + { + // No versioning info. + name = string(data.begin(), data.end()); + version_status = symbol::none; + } + + // Set the demangled name. + demangled_name = demangle(name); +} + +void +symbol::print() const +{ + const char tab = '\t'; + cout << name << endl; + + if (demangled_name != name) + cout << demangled_name << endl; + + string vers; + switch (version_status) + { + case none: + vers = "none"; + break; + case compatible: + vers = "compatible"; + break; + case incompatible: + vers = "incompatible"; + break; + case unversioned: + vers = "unversioned"; + break; + default: + vers = ""; + } + cout << "version status: " << vers << endl; + + if (version_name.size() + && (version_status == compatible || version_status == incompatible)) + cout << version_name << endl; + + string type_string; + switch (type) + { + case function: + type_string = "function"; + break; + case object: + type_string = "object"; + break; + case uncategorized: + type_string = "uncategorized"; + break; + default: + type_string = ""; + } + cout << "type: " << type_string << endl; + + if (type == object) + cout << "type size: " << size << endl; + + string status_string; + switch (status) + { + case added: + status_string = "added"; + break; + case subtracted: + status_string = "subtracted"; + break; + case undesignated: + status_string = "undesignated"; + break; + default: + status_string = ""; + } + cout << "status: " << status_string << endl; + + cout << endl; +} + + +bool +check_version(symbol& test, bool added) +{ + // Construct list of compatible versions. + typedef std::vector compat_list; + static compat_list known_versions; + if (known_versions.empty()) + { + // NB: First version here must be the default version for this + // version of DT_SONAME. + known_versions.push_back("GLIBCXX_3.4"); + known_versions.push_back("GLIBCXX_3.4.1"); + known_versions.push_back("GLIBCXX_3.4.2"); + known_versions.push_back("GLIBCXX_3.4.3"); + known_versions.push_back("GLIBCXX_3.4.4"); + known_versions.push_back("GLIBCXX_3.4.5"); + known_versions.push_back("GLIBCXX_3.4.6"); + known_versions.push_back("GLIBCXX_3.4.7"); + known_versions.push_back("GLIBCXX_3.4.8"); + known_versions.push_back("GLIBCXX_3.4.9"); + known_versions.push_back("GLIBCXX_3.4.10"); + known_versions.push_back("GLIBCXX_3.4.11"); + known_versions.push_back("GLIBCXX_3.4.12"); + known_versions.push_back("GLIBCXX_3.4.13"); + known_versions.push_back("GLIBCXX_LDBL_3.4"); + known_versions.push_back("GLIBCXX_LDBL_3.4.7"); + known_versions.push_back("GLIBCXX_LDBL_3.4.10"); + known_versions.push_back("CXXABI_1.3"); + known_versions.push_back("CXXABI_1.3.1"); + known_versions.push_back("CXXABI_1.3.2"); + known_versions.push_back("CXXABI_1.3.3"); + known_versions.push_back("CXXABI_LDBL_1.3"); + } + compat_list::iterator begin = known_versions.begin(); + compat_list::iterator end = known_versions.end(); + + // Check for compatible version. + if (test.version_name.size()) + { + compat_list::iterator it1 = find(begin, end, test.version_name); + compat_list::iterator it2 = find(begin, end, test.name); + if (it1 != end) + test.version_status = symbol::compatible; + else + test.version_status = symbol::incompatible; + + // Check that added symbols aren't added in the base version. + if (added && test.version_name == known_versions[0]) + test.version_status = symbol::incompatible; + + // Check that long double compatibility symbols demangled as + // __float128 are put into some _LDBL_ version name. + if (added && test.demangled_name.find("__float128") != std::string::npos) + { + // Has to be in _LDBL_ version name. + if (test.version_name.find("_LDBL_") == std::string::npos) + test.version_status = symbol::incompatible; + } + + // Check for weak label. + if (it1 == end && it2 == end) + test.version_status = symbol::incompatible; + + // Check that + // GLIBCXX_3.4 + // GLIBCXX_3.4.5 + // version as compatible + // XXX + } + else + { + if (added) + { + // New version labels are ok. The rest are not. + compat_list::iterator it2 = find(begin, end, test.name); + if (it2 != end) + test.version_status = symbol::compatible; + else + test.version_status = symbol::incompatible; + } + } + return test.version_status == symbol::compatible; +} + +bool +check_compatible(symbol& lhs, symbol& rhs, bool verbose) +{ + bool ret = true; + const char tab = '\t'; + + // Check to see if symbol_objects are compatible. + if (lhs.type != rhs.type) + { + ret = false; + if (verbose) + cout << tab << "incompatible types" << endl; + } + + if (lhs.name != rhs.name) + { + ret = false; + if (verbose) + cout << tab << "incompatible names" << endl; + } + + if (lhs.size != rhs.size) + { + ret = false; + if (verbose) + { + cout << tab << "incompatible sizes" << endl; + cout << tab << lhs.size << endl; + cout << tab << rhs.size << endl; + } + } + + if (lhs.version_name != rhs.version_name + && !check_version(lhs) && !check_version(rhs)) + { + ret = false; + if (verbose) + { + cout << tab << "incompatible versions" << endl; + cout << tab << lhs.version_name << endl; + cout << tab << rhs.version_name << endl; + } + } + + if (verbose) + cout << endl; + + return ret; +} + + +inline bool +has_symbol(const string& name, const symbols& s) throw() +{ return s.find(name) != s.end(); } + +const symbol& +get_symbol(const string& name, const symbols& s) +{ + symbols::const_iterator i = s.find(name); + if (i != s.end()) + { + return i->second; + } + else + { + ostringstream os; + os << "get_symbol failed for symbol " << name; + __throw_logic_error(os.str().c_str()); + } +} + +void +examine_symbol(const char* name, const char* file) +{ + try + { + symbols s = create_symbols(file); + const symbol& sym = get_symbol(name, s); + sym.print(); + } + catch(...) + { __throw_exception_again; } +} + +int +compare_symbols(const char* baseline_file, const char* test_file, + bool verbose) +{ + // Input both lists of symbols into container. + symbols baseline = create_symbols(baseline_file); + symbols test = create_symbols(test_file); + + // Sanity check results. + if (!baseline.size() || !test.size()) + { + cerr << "Problems parsing the list of exported symbols." << endl; + exit(2); + } + + // Check to see if any long double compatibility symbols are produced. + bool ld_version_found(false); + symbols::iterator li(test.begin()); + while (!ld_version_found && li != test.end()) + { + if (li->second.version_name.find("_LDBL_") != std::string::npos) + ld_version_found = true; + ++li; + } + + // Sort out names. + // Assuming all baseline names and test names are both unique w/ no + // duplicates. + // + // The names added to missing_names are baseline names not found in + // test names + // -> symbols that have been deleted. + // + // The names added to added_names are test names not in + // baseline names + // -> symbols that have been added. + typedef std::vector symbol_names; + symbol_names shared_names; + symbol_names missing_names; + symbol_names added_names; + for (li = test.begin(); li != test.end(); ++li) + added_names.push_back(li->first); + + for (symbols::iterator i = baseline.begin(); i != baseline.end(); ++i) + { + string name(i->first); + symbol_names::iterator end = added_names.end(); + symbol_names::iterator it = find(added_names.begin(), end, name); + if (it != end) + { + // Found. + shared_names.push_back(name); + added_names.erase(it); + } + else + { + // Iff no test long double compatibility symbols at all and the symbol + // missing is a baseline long double compatibility symbol, skip. + string version_name(i->second.version_name); + bool base_ld(version_name.find("_LDBL_") != std::string::npos); + if (!base_ld || base_ld && ld_version_found) + missing_names.push_back(name); + } + } + + // Fill out list of incompatible symbols. + typedef pair symbol_pair; + vector incompatible; + + // Check missing names for compatibility. + for (size_t j = 0; j < missing_names.size(); ++j) + { + symbol& sbase = baseline[missing_names[j]]; + sbase.status = symbol::subtracted; + incompatible.push_back(symbol_pair(sbase, sbase)); + } + + // Check shared names for compatibility. + const symbol_names::size_type shared_size = shared_names.size(); + for (size_t k = 0; k < shared_size; ++k) + { + symbol& sbase = baseline[shared_names[k]]; + symbol& stest = test[shared_names[k]]; + stest.status = symbol::existing; + if (!check_compatible(sbase, stest)) + incompatible.push_back(symbol_pair(sbase, stest)); + } + + // Check added names for compatibility. + const symbol_names::size_type added_size = added_names.size(); + for (size_t l = 0; l < added_size; ++l) + { + symbol& stest = test[added_names[l]]; + stest.status = symbol::added; + if (!check_version(stest, true)) + incompatible.push_back(symbol_pair(stest, stest)); + } + + // Report results. + if (verbose && added_names.size()) + { + cout << endl << added_names.size() << " added symbols " << endl; + for (size_t j = 0; j < added_names.size() ; ++j) + { + cout << j << endl; + test[added_names[j]].print(); + } + } + + if (verbose && missing_names.size()) + { + cout << endl << missing_names.size() << " missing symbols " << endl; + for (size_t j = 0; j < missing_names.size() ; ++j) + { + cout << j << endl; + baseline[missing_names[j]].print(); + } + } + + if (verbose && incompatible.size()) + { + cout << endl << incompatible.size() << " incompatible symbols " << endl; + for (size_t j = 0; j < incompatible.size() ; ++j) + { + // First, print index. + cout << j << endl; + + // Second, report name. + symbol& sbase = incompatible[j].first; + symbol& stest = incompatible[j].second; + stest.print(); + + // Second, report reason or reasons incompatible. + check_compatible(sbase, stest, true); + } + } + + cout << "\n\t\t=== libstdc++-v3 check-abi Summary ===" << endl; + cout << endl; + cout << "# of added symbols:\t\t " << added_names.size() << endl; + cout << "# of missing symbols:\t\t " << missing_names.size() << endl; + cout << "# of incompatible symbols:\t " << incompatible.size() << endl; + cout << endl; + cout << "using: " << baseline_file << endl; + + return !(missing_names.size() || incompatible.size()); +} + + +symbols +create_symbols(const char* file) +{ + symbols s; + ifstream ifs(file); + if (ifs.is_open()) + { + // Organize file data into an associated container (symbols) of symbol + // objects mapped to mangled names without versioning + // information. + const string empty; + string line = empty; + while (getline(ifs, line).good()) + { + symbol tmp; + tmp.init(line); + s[tmp.name] = tmp; + line = empty; + } + } + else + { + ostringstream os; + os << "create_symbols failed for file " << file; + __throw_runtime_error(os.str().c_str()); + } + return s; +} + + +const char* +demangle(const std::string& mangled) +{ + const char* name; + if (mangled[0] != '_' || mangled[1] != 'Z') + { + // This is not a mangled symbol, thus has "C" linkage. + name = mangled.c_str(); + } + else + { + // Use __cxa_demangle to demangle. + int status = 0; + name = abi::__cxa_demangle(mangled.c_str(), 0, 0, &status); + if (!name) + { + switch (status) + { + case 0: + name = "error code = 0: success"; + break; + case -1: + name = "error code = -1: memory allocation failure"; + break; + case -2: + name = "error code = -2: invalid mangled name"; + break; + case -3: + name = "error code = -3: invalid arguments"; + break; + default: + name = "error code unknown - who knows what happened"; + } + } + } + return name; +} +