OSDN Git Service

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