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