1 // Copyright (C) 2019 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 "repr/protobuf/ir_diff_dumper.h" 16 17 #include "repr/ir_diff_representation.h" 18 #include "repr/protobuf/api.h" 19 #include "repr/protobuf/converter.h" 20 21 #include <fstream> 22 #include <memory> 23 #include <string> 24 25 #include <llvm/Support/raw_ostream.h> 26 27 #include <google/protobuf/io/zero_copy_stream_impl.h> 28 #include <google/protobuf/text_format.h> 29 30 31 namespace header_checker { 32 namespace repr { 33 34 35 void ProtobufIRDiffDumper::AddLibNameIR(const std::string &name) { 36 diff_tu_->set_lib_name(name); 37 } 38 39 void ProtobufIRDiffDumper::AddArchIR(const std::string &arch) { 40 diff_tu_->set_arch(arch); 41 } 42 43 CompatibilityStatusIR ProtobufIRDiffDumper::GetCompatibilityStatusIR() { 44 if (diff_tu_->functions_removed().size() != 0 || 45 diff_tu_->global_vars_removed().size() != 0 || 46 diff_tu_->function_diffs().size() != 0 || 47 diff_tu_->global_var_diffs().size() != 0 || 48 diff_tu_->enum_type_diffs().size() != 0 || 49 diff_tu_->record_type_diffs().size() != 0) { 50 return CompatibilityStatusIR::Incompatible; 51 } 52 53 CompatibilityStatusIR combined_status = CompatibilityStatusIR::Compatible; 54 55 if (diff_tu_->enum_type_extension_diffs().size() != 0 || 56 diff_tu_->functions_added().size() != 0 || 57 diff_tu_->global_vars_added().size() != 0) { 58 combined_status = combined_status | CompatibilityStatusIR::Extension; 59 } 60 61 if (diff_tu_->unreferenced_enum_type_diffs().size() != 0 || 62 diff_tu_->unreferenced_enum_types_removed().size() != 0 || 63 diff_tu_->unreferenced_record_types_removed().size() != 0 || 64 diff_tu_->unreferenced_record_type_diffs().size() != 0 || 65 diff_tu_->unreferenced_enum_type_extension_diffs().size() != 0 || 66 diff_tu_->unreferenced_record_types_added().size() != 0 || 67 diff_tu_->unreferenced_enum_types_added().size()) { 68 combined_status = 69 combined_status | CompatibilityStatusIR::UnreferencedChanges; 70 } 71 72 if (diff_tu_->removed_elf_functions().size() != 0 || 73 diff_tu_->removed_elf_objects().size() != 0) { 74 combined_status = combined_status | CompatibilityStatusIR::ElfIncompatible; 75 } 76 77 return combined_status; 78 } 79 80 void ProtobufIRDiffDumper::AddCompatibilityStatusIR( 81 CompatibilityStatusIR status) { 82 diff_tu_->set_compatibility_status(CompatibilityStatusIRToProtobuf(status)); 83 } 84 85 bool ProtobufIRDiffDumper::AddDiffMessageIR(const DiffMessageIR *message, 86 const std::string &type_stack, 87 DiffKind diff_kind) { 88 switch (message->Kind()) { 89 case RecordTypeKind: 90 return AddRecordTypeDiffIR( 91 static_cast<const RecordTypeDiffIR *>(message), type_stack, 92 diff_kind); 93 case EnumTypeKind: 94 return AddEnumTypeDiffIR( 95 static_cast<const EnumTypeDiffIR *>(message), type_stack, diff_kind); 96 case GlobalVarKind: 97 return AddGlobalVarDiffIR( 98 static_cast<const GlobalVarDiffIR*>(message), type_stack, diff_kind); 99 case FunctionKind: 100 return AddFunctionDiffIR( 101 static_cast<const FunctionDiffIR*>(message), type_stack, diff_kind); 102 default: 103 break; 104 } 105 llvm::errs() << "Dump Diff attempted on something not a user defined type / " 106 << "function / global variable\n"; 107 return false; 108 } 109 110 bool ProtobufIRDiffDumper::AddLinkableMessageIR( 111 const LinkableMessageIR *message, DiffKind diff_kind) { 112 switch (message->GetKind()) { 113 case RecordTypeKind: 114 return AddLoneRecordTypeDiffIR( 115 static_cast<const RecordTypeIR *>(message), diff_kind); 116 case EnumTypeKind: 117 return AddLoneEnumTypeDiffIR( 118 static_cast<const EnumTypeIR *>(message), diff_kind); 119 case GlobalVarKind: 120 return AddLoneGlobalVarDiffIR( 121 static_cast<const GlobalVarIR*>(message), diff_kind); 122 case FunctionKind: 123 return AddLoneFunctionDiffIR( 124 static_cast<const FunctionIR*>(message), diff_kind); 125 default: 126 break; 127 } 128 llvm::errs() << "Dump Diff attempted on something not a user defined type / " 129 << "function / global variable\n"; 130 return false; 131 } 132 133 bool ProtobufIRDiffDumper::AddElfSymbolMessageIR(const ElfSymbolIR *elf_symbol, 134 DiffKind diff_kind) { 135 switch (elf_symbol->GetKind()) { 136 case ElfSymbolIR::ElfFunctionKind: 137 return AddElfFunctionIR(static_cast<const ElfFunctionIR *>(elf_symbol), 138 diff_kind); 139 break; 140 case ElfSymbolIR::ElfObjectKind: 141 return AddElfObjectIR(static_cast<const ElfObjectIR *>(elf_symbol), 142 diff_kind); 143 break; 144 } 145 // Any other kind is invalid 146 return false; 147 } 148 149 bool ProtobufIRDiffDumper::AddElfFunctionIR( 150 const ElfFunctionIR *elf_function_ir, DiffKind diff_kind) { 151 abi_dump::ElfFunction *added_elf_function = nullptr; 152 switch (diff_kind) { 153 case DiffKind::Removed: 154 added_elf_function = diff_tu_->add_removed_elf_functions(); 155 break; 156 case DiffKind::Added: 157 added_elf_function = diff_tu_->add_added_elf_functions(); 158 break; 159 default: 160 llvm::errs() << "Invalid call to AddElfFunctionIR\n"; 161 return false; 162 } 163 if (added_elf_function == nullptr) { 164 return false; 165 } 166 *added_elf_function = 167 IRToProtobufConverter::ConvertElfFunctionIR(elf_function_ir); 168 return true; 169 } 170 171 bool ProtobufIRDiffDumper::AddElfObjectIR( 172 const ElfObjectIR *elf_object_ir, DiffKind diff_kind) { 173 abi_dump::ElfObject *added_elf_object = nullptr; 174 switch (diff_kind) { 175 case DiffKind::Removed: 176 added_elf_object = diff_tu_->add_removed_elf_objects(); 177 break; 178 case DiffKind::Added: 179 added_elf_object = diff_tu_->add_added_elf_objects(); 180 break; 181 default: 182 llvm::errs() << "Invalid call to AddElfObjectIR\n"; 183 return false; 184 } 185 if (added_elf_object == nullptr) { 186 return false; 187 } 188 *added_elf_object = 189 IRToProtobufConverter::ConvertElfObjectIR(elf_object_ir); 190 return true; 191 } 192 193 bool ProtobufIRDiffDumper::AddLoneRecordTypeDiffIR( 194 const RecordTypeIR *record_type_ir, DiffKind diff_kind) { 195 abi_dump::RecordType *added_record_type = nullptr; 196 switch (diff_kind) { 197 case DiffKind::Removed: 198 // Referenced record types do not get reported as added / removed, 199 // the diff shows up in the parent type / function/ global variable 200 // referencing the record. 201 added_record_type = diff_tu_->add_unreferenced_record_types_removed(); 202 break; 203 case DiffKind::Added: 204 added_record_type = diff_tu_->add_unreferenced_record_types_added(); 205 break; 206 default: 207 llvm::errs() << "Invalid call to AddLoneRecordTypeDiffIR\n"; 208 return false; 209 } 210 if (added_record_type == nullptr) { 211 return false; 212 } 213 *added_record_type = 214 IRToProtobufConverter::ConvertRecordTypeIR(record_type_ir); 215 return true; 216 } 217 218 bool ProtobufIRDiffDumper::AddLoneFunctionDiffIR( 219 const FunctionIR *function_ir, DiffKind diff_kind) { 220 abi_dump::FunctionDecl *added_function = nullptr; 221 switch (diff_kind) { 222 case DiffKind::Removed: 223 added_function = diff_tu_->add_functions_removed(); 224 break; 225 case DiffKind::Added: 226 added_function = diff_tu_->add_functions_added(); 227 break; 228 default: 229 llvm::errs() << "Invalid call to AddLoneFunctionDiffIR\n"; 230 return false; 231 } 232 *added_function = IRToProtobufConverter::ConvertFunctionIR(function_ir); 233 return true; 234 } 235 236 bool ProtobufIRDiffDumper::AddLoneEnumTypeDiffIR( 237 const EnumTypeIR *enum_type_ir, DiffKind diff_kind) { 238 abi_dump::EnumType *added_enum_type = nullptr; 239 switch (diff_kind) { 240 case DiffKind::Removed: 241 // Referenced enum types do not get reported as added / removed, 242 // the diff shows up in the parent type / function/ global variable 243 // referencing the enum. 244 added_enum_type = diff_tu_->add_unreferenced_enum_types_removed(); 245 break; 246 case DiffKind::Added: 247 added_enum_type = diff_tu_->add_unreferenced_enum_types_added(); 248 break; 249 default: 250 llvm::errs() << "Invalid call to AddLoneRecordTypeDiffIR\n"; 251 return false; 252 } 253 if (added_enum_type == nullptr) { 254 return false; 255 } 256 *added_enum_type = IRToProtobufConverter::ConvertEnumTypeIR(enum_type_ir); 257 return true; 258 } 259 260 bool ProtobufIRDiffDumper::AddLoneGlobalVarDiffIR( 261 const GlobalVarIR *global_var_ir, DiffKind diff_kind) { 262 abi_dump::GlobalVarDecl *added_global_var = nullptr; 263 switch (diff_kind) { 264 case DiffKind::Removed: 265 added_global_var = diff_tu_->add_global_vars_removed(); 266 break; 267 case DiffKind::Added: 268 added_global_var = diff_tu_->add_global_vars_added(); 269 break; 270 default: 271 llvm::errs() << "Invalid call to AddLoneFunctionDiffIR\n"; 272 return false; 273 } 274 *added_global_var = IRToProtobufConverter::ConvertGlobalVarIR(global_var_ir); 275 return true; 276 } 277 278 bool ProtobufIRDiffDumper::AddRecordTypeDiffIR( 279 const RecordTypeDiffIR *record_diff_ir, const std::string &type_stack, 280 DiffKind diff_kind) { 281 abi_diff::RecordTypeDiff *added_record_type_diff = nullptr; 282 switch (diff_kind) { 283 case DiffKind::Unreferenced: 284 added_record_type_diff = diff_tu_->add_unreferenced_record_type_diffs(); 285 break; 286 case DiffKind::Referenced: 287 added_record_type_diff = diff_tu_->add_record_type_diffs(); 288 break; 289 default: 290 break; 291 } 292 if (!added_record_type_diff) { 293 return false; 294 } 295 296 *added_record_type_diff = 297 IRDiffToProtobufConverter::ConvertRecordTypeDiffIR(record_diff_ir); 298 added_record_type_diff->set_type_stack(type_stack); 299 return true; 300 } 301 302 bool ProtobufIRDiffDumper::AddFunctionDiffIR( 303 const FunctionDiffIR *function_diff_ir, const std::string &type_stack, 304 DiffKind diff_kind) { 305 abi_diff::FunctionDeclDiff *added_function_diff = 306 diff_tu_->add_function_diffs(); 307 if (!added_function_diff) { 308 return false; 309 } 310 *added_function_diff = 311 IRDiffToProtobufConverter::ConvertFunctionDiffIR(function_diff_ir); 312 return true; 313 } 314 315 bool ProtobufIRDiffDumper::AddEnumTypeDiffIR(const EnumTypeDiffIR *enum_diff_ir, 316 const std::string &type_stack, 317 DiffKind diff_kind) { 318 abi_diff::EnumTypeDiff *added_enum_type_diff = nullptr; 319 switch (diff_kind) { 320 case DiffKind::Unreferenced: 321 if (enum_diff_ir->IsExtended()) { 322 added_enum_type_diff = 323 diff_tu_->add_unreferenced_enum_type_extension_diffs(); 324 } else { 325 added_enum_type_diff = 326 diff_tu_->add_unreferenced_enum_type_diffs(); 327 } 328 break; 329 case DiffKind::Referenced: 330 if (enum_diff_ir->IsExtended()) { 331 added_enum_type_diff = 332 diff_tu_->add_enum_type_extension_diffs(); 333 } else { 334 added_enum_type_diff = 335 diff_tu_->add_enum_type_diffs(); 336 } 337 break; 338 default: 339 break; 340 } 341 if (!added_enum_type_diff) { 342 return false; 343 } 344 *added_enum_type_diff = 345 IRDiffToProtobufConverter::ConvertEnumTypeDiffIR(enum_diff_ir); 346 added_enum_type_diff->set_type_stack(type_stack); 347 return true; 348 } 349 350 bool ProtobufIRDiffDumper::AddGlobalVarDiffIR( 351 const GlobalVarDiffIR *global_var_diff_ir, const std::string &type_stack, 352 DiffKind diff_kind) { 353 abi_diff::GlobalVarDeclDiff *added_global_var_diff = 354 diff_tu_->add_global_var_diffs(); 355 if (!added_global_var_diff) { 356 return false; 357 } 358 *added_global_var_diff = 359 IRDiffToProtobufConverter::ConvertGlobalVarDiffIR(global_var_diff_ir); 360 return true; 361 } 362 363 bool ProtobufIRDiffDumper::Dump() { 364 GOOGLE_PROTOBUF_VERIFY_VERSION; 365 assert(diff_tu_.get() != nullptr); 366 std::ofstream text_output(dump_path_); 367 google::protobuf::io::OstreamOutputStream text_os(&text_output); 368 return google::protobuf::TextFormat::Print(*diff_tu_.get(), &text_os); 369 } 370 371 std::unique_ptr<IRDiffDumper> CreateProtobufIRDiffDumper( 372 const std::string &dump_path) { 373 return std::make_unique<ProtobufIRDiffDumper>(dump_path); 374 } 375 376 377 } // namespace repr 378 } // namespace header_checker 379