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