• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2016 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "abi_diff.h"
16 
17 #include <llvm/Support/raw_ostream.h>
18 
19 #include <google/protobuf/text_format.h>
20 #include <google/protobuf/io/zero_copy_stream_impl.h>
21 
22 #include <memory>
23 #include <fstream>
24 #include <iostream>
25 #include <string>
26 #include <vector>
27 
28 #include <stdlib.h>
29 
GenerateCompatibilityReport()30 CompatibilityStatus HeaderAbiDiff::GenerateCompatibilityReport() {
31   abi_dump::TranslationUnit old_tu;
32   abi_dump::TranslationUnit new_tu;
33   std::ifstream old_input(old_dump_);
34   std::ifstream new_input(new_dump_);
35   google::protobuf::io::IstreamInputStream text_iso(&old_input);
36   google::protobuf::io::IstreamInputStream text_isn(&new_input);
37 
38   if (!google::protobuf::TextFormat::Parse(&text_iso, &old_tu) ||
39       !google::protobuf::TextFormat::Parse(&text_isn, &new_tu)) {
40     llvm::errs() << "Failed to generate compatibility report\n";
41     ::exit(1);
42   }
43   return CompareTUs(old_tu, new_tu);
44 }
45 
CompareTUs(const abi_dump::TranslationUnit & old_tu,const abi_dump::TranslationUnit & new_tu)46 CompatibilityStatus HeaderAbiDiff::CompareTUs(
47     const abi_dump::TranslationUnit &old_tu,
48     const abi_dump::TranslationUnit &new_tu) {
49   std::unique_ptr<abi_diff::TranslationUnitDiff> diff_tu(
50       new abi_diff::TranslationUnitDiff);
51   CompatibilityStatus record_status = Collect<abi_dump::RecordDecl>(
52       diff_tu->mutable_records_added(), diff_tu->mutable_records_removed(),
53       diff_tu->mutable_records_diff(), old_tu.records(), new_tu.records(),
54       ignored_symbols_);
55 
56   CompatibilityStatus function_status = Collect<abi_dump::FunctionDecl>(
57       diff_tu->mutable_functions_added(), diff_tu->mutable_functions_removed(),
58       diff_tu->mutable_functions_diff(), old_tu.functions(),
59       new_tu.functions(), ignored_symbols_);
60 
61   CompatibilityStatus enum_status = Collect<abi_dump::EnumDecl>(
62       diff_tu->mutable_enums_added(), diff_tu->mutable_enums_removed(),
63       diff_tu->mutable_enums_diff(), old_tu.enums(), new_tu.enums(),
64       ignored_symbols_);
65 
66   CompatibilityStatus global_var_status = Collect<abi_dump::GlobalVarDecl>(
67       diff_tu->mutable_global_vars_added(),
68       diff_tu->mutable_global_vars_removed(),
69       diff_tu->mutable_global_vars_diff(), old_tu.global_vars(),
70       new_tu.global_vars(), ignored_symbols_);
71 
72   CompatibilityStatus combined_status =
73       record_status | function_status | enum_status | global_var_status;
74 
75   if (combined_status & CompatibilityStatus::INCOMPATIBLE) {
76     combined_status = CompatibilityStatus::INCOMPATIBLE;
77   } else if (combined_status & CompatibilityStatus::EXTENSION) {
78     combined_status = CompatibilityStatus::EXTENSION;
79   } else {
80     combined_status = CompatibilityStatus::COMPATIBLE;
81   }
82   diff_tu->set_compatibility_status(combined_status);
83   diff_tu->set_lib_name(lib_name_);
84   diff_tu->set_arch(arch_);
85   std::ofstream text_output(cr_);
86   google::protobuf::io::OstreamOutputStream text_os(&text_output);
87 
88   if(!google::protobuf::TextFormat::Print(*diff_tu, &text_os)) {
89     llvm::errs() << "Unable to dump report\n";
90     ::exit(1);
91   }
92   return combined_status;
93 }
94 
95 template <typename T, typename TDiff>
Collect(google::protobuf::RepeatedPtrField<T> * elements_added,google::protobuf::RepeatedPtrField<T> * elements_removed,google::protobuf::RepeatedPtrField<TDiff> * elements_diff,const google::protobuf::RepeatedPtrField<T> & old_srcs,const google::protobuf::RepeatedPtrField<T> & new_srcs,const std::set<std::string> & ignored_symbols)96 abi_diff::CompatibilityStatus HeaderAbiDiff::Collect(
97     google::protobuf::RepeatedPtrField<T> *elements_added,
98     google::protobuf::RepeatedPtrField<T> *elements_removed,
99     google::protobuf::RepeatedPtrField<TDiff> *elements_diff,
100     const google::protobuf::RepeatedPtrField<T> &old_srcs,
101     const google::protobuf::RepeatedPtrField<T> &new_srcs,
102     const std::set<std::string> &ignored_symbols) {
103   assert(elements_added != nullptr);
104   assert(elements_removed != nullptr);
105   assert(elements_diff != nullptr);
106 
107   std::map<std::string, const T*> old_elements_map;
108   std::map<std::string, const T*> new_elements_map;
109   AddToMap(&old_elements_map, old_srcs);
110   AddToMap(&new_elements_map, new_srcs);
111 
112   if (!PopulateRemovedElements(elements_removed, old_elements_map,
113                                new_elements_map, ignored_symbols) ||
114       !PopulateRemovedElements(elements_added, new_elements_map,
115                                old_elements_map, ignored_symbols) ||
116       !PopulateCommonElements(elements_diff, old_elements_map,
117                               new_elements_map, ignored_symbols)) {
118     llvm::errs() << "Populating functions in report failed\n";
119     ::exit(1);
120   }
121   if (elements_diff->size() || elements_removed->size()) {
122     return CompatibilityStatus::INCOMPATIBLE;
123   }
124   if (elements_added->size()) {
125     return CompatibilityStatus::EXTENSION;
126   }
127   return CompatibilityStatus::COMPATIBLE;
128 }
129 
130 template <typename T>
PopulateRemovedElements(google::protobuf::RepeatedPtrField<T> * dst,const std::map<std::string,const T * > & old_elements_map,const std::map<std::string,const T * > & new_elements_map,const std::set<std::string> & ignored_symbols)131 bool HeaderAbiDiff::PopulateRemovedElements(
132     google::protobuf::RepeatedPtrField<T> *dst,
133     const std::map<std::string, const T*> &old_elements_map,
134     const std::map<std::string, const T*> &new_elements_map,
135     const std::set<std::string> &ignored_symbols) {
136 
137   std::vector<const T *> removed_elements;
138   for (auto &&map_element : old_elements_map) {
139       const T *element = map_element.second;
140       auto new_element =
141           new_elements_map.find(element->basic_abi().linker_set_key());
142       if (new_element == new_elements_map.end()) {
143         removed_elements.emplace_back(element);
144       }
145   }
146   if (!DumpLoneElements(dst, removed_elements, ignored_symbols)) {
147     llvm::errs() << "Dumping added / removed element to report failed\n";
148     return false;
149   }
150   return true;
151 }
152 
153 template <typename T, typename TDiff>
PopulateCommonElements(google::protobuf::RepeatedPtrField<TDiff> * dst,const std::map<std::string,const T * > & old_elements_map,const std::map<std::string,const T * > & new_elements_map,const std::set<std::string> & ignored_symbols)154 bool HeaderAbiDiff::PopulateCommonElements(
155     google::protobuf::RepeatedPtrField<TDiff> *dst,
156     const std::map<std::string, const T *> &old_elements_map,
157     const std::map<std::string, const T *> &new_elements_map,
158     const std::set<std::string> &ignored_symbols) {
159   std::vector<std::pair<const T *, const T *>> common_elements;
160   typename std::map<std::string, const T *>::const_iterator old_element =
161       old_elements_map.begin();
162   typename std::map<std::string, const T *>::const_iterator new_element =
163       new_elements_map.begin();
164   while (old_element != old_elements_map.end() &&
165          new_element != new_elements_map.end()) {
166     if (old_element->first == new_element->first) {
167       common_elements.emplace_back(std::make_pair(
168           old_element->second, new_element->second));
169       old_element++;
170       new_element++;
171       continue;
172     }
173     if (old_element->first < new_element->first) {
174       old_element++;
175     } else {
176       new_element++;
177     }
178   }
179   if (!DumpDiffElements(dst, common_elements, ignored_symbols)) {
180     llvm::errs() << "Dumping difference in common element to report failed\n";
181     return false;
182   }
183   return true;
184 }
185 
186 template <typename T>
DumpLoneElements(google::protobuf::RepeatedPtrField<T> * dst,std::vector<const T * > & elements,const std::set<std::string> & ignored_symbols)187 bool HeaderAbiDiff::DumpLoneElements(
188     google::protobuf::RepeatedPtrField<T> *dst,
189     std::vector<const T *> &elements,
190     const std::set<std::string> &ignored_symbols) {
191   for (auto &&element : elements) {
192     if (abi_diff_wrappers::IgnoreSymbol<T>(element, ignored_symbols)) {
193       continue;
194     }
195     T *added_element = dst->Add();
196     if (!added_element) {
197       llvm::errs() << "Adding element diff failed\n";
198       return false;
199     }
200     *added_element = *element;
201   }
202   return true;
203 }
204 
205 template <typename T, typename TDiff>
DumpDiffElements(google::protobuf::RepeatedPtrField<TDiff> * dst,std::vector<std::pair<const T *,const T * >> & pairs,const std::set<std::string> & ignored_symbols)206 bool HeaderAbiDiff::DumpDiffElements(
207     google::protobuf::RepeatedPtrField<TDiff>  *dst,
208     std::vector<std::pair<const T *,const T *>> &pairs,
209     const std::set<std::string> &ignored_symbols) {
210   for (auto &&pair : pairs) {
211     const T *old_element = pair.first;
212     const T *new_element = pair.second;
213     // Not having inheritance from protobuf messages makes this
214     // restrictive code.
215     if (abi_diff_wrappers::IgnoreSymbol<T>(old_element, ignored_symbols)) {
216       continue;
217     }
218     abi_diff_wrappers::DiffWrapper<T, TDiff> diff_wrapper(old_element,
219                                                           new_element);
220     std::unique_ptr<TDiff> decl_diff_ptr = diff_wrapper.Get();
221     if (!decl_diff_ptr) {
222       continue;
223     }
224     TDiff *added_element_diff = dst->Add();
225     if (!added_element_diff) {
226       llvm::errs() << "Adding element diff failed\n";
227       return false;
228     }
229     *added_element_diff = *decl_diff_ptr;
230   }
231   return true;
232 }
233