OSDN Git Service

64c90118821c06edecef50fba9bf03b95a0fea8e
[pf3gnuchains/gcc-fork.git] / libstdc++-v3 / testsuite / util / testsuite_abi.cc
1 // -*- C++ -*-
2
3 // Copyright (C) 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
4
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation; either version 2, or (at
8 // your option) any later version.
9
10 // This library is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // General Public License for more details.
14
15 // You should have received a copy of the GNU General Public License
16 // along with this library; see the file COPYING.  If not, write to
17 // the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston,
18 // MA 02110-1301, USA.
19
20 // As a special exception, you may use this file as part of a free
21 // software library without restriction.  Specifically, if other files
22 // instantiate templates or use macros or inline functions from this
23 // file, or you compile this file and link it with other files to
24 // produce an executable, this file does not by itself cause the
25 // resulting executable to be covered by the GNU General Public
26 // License.  This exception does not however invalidate any other
27 // reasons why the executable file might be covered by the GNU General
28 // Public License.
29
30 // Benjamin Kosnik  <bkoz@redhat.com>
31
32 #include "testsuite_abi.h"
33 #include <sstream>
34 #include <fstream>
35 #include <iostream>
36 #include <cstdlib>
37
38 using namespace std;
39
40 void 
41 symbol::init(string& data)
42 {
43   const char delim = ':';
44   const char version_delim = '@';
45   const string::size_type npos = string::npos;
46   string::size_type n = 0;
47
48   // Set the type.
49   if (data.find("FUNC") == 0)
50     type = symbol::function;
51   else if (data.find("OBJECT") == 0)
52     type = symbol::object;
53
54   n = data.find_first_of(delim);
55   if (n != npos)
56     data.erase(data.begin(), data.begin() + n + 1);
57
58   // Iff object, get size info.
59   if (type == symbol::object)
60     {
61       n = data.find_first_of(delim);
62       if (n != npos)
63         {
64           string size(data.begin(), data.begin() + n);
65           istringstream iss(size);
66           int x;
67           iss >> x;
68           if (!iss.fail())
69             size = x;
70           data.erase(data.begin(), data.begin() + n + 1);
71         }
72     }
73
74   // Set the name and raw_name.
75   raw_name = string(data.begin(), data.end());
76   n = data.find_first_of(version_delim);
77   if (n != npos)
78     {
79       // Found version string.
80       name = string(data.begin(), data.begin() + n);
81       n = data.find_last_of(version_delim);
82       data.erase(data.begin(), data.begin() + n + 1);
83
84       // Set version name.
85       version_name = data;
86     }
87   else
88     {
89       // No versioning info.
90       name = string(data.begin(), data.end());
91       version_status = symbol::none;
92     }
93
94   // Set the demangled name.
95   demangled_name = demangle(name);
96 }
97
98 void
99 symbol::print() const
100 {
101   const char tab = '\t';
102   cout << name << endl;
103
104   if (demangled_name != name)
105     cout << demangled_name << endl;
106
107   string vers;
108   switch (version_status)
109     {
110     case none:
111       vers = "none";
112       break;
113     case compatible:
114       vers = "compatible";
115       break;
116     case incompatible:
117       vers = "incompatible";
118       break;
119      case unversioned:
120       vers = "unversioned";
121       break;
122    default:
123       vers = "<default>";
124     }
125   cout << "version status: " << vers << endl;
126
127   if (version_name.size() 
128       && (version_status == compatible || version_status == incompatible))
129     cout << version_name << endl;  
130
131   string type_string;
132   switch (type)
133     {
134     case function:
135       type_string = "function";
136       break;
137     case object:
138       type_string = "object";
139       break;
140     case uncategorized:
141       type_string = "uncategorized";
142       break;
143     default:
144       type_string = "<default>";
145     }
146   cout << "type: " << type_string << endl;
147   
148   if (type == object)
149     cout << "type size: " << size << endl;
150
151   string status_string;
152   switch (status)
153     {
154     case added:
155       status_string = "added";
156       break;
157     case subtracted:
158       status_string = "subtracted";
159       break;
160     case undesignated:
161       status_string = "undesignated";
162       break;
163     default:
164       status_string = "<default>";
165     }
166   cout << "status: " << status_string << endl;
167
168   cout << endl;
169 }
170
171
172 bool
173 check_version(symbol& test, bool added)
174 {
175   // Construct list of compatible versions.
176   typedef std::vector<std::string> compat_list;
177   static compat_list known_versions;
178   if (known_versions.empty())
179     {
180       // NB: First version here must be the default version for this
181       // version of DT_SONAME.
182       known_versions.push_back("GLIBCXX_3.4");
183       known_versions.push_back("GLIBCXX_3.4.1");
184       known_versions.push_back("GLIBCXX_3.4.2");
185       known_versions.push_back("GLIBCXX_3.4.3");
186       known_versions.push_back("GLIBCXX_3.4.4"); 
187       known_versions.push_back("GLIBCXX_3.4.5");
188       known_versions.push_back("GLIBCXX_3.4.6");
189       known_versions.push_back("GLIBCXX_3.4.7");
190       known_versions.push_back("GLIBCXX_3.4.8");
191       known_versions.push_back("GLIBCXX_3.4.9");
192       known_versions.push_back("GLIBCXX_LDBL_3.4");
193       known_versions.push_back("GLIBCXX_LDBL_3.4.7");
194       known_versions.push_back("CXXABI_1.3");
195       known_versions.push_back("CXXABI_1.3.1");
196       known_versions.push_back("CXXABI_1.3.2");
197       known_versions.push_back("CXXABI_LDBL_1.3");
198     }
199   compat_list::iterator begin = known_versions.begin();
200   compat_list::iterator end = known_versions.end();
201
202   // Check for compatible version.
203   if (test.version_name.size())
204     {
205       compat_list::iterator it1 = find(begin, end, test.version_name);
206       compat_list::iterator it2 = find(begin, end, test.name);
207       if (it1 != end)
208         test.version_status = symbol::compatible;
209       else
210         test.version_status = symbol::incompatible;
211       
212       // Check that added symbols aren't added in the base version.
213       if (added && test.version_name == known_versions[0])
214         test.version_status = symbol::incompatible;
215       
216       // Check for weak label.
217       if (it1 == end && it2 == end)
218         test.version_status = symbol::incompatible;
219       
220       // Check that 
221       // GLIBCXX_3.4
222       // GLIBCXX_3.4.5
223       // version as compatible
224       // XXX
225     }
226   else
227     {
228       if (added)
229         {
230           // New version labels are ok. The rest are not.
231           compat_list::iterator it2 = find(begin, end, test.name);
232           if (it2 != end)
233             test.version_status = symbol::compatible;
234           else
235             test.version_status = symbol::incompatible;
236         }
237     }
238   return test.version_status == symbol::compatible;
239 }
240
241 bool 
242 check_compatible(symbol& lhs, symbol& rhs, bool verbose)
243 {
244   bool ret = true;
245   const char tab = '\t';
246
247   // Check to see if symbol_objects are compatible.
248   if (lhs.type != rhs.type)
249     {
250       ret = false;
251       if (verbose)
252         cout << tab << "incompatible types" << endl;
253     }
254   
255   if (lhs.name != rhs.name)
256     {
257       ret = false;
258       if (verbose)
259         cout << tab << "incompatible names" << endl;
260     }
261
262   if (lhs.size != rhs.size)
263     {
264       ret = false;
265       if (verbose)
266         {
267           cout << tab << "incompatible sizes" << endl;
268           cout << tab << lhs.size << endl;
269           cout << tab << rhs.size << endl;
270         }
271     }
272
273   if (lhs.version_name != rhs.version_name 
274       && !check_version(lhs) && !check_version(rhs))
275     {
276       ret = false;
277       if (verbose)
278         {
279           cout << tab << "incompatible versions" << endl;
280           cout << tab << lhs.version_name << endl;
281           cout << tab << rhs.version_name << endl;
282         }
283     }
284
285   if (verbose)
286     cout << endl;
287
288   return ret;
289 }
290
291
292 bool
293 has_symbol(const string& mangled, const symbols& s) throw()
294 {
295   const symbol_names& names = s.first;
296   symbol_names::const_iterator i = find(names.begin(), names.end(), mangled);
297   return i != names.end();
298 }
299
300 symbol&
301 get_symbol(const string& mangled, const symbols& s)
302 {
303   const symbol_names& names = s.first;
304   symbol_names::const_iterator i = find(names.begin(), names.end(), mangled);
305   if (i != names.end())
306     {
307       symbol_objects objects = s.second;
308       return objects[mangled];
309     }
310   else
311     {
312       ostringstream os;
313       os << "get_symbol failed for symbol " << mangled;
314       __throw_logic_error(os.str().c_str());
315     }
316 }
317
318 void 
319 examine_symbol(const char* name, const char* file)
320 {
321   try
322     {
323       symbols s = create_symbols(file);
324       symbol& sym = get_symbol(name, s);
325       sym.print();
326     }
327   catch(...)
328     { __throw_exception_again; }
329 }
330
331 int
332 compare_symbols(const char* baseline_file, const char* test_file, 
333                 bool verbose)
334 {
335   // Input both lists of symbols into container.
336   symbols baseline = create_symbols(baseline_file);
337   symbols test = create_symbols(test_file);
338   symbol_names& baseline_names = baseline.first;
339   symbol_objects& baseline_objects = baseline.second;
340   symbol_names& test_names = test.first;
341   symbol_objects& test_objects = test.second;
342
343   //  Sanity check results.
344   const symbol_names::size_type baseline_size = baseline_names.size();
345   const symbol_names::size_type test_size = test_names.size();
346   if (!baseline_size || !test_size)
347     {
348       cerr << "Problems parsing the list of exported symbols." << endl;
349       exit(2);
350     }
351
352   // Sort out names.
353   // Assuming baseline_names, test_names are both unique w/ no duplicates.
354   //
355   // The names added to missing_names are baseline_names not found in
356   // test_names 
357   // -> symbols that have been deleted.
358   //
359   // The names added to added_names are test_names not in
360   // baseline_names
361   // -> symbols that have been added.
362   symbol_names shared_names;
363   symbol_names missing_names;
364   symbol_names added_names = test_names;
365   for (size_t i = 0; i < baseline_size; ++i)
366     {
367       string what(baseline_names[i]);
368       symbol_names::iterator end = added_names.end();
369       symbol_names::iterator it = find(added_names.begin(), end, what);
370       if (it != end)
371         {
372           // Found.
373           shared_names.push_back(what);
374           added_names.erase(it);
375         }
376       else
377         missing_names.push_back(what);
378     }
379
380   // Check missing names for compatibility.
381   typedef pair<symbol, symbol> symbol_pair;
382   vector<symbol_pair> incompatible;
383   const symbol_names::size_type missing_size = missing_names.size();
384   for (size_t j = 0; j < missing_size; ++j)
385     {
386       symbol& base = baseline_objects[missing_names[j]];
387       base.status = symbol::subtracted;
388       incompatible.push_back(symbol_pair(base, base));
389     }
390
391   // Check shared names for compatibility.
392   const symbol_names::size_type shared_size = shared_names.size();
393   for (size_t k = 0; k < shared_size; ++k)
394     {
395       symbol& base = baseline_objects[shared_names[k]];
396       symbol& test = test_objects[shared_names[k]];
397       test.status = symbol::existing;
398       if (!check_compatible(base, test))
399         incompatible.push_back(symbol_pair(base, test));
400     }
401
402   // Check added names for compatibility.
403   const symbol_names::size_type added_size = added_names.size();
404   for (size_t l = 0; l < added_size; ++l)
405     {
406       symbol& test = test_objects[added_names[l]];
407       test.status = symbol::added;
408       if (!check_version(test, true))
409         incompatible.push_back(symbol_pair(test, test));
410     }
411
412   // Report results.
413   if (verbose && added_names.size())
414     {
415       cout << endl << added_names.size() << " added symbols " << endl;
416       for (size_t j = 0; j < added_names.size() ; ++j)
417         {
418           cout << j << endl;
419           test_objects[added_names[j]].print();
420         }
421     }
422   
423   if (verbose && missing_names.size())
424     {
425       cout << endl << missing_names.size() << " missing symbols " << endl;
426       for (size_t j = 0; j < missing_names.size() ; ++j)
427         {
428           cout << j << endl;
429           baseline_objects[missing_names[j]].print();
430         }
431     }
432   
433   if (verbose && incompatible.size())
434     {
435       cout << endl << incompatible.size() << " incompatible symbols " << endl;
436       for (size_t j = 0; j < incompatible.size() ; ++j)
437         {
438           // First, print index.
439           cout << j << endl;
440
441           // Second, report name.
442           symbol& base = incompatible[j].first;
443           symbol& test = incompatible[j].second;
444           test.print();
445           
446           // Second, report reason or reasons incompatible.
447           check_compatible(base, test, true);
448         }
449     }
450   
451   cout << "\n\t\t=== libstdc++-v3 check-abi Summary ===" << endl;
452   cout << endl;
453   cout << "# of added symbols:\t\t " << added_names.size() << endl;
454   cout << "# of missing symbols:\t\t " << missing_names.size() << endl;
455   cout << "# of incompatible symbols:\t " << incompatible.size() << endl;
456   cout << endl;
457   cout << "using: " << baseline_file << endl;
458
459   return !(missing_names.size() || incompatible.size());
460 }
461
462
463 symbols
464 create_symbols(const char* file)
465 {
466   symbols s;
467   ifstream ifs(file);
468   if (ifs.is_open())
469     {
470       // Organize file data into container of symbol objects, and a
471       // container of mangled names without versioning information.
472       symbol_names& names = s.first;
473       symbol_objects& objects = s.second;
474       const string empty;
475       string line = empty;
476       while (getline(ifs, line).good())
477         {
478           symbol tmp;
479           tmp.init(line);
480           objects[tmp.name] = tmp;
481           names.push_back(tmp.name);
482           line = empty;
483         }
484     }
485   else
486     {
487       ostringstream os;
488       os << "create_symbols failed for file " << file;
489       __throw_runtime_error(os.str().c_str());
490     }
491   return s;
492 }
493
494
495 const char*
496 demangle(const std::string& mangled)
497 {
498   const char* name;
499   if (mangled[0] != '_' || mangled[1] != 'Z')
500     {
501       // This is not a mangled symbol, thus has "C" linkage.
502       name = mangled.c_str();
503     }
504   else
505     {
506       // Use __cxa_demangle to demangle.
507       int status = 0;
508       name = abi::__cxa_demangle(mangled.c_str(), 0, 0, &status);
509       if (!name)
510         {
511           switch (status)
512             {
513             case 0:
514               name = "error code = 0: success";
515               break;
516             case -1:
517               name = "error code = -1: memory allocation failure";
518               break;
519             case -2:
520               name = "error code = -2: invalid mangled name";
521               break;
522             case -3:
523               name = "error code = -3: invalid arguments";
524               break;
525             default:
526               name = "error code unknown - who knows what happened";
527             }
528         }
529     }
530   return name;
531 }
532