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 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 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> 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> 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> 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> 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> 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