Home | History | Annotate | Download | only in protobuf
      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