OSDN Git Service

2006-06-06 Benjamin Kosnik <bkoz@redhat.com>
[pf3gnuchains/gcc-fork.git] / libstdc++-v3 / testsuite / util / testsuite_abi.cc
diff --git a/libstdc++-v3/testsuite/util/testsuite_abi.cc b/libstdc++-v3/testsuite/util/testsuite_abi.cc
new file mode 100644 (file)
index 0000000..6ed559f
--- /dev/null
@@ -0,0 +1,530 @@
+// -*- C++ -*-
+
+// Copyright (C) 2004, 2005, 2006 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 2, 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 COPYING.  If not, write to
+// the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston,
+// MA 02110-1301, USA.
+
+// As a special exception, you may use this file as part of a free
+// software library without restriction.  Specifically, if other files
+// instantiate templates or use macros or inline functions from this
+// file, or you compile this file and link it with other files to
+// produce an executable, this file does not by itself cause the
+// resulting executable to be covered by the GNU General Public
+// License.  This exception does not however invalidate any other
+// reasons why the executable file might be covered by the GNU General
+// Public License.
+
+// Benjamin Kosnik  <bkoz@redhat.com>
+
+#include "testsuite_abi.h"
+#include <sstream>
+#include <fstream>
+#include <iostream>
+
+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 size(data.begin(), data.begin() + n);
+         istringstream iss(size);
+         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 = "<default>";
+    }
+  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 = "<default>";
+    }
+  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 = "<default>";
+    }
+  cout << "status: " << status_string << endl;
+
+  cout << endl;
+}
+
+
+bool
+check_version(symbol& test, bool added)
+{
+  // Construct list of compatible versions.
+  typedef std::vector<std::string> 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_LDBL_3.4");
+      known_versions.push_back("GLIBCXX_LDBL_3.4.7");
+      known_versions.push_back("CXXABI_1.3");
+      known_versions.push_back("CXXABI_1.3.1");
+      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 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;
+}
+
+
+bool
+has_symbol(const string& mangled, const symbols& s) throw()
+{
+  const symbol_names& names = s.first;
+  symbol_names::const_iterator i = find(names.begin(), names.end(), mangled);
+  return i != names.end();
+}
+
+symbol&
+get_symbol(const string& mangled, const symbols& s)
+{
+  const symbol_names& names = s.first;
+  symbol_names::const_iterator i = find(names.begin(), names.end(), mangled);
+  if (i != names.end())
+    {
+      symbol_objects objects = s.second;
+      return objects[mangled];
+    }
+  else
+    {
+      ostringstream os;
+      os << "get_symbol failed for symbol " << mangled;
+      __throw_logic_error(os.str().c_str());
+    }
+}
+
+void 
+examine_symbol(const char* name, const char* file)
+{
+  try
+    {
+      symbols s = create_symbols(file);
+      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);
+  symbol_names& baseline_names = baseline.first;
+  symbol_objects& baseline_objects = baseline.second;
+  symbol_names& test_names = test.first;
+  symbol_objects& test_objects = test.second;
+
+  //  Sanity check results.
+  const symbol_names::size_type baseline_size = baseline_names.size();
+  const symbol_names::size_type test_size = test_names.size();
+  if (!baseline_size || !test_size)
+    {
+      cerr << "Problems parsing the list of exported symbols." << endl;
+      exit(2);
+    }
+
+  // Sort out names.
+  // Assuming baseline_names, 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.
+  symbol_names shared_names;
+  symbol_names missing_names;
+  symbol_names added_names = test_names;
+  for (size_t i = 0; i < baseline_size; ++i)
+    {
+      string what(baseline_names[i]);
+      symbol_names::iterator end = added_names.end();
+      symbol_names::iterator it = find(added_names.begin(), end, what);
+      if (it != end)
+       {
+         // Found.
+         shared_names.push_back(what);
+         added_names.erase(it);
+       }
+      else
+       missing_names.push_back(what);
+    }
+
+  // Check missing names for compatibility.
+  typedef pair<symbol, symbol> symbol_pair;
+  vector<symbol_pair> incompatible;
+  const symbol_names::size_type missing_size = missing_names.size();
+  for (size_t j = 0; j < missing_size; ++j)
+    {
+      symbol& base = baseline_objects[missing_names[j]];
+      base.status = symbol::subtracted;
+      incompatible.push_back(symbol_pair(base, base));
+    }
+
+  // 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& base = baseline_objects[shared_names[k]];
+      symbol& test = test_objects[shared_names[k]];
+      test.status = symbol::existing;
+      if (!check_compatible(base, test))
+       incompatible.push_back(symbol_pair(base, test));
+    }
+
+  // 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& test = test_objects[added_names[l]];
+      test.status = symbol::added;
+      if (!check_version(test, true))
+       incompatible.push_back(symbol_pair(test, test));
+    }
+
+  // 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_objects[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_objects[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& base = incompatible[j].first;
+         symbol& test = incompatible[j].second;
+         test.print();
+         
+         // Second, report reason or reasons incompatible.
+         check_compatible(base, test, 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 container of symbol objects, and a
+      // container of mangled names without versioning information.
+      symbol_names& names = s.first;
+      symbol_objects& objects = s.second;
+      const string empty;
+      string line = empty;
+      while (getline(ifs, line).good())
+       {
+         symbol tmp;
+         tmp.init(line);
+         objects[tmp.name] = tmp;
+         names.push_back(tmp.name);
+         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;
+}
+