From eb755c1323c0e5a9c9458b7b8dfc183a6e67762f Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Mon, 8 May 2017 15:03:10 -0700 Subject: [PATCH] Merge abi diffs, improve linking performance. Bug: 38325544 Test: As shown by out/build.trace.gz when using built header-abi-linker to link .sdump files produced by header-abi-linker. Without fix: Time taken to produce libc.so.lsdump by header-abi-linker => ~51 sec With fix: Time taken to produce libc.so.lsdump by header-abi-linker => ~3 sec Bug: 38325929 Test: merged abiff reports of libjpeg, libc. Change-Id: Iae54f4754d8b0407fabdb8bc8550a7f7b3479f8f --- vndk/tools/header-checker/Android.bp | 32 ++++ .../header-abi-diff/src/abi_diff.cpp | 48 +++--- .../header-abi-diff/src/abi_diff.h | 47 +++--- .../header-abi-diff/src/abi_diff_wrappers.cpp | 1 + .../header-abi-diff/src/header_abi_diff.cpp | 50 +++--- .../src/header_abi_linker.cpp | 70 +++++--- .../header-abi-util/include/header_abi_util.h | 12 ++ .../src/version_script_parser.cpp | 28 +++- .../merge-abi-diff/src/merge_abi_diff.cpp | 156 ++++++++++++++++++ .../tools/header-checker/proto/abi_diff.proto | 57 +++++-- 10 files changed, 390 insertions(+), 111 deletions(-) create mode 100644 vndk/tools/header-checker/merge-abi-diff/src/merge_abi_diff.cpp diff --git a/vndk/tools/header-checker/Android.bp b/vndk/tools/header-checker/Android.bp index 09280dd74..032e863a8 100644 --- a/vndk/tools/header-checker/Android.bp +++ b/vndk/tools/header-checker/Android.bp @@ -212,6 +212,38 @@ cc_binary_host { }, } +cc_binary_host { + name: "merge-abi-diff", + + defaults: [ + "header-checker-defaults", + "header-abi-linker-lib-defaults", + ], + + srcs: [ + "merge-abi-diff/src/*.cpp", + ], + + static_libs: [ + "libheader-checker-proto", + ], + + target: { + linux: { + host_ldlibs: [ + "-ldl", + "-lpthread", + ], + }, + darwin: { + host_ldlibs: [ + "-ldl", + "-lpthread", + ], + }, + }, +} + cc_library_static { name: "libheader-abi-util", defaults: [ diff --git a/vndk/tools/header-checker/header-abi-diff/src/abi_diff.cpp b/vndk/tools/header-checker/header-abi-diff/src/abi_diff.cpp index ebe782e5c..45aac6416 100644 --- a/vndk/tools/header-checker/header-abi-diff/src/abi_diff.cpp +++ b/vndk/tools/header-checker/header-abi-diff/src/abi_diff.cpp @@ -27,7 +27,7 @@ #include -Status HeaderAbiDiff::GenerateCompatibilityReport() { +CompatibilityStatus HeaderAbiDiff::GenerateCompatibilityReport() { abi_dump::TranslationUnit old_tu; abi_dump::TranslationUnit new_tu; std::ifstream old_input(old_dump_); @@ -37,41 +37,51 @@ Status HeaderAbiDiff::GenerateCompatibilityReport() { if (!google::protobuf::TextFormat::Parse(&text_iso, &old_tu) || !google::protobuf::TextFormat::Parse(&text_isn, &new_tu)) { - llvm::errs() << "Failed to Parse Input\n"; + llvm::errs() << "Failed to generate compatibility report\n"; ::exit(1); } - return CompareTUs(old_tu, new_tu); + return CompareTUs(old_tu, new_tu); } -Status HeaderAbiDiff::CompareTUs(const abi_dump::TranslationUnit &old_tu, - const abi_dump::TranslationUnit &new_tu) { +CompatibilityStatus HeaderAbiDiff::CompareTUs( + const abi_dump::TranslationUnit &old_tu, + const abi_dump::TranslationUnit &new_tu) { std::unique_ptr diff_tu( new abi_diff::TranslationUnitDiff); - - Status record_status = Collect( + CompatibilityStatus record_status = Collect( diff_tu->mutable_records_added(), diff_tu->mutable_records_removed(), diff_tu->mutable_records_diff(), old_tu.records(), new_tu.records(), ignored_symbols_); - Status function_status = Collect( + CompatibilityStatus function_status = Collect( diff_tu->mutable_functions_added(), diff_tu->mutable_functions_removed(), diff_tu->mutable_functions_diff(), old_tu.functions(), new_tu.functions(), ignored_symbols_); - Status enum_status = Collect( + CompatibilityStatus enum_status = Collect( diff_tu->mutable_enums_added(), diff_tu->mutable_enums_removed(), diff_tu->mutable_enums_diff(), old_tu.enums(), new_tu.enums(), ignored_symbols_); - Status global_var_status = Collect( + CompatibilityStatus global_var_status = Collect( diff_tu->mutable_global_vars_added(), diff_tu->mutable_global_vars_removed(), diff_tu->mutable_global_vars_diff(), old_tu.global_vars(), new_tu.global_vars(), ignored_symbols_); - Status combined_status = + CompatibilityStatus combined_status = record_status | function_status | enum_status | global_var_status; + if (combined_status & CompatibilityStatus::INCOMPATIBLE) { + combined_status = CompatibilityStatus::INCOMPATIBLE; + } else if (combined_status & CompatibilityStatus::EXTENSION) { + combined_status = CompatibilityStatus::EXTENSION; + } else { + combined_status = CompatibilityStatus::COMPATIBLE; + } + diff_tu->set_compatibility_status(combined_status); + diff_tu->set_lib_name(lib_name_); + diff_tu->set_arch(arch_); std::ofstream text_output(cr_); google::protobuf::io::OstreamOutputStream text_os(&text_output); @@ -79,17 +89,11 @@ Status HeaderAbiDiff::CompareTUs(const abi_dump::TranslationUnit &old_tu, llvm::errs() << "Unable to dump report\n"; ::exit(1); } - if (combined_status & INCOMPATIBLE) { - return INCOMPATIBLE; - } - if (combined_status & EXTENSION) { - return EXTENSION; - } - return COMPATIBLE; + return combined_status; } template -Status HeaderAbiDiff::Collect( +abi_diff::CompatibilityStatus HeaderAbiDiff::Collect( google::protobuf::RepeatedPtrField *elements_added, google::protobuf::RepeatedPtrField *elements_removed, google::protobuf::RepeatedPtrField *elements_diff, @@ -115,12 +119,12 @@ Status HeaderAbiDiff::Collect( ::exit(1); } if (elements_diff->size() || elements_removed->size()) { - return INCOMPATIBLE; + return CompatibilityStatus::INCOMPATIBLE; } if (elements_added->size()) { - return EXTENSION; + return CompatibilityStatus::EXTENSION; } - return COMPATIBLE; + return CompatibilityStatus::COMPATIBLE; } template diff --git a/vndk/tools/header-checker/header-abi-diff/src/abi_diff.h b/vndk/tools/header-checker/header-abi-diff/src/abi_diff.h index c7ee27284..df8a6d15b 100644 --- a/vndk/tools/header-checker/header-abi-diff/src/abi_diff.h +++ b/vndk/tools/header-checker/header-abi-diff/src/abi_diff.h @@ -28,29 +28,26 @@ #include +typedef abi_diff::CompatibilityStatus CompatibilityStatus; + class HeaderAbiDiff { public: - enum Status { - COMPATIBLE = 1 << 0, - EXTENSION = 1 << 1, - INCOMPATIBLE = 1 << 2, - }; - - - HeaderAbiDiff(const std::string &old_dump, const std::string &new_dump, + HeaderAbiDiff(const std::string &lib_name, const std::string &arch, + const std::string &old_dump, const std::string &new_dump, const std::string &compatibility_report, const std::set &ignored_symbols) - : old_dump_(old_dump), new_dump_(new_dump), cr_(compatibility_report), - ignored_symbols_(ignored_symbols) { } + : lib_name_(lib_name), arch_(arch), old_dump_(old_dump), + new_dump_(new_dump), cr_(compatibility_report), + ignored_symbols_(ignored_symbols) { } - Status GenerateCompatibilityReport(); + CompatibilityStatus GenerateCompatibilityReport(); private: - Status CompareTUs(const abi_dump::TranslationUnit &old_tu, - const abi_dump::TranslationUnit &new_tu); + CompatibilityStatus CompareTUs(const abi_dump::TranslationUnit &old_tu, + const abi_dump::TranslationUnit &new_tu); // Collect* methods fill in the diff_tu. template - static Status Collect( + static CompatibilityStatus Collect( google::protobuf::RepeatedPtrField *elements_added, google::protobuf::RepeatedPtrField *elements_removed, google::protobuf::RepeatedPtrField *elements_diff, @@ -88,14 +85,14 @@ class HeaderAbiDiff { const std::set &ignored_symbols); private: + const std::string &lib_name_; + const std::string &arch_; const std::string &old_dump_; const std::string &new_dump_; const std::string &cr_; const std::set &ignored_symbols_; }; -typedef HeaderAbiDiff::Status Status; - template inline void HeaderAbiDiff::AddToMap( std::map *dst, @@ -105,14 +102,16 @@ inline void HeaderAbiDiff::AddToMap( } } -static inline Status operator|(Status f, Status s) { - return static_cast( - static_cast::type>(f) | - static_cast::type>(s)); +static inline CompatibilityStatus operator|(CompatibilityStatus f, + CompatibilityStatus s) { + return static_cast( + static_cast::type>(f) | + static_cast::type>(s)); } -static inline Status operator&(Status f, Status s) { - return static_cast( - static_cast::type>(f) & - static_cast::type>(s)); +static inline CompatibilityStatus operator&( + CompatibilityStatus f, CompatibilityStatus s) { + return static_cast( + static_cast::type>(f) & + static_cast::type>(s)); } diff --git a/vndk/tools/header-checker/header-abi-diff/src/abi_diff_wrappers.cpp b/vndk/tools/header-checker/header-abi-diff/src/abi_diff_wrappers.cpp index 36fcd355f..b9978a933 100644 --- a/vndk/tools/header-checker/header-abi-diff/src/abi_diff_wrappers.cpp +++ b/vndk/tools/header-checker/header-abi-diff/src/abi_diff_wrappers.cpp @@ -276,6 +276,7 @@ DiffWrapper::Get() { google::protobuf::RepeatedPtrField *fdiffs = enum_diff->mutable_field_diffs(); assert(fdiffs != nullptr); + enum_diff->set_name(oldp_->basic_abi().name()); if (GetElementDiffs(fdiffs, oldp_->enum_fields(), newp_->enum_fields()) || DiffBasicNamedAndTypedDecl( enum_diff->mutable_type_diff()->mutable_old(), diff --git a/vndk/tools/header-checker/header-abi-diff/src/header_abi_diff.cpp b/vndk/tools/header-checker/header-abi-diff/src/header_abi_diff.cpp index 10d56dd5a..50ceb6a20 100644 --- a/vndk/tools/header-checker/header-abi-diff/src/header_abi_diff.cpp +++ b/vndk/tools/header-checker/header-abi-diff/src/header_abi_diff.cpp @@ -25,6 +25,14 @@ static llvm::cl::opt compatibility_report( "o", llvm::cl::desc(""), llvm::cl::Required, llvm::cl::cat(header_checker_category)); +static llvm::cl::opt lib_name( + "lib", llvm::cl::desc(""), llvm::cl::Required, + llvm::cl::cat(header_checker_category)); + +static llvm::cl::opt arch( + "arch", llvm::cl::desc(""), llvm::cl::Required, + llvm::cl::cat(header_checker_category)); + static llvm::cl::opt new_dump( "new", llvm::cl::desc(""), llvm::cl::Required, llvm::cl::cat(header_checker_category)); @@ -33,13 +41,18 @@ static llvm::cl::opt old_dump( "old", llvm::cl::desc(""), llvm::cl::Required, llvm::cl::cat(header_checker_category)); +static llvm::cl::opt ignore_symbol_list( + "ignore-symbols", llvm::cl::desc("ignore symbols"), llvm::cl::Optional, + llvm::cl::cat(header_checker_category)); + static llvm::cl::opt advice_only( "advice-only", llvm::cl::desc("Advisory mode only"), llvm::cl::Optional, llvm::cl::cat(header_checker_category)); -static llvm::cl::opt ignore_symbol_list( - "ignore-symbols", llvm::cl::desc("ignore symbols"), llvm::cl::Optional, - llvm::cl::cat(header_checker_category)); +static llvm::cl::opt allow_extensions( + "allow-extensions", + llvm::cl::desc("Do not return a non zero status on extensions"), + llvm::cl::Optional, llvm::cl::cat(header_checker_category)); static std::set LoadIgnoredSymbols(std::string &symbol_list_path) { std::ifstream symbol_ifstream(symbol_list_path); @@ -58,32 +71,21 @@ static std::set LoadIgnoredSymbols(std::string &symbol_list_path) { int main(int argc, const char **argv) { GOOGLE_PROTOBUF_VERIFY_VERSION; llvm::cl::ParseCommandLineOptions(argc, argv, "header-checker"); - uint8_t extension_or_incompatible = 0; std::set ignored_symbols; if (llvm::sys::fs::exists(ignore_symbol_list)) { ignored_symbols = LoadIgnoredSymbols(ignore_symbol_list); } - HeaderAbiDiff judge(old_dump, new_dump, compatibility_report, + HeaderAbiDiff judge(lib_name, arch, old_dump, new_dump, compatibility_report, ignored_symbols); - switch (judge.GenerateCompatibilityReport()) { - case HeaderAbiDiff::COMPATIBLE: - break; - case HeaderAbiDiff::EXTENSION: - extension_or_incompatible = 1; - break; - default: - extension_or_incompatible = 2; - break; + + CompatibilityStatus status = judge.GenerateCompatibilityReport(); + + if (advice_only) { + return CompatibilityStatus::COMPATIBLE; } - if (extension_or_incompatible) { - llvm::errs() << "******************************************************\n" - << "VNDK Abi Compliance breakage:" - << " Please check compatiblity report at : " - << compatibility_report << "\n" - << "*****************************************************\n"; - if (!advice_only) { - return extension_or_incompatible; - } + + if (allow_extensions && status == CompatibilityStatus::EXTENSION) { + return CompatibilityStatus::COMPATIBLE; } - return 0; + return status; } diff --git a/vndk/tools/header-checker/header-abi-linker/src/header_abi_linker.cpp b/vndk/tools/header-checker/header-abi-linker/src/header_abi_linker.cpp index 399b6581b..850fd913a 100644 --- a/vndk/tools/header-checker/header-abi-linker/src/header_abi_linker.cpp +++ b/vndk/tools/header-checker/header-abi-linker/src/header_abi_linker.cpp @@ -92,6 +92,8 @@ class HeaderAbiLinker { template inline bool LinkDecl(google::protobuf::RepeatedPtrField *dst, std::set *link_set, + std::set *regex_matched_link_set, + const std::regex *vs_regex, const google::protobuf::RepeatedPtrField &src, bool use_version_script); @@ -104,13 +106,18 @@ class HeaderAbiLinker { const std::string &out_dump_name_; const std::string &arch_; const std::string &api_; + // TODO: Add to a map of std::sets instead. std::set exported_headers_; std::set record_decl_set_; std::set function_decl_set_; std::set enum_decl_set_; std::set globvar_decl_set_; - // Version Script regex matched link set. - std::set vs_regex_matched_link_set_; + // Version Script Regex Matching. + std::set functions_regex_matched_set; + std::regex functions_vs_regex_; + // Version Script Regex Matching. + std::set globvars_regex_matched_set; + std::regex globvars_vs_regex_; }; bool HeaderAbiLinker::LinkAndDump() { @@ -164,32 +171,44 @@ static std::string GetSymbol(const abi_dump::GlobalVarDecl &element) { } static bool QueryRegexMatches(std::set *regex_matched_link_set, - const std::set &link_set, + const std::regex *vs_regex, const std::string &symbol) { + assert(regex_matched_link_set != nullptr); + assert(vs_regex != nullptr); if (regex_matched_link_set->find(symbol) != regex_matched_link_set->end()) { return false; } - // Go through each element in link_set, if there is a regex match, add the - // symbol to regex_matched_link_set and return true; - for (auto &®ex_match_str : link_set) { - std::smatch matcher; - std::string regex_match_str_find_glob = - abi_util::FindAndReplace(regex_match_str, "\\*", ".*"); - std::regex match_clause("\\b" + regex_match_str_find_glob + "\\b"); - if (std::regex_search(symbol, matcher, match_clause)) { - regex_matched_link_set->insert(symbol); - return true; - } + if (std::regex_search(symbol, *vs_regex)) { + regex_matched_link_set->insert(symbol); + return true; } return false; } +static std::regex CreateRegexMatchExprFromSet( + const std::set &link_set) { + std::string all_regex_match_str = ""; + std::set::iterator it = link_set.begin(); + while (it != link_set.end()) { + std::string regex_match_str_find_glob = + abi_util::FindAndReplace(*it, "\\*", ".*"); + all_regex_match_str += "(\\b" + regex_match_str_find_glob + "\\b)"; + if (++it != link_set.end()) { + all_regex_match_str += "|"; + } + } + if (all_regex_match_str == "") { + return std::regex(); + } + return std::regex(all_regex_match_str); +} + template inline bool HeaderAbiLinker::LinkDecl( google::protobuf::RepeatedPtrField *dst, std::set *link_set, - const google::protobuf::RepeatedPtrField &src, - bool use_version_script) { + std::set *regex_matched_link_set, const std::regex *vs_regex, + const google::protobuf::RepeatedPtrField &src, bool use_version_script) { assert(dst != nullptr); assert(link_set != nullptr); for (auto &&element : src) { @@ -210,8 +229,7 @@ inline bool HeaderAbiLinker::LinkDecl( std::set::iterator it = link_set->find(element_str); if (it == link_set->end()) { - if (!QueryRegexMatches(&vs_regex_matched_link_set_, *link_set, - element_str)) { + if (!QueryRegexMatches(regex_matched_link_set, vs_regex, element_str)) { continue; } } else { @@ -234,14 +252,15 @@ bool HeaderAbiLinker::LinkRecords(const abi_dump::TranslationUnit &dump_tu, assert(linked_tu != nullptr); // Even if version scripts are available we take in records, since the symbols // in the version script might reference a record exposed by the library. - return LinkDecl(linked_tu->mutable_records(), &record_decl_set_, - dump_tu.records(), false); + return LinkDecl(linked_tu->mutable_records(), &record_decl_set_, nullptr, + nullptr, dump_tu.records(), false); } bool HeaderAbiLinker::LinkFunctions(const abi_dump::TranslationUnit &dump_tu, abi_dump::TranslationUnit *linked_tu) { assert(linked_tu != nullptr); return LinkDecl(linked_tu->mutable_functions(), &function_decl_set_, + &functions_regex_matched_set, &functions_vs_regex_, dump_tu.functions(), (!version_script_.empty())); } @@ -250,14 +269,15 @@ bool HeaderAbiLinker::LinkEnums(const abi_dump::TranslationUnit &dump_tu, assert(linked_tu != nullptr); // Even if version scripts are available we take in records, since the symbols // in the version script might reference an enum exposed by the library. - return LinkDecl(linked_tu->mutable_enums(), &enum_decl_set_, - dump_tu.enums(), false); + return LinkDecl(linked_tu->mutable_enums(), &enum_decl_set_, nullptr, + nullptr, dump_tu.enums(), false); } bool HeaderAbiLinker::LinkGlobalVars(const abi_dump::TranslationUnit &dump_tu, abi_dump::TranslationUnit *linked_tu) { assert(linked_tu != nullptr); return LinkDecl(linked_tu->mutable_global_vars(), &globvar_decl_set_, + &globvars_regex_matched_set, &globvars_vs_regex_, dump_tu.global_vars(), (!version_script.empty())); } @@ -269,6 +289,12 @@ bool HeaderAbiLinker::ParseVersionScriptFiles() { } function_decl_set_ = version_script_parser.GetFunctions(); globvar_decl_set_ = version_script_parser.GetGlobVars(); + std::set function_regexs = + version_script_parser.GetFunctionRegexs(); + std::set globvar_regexs = + version_script_parser.GetGlobVarRegexs(); + functions_vs_regex_ = CreateRegexMatchExprFromSet(function_regexs); + globvars_vs_regex_ = CreateRegexMatchExprFromSet(globvar_regexs); return true; } diff --git a/vndk/tools/header-checker/header-abi-util/include/header_abi_util.h b/vndk/tools/header-checker/header-abi-util/include/header_abi_util.h index 3db87d0ec..9ce7af1c2 100644 --- a/vndk/tools/header-checker/header-abi-util/include/header_abi_util.h +++ b/vndk/tools/header-checker/header-abi-util/include/header_abi_util.h @@ -21,6 +21,7 @@ namespace abi_util { std::set CollectAllExportedHeaders( const std::vector &exported_header_dirs); + class VersionScriptParser { public: @@ -38,6 +39,10 @@ class VersionScriptParser { const std::set &GetGlobVars(); + const std::set &GetFunctionRegexs(); + + const std::set &GetGlobVarRegexs(); + private: bool ParseInnerBlock(std::ifstream &symbol_ifstream); @@ -54,11 +59,18 @@ class VersionScriptParser { int ApiStrToInt(const std::string &api); + void AddToVars(std::string &symbol); + + void AddToFunctions(std::string &symbol); + private: const std::string &version_script_; const std::string &arch_; std::set functions_; std::set globvars_; + // Added to speed up version script parsing and linking. + std::set function_regexs_; + std::set globvar_regexs_; int api_; }; diff --git a/vndk/tools/header-checker/header-abi-util/src/version_script_parser.cpp b/vndk/tools/header-checker/header-abi-util/src/version_script_parser.cpp index 9b0034ed5..4eb4ebd80 100644 --- a/vndk/tools/header-checker/header-abi-util/src/version_script_parser.cpp +++ b/vndk/tools/header-checker/header-abi-util/src/version_script_parser.cpp @@ -102,6 +102,22 @@ bool VersionScriptParser::SymbolExported(const std::string &line, return false; } +void VersionScriptParser::AddToVars(std::string &symbol) { + if (symbol.find("*") != std::string::npos) { + globvar_regexs_.insert(symbol); + } else { + globvars_.insert(symbol); + } +} + +void VersionScriptParser::AddToFunctions(std::string &symbol) { + if (symbol.find("*") != std::string::npos) { + function_regexs_.insert(symbol); + } else { + functions_.insert(symbol); + } +} + bool VersionScriptParser::ParseSymbolLine(const std::string &line) { //The symbol lies before the ; and the tags are after ; std::string::size_type pos = line.find(";"); @@ -115,9 +131,9 @@ bool VersionScriptParser::ParseSymbolLine(const std::string &line) { std::string tags = line.substr(pos + 1); if (SymbolExported(tags, arch_, api_)) { if (StringContains(tags, "var")) { - globvars_.insert(symbol); + AddToVars(symbol); } else { - functions_.insert(symbol); + AddToFunctions(symbol); } } return true; @@ -161,6 +177,14 @@ const std::set &VersionScriptParser::GetGlobVars() { return globvars_; } +const std::set &VersionScriptParser::GetFunctionRegexs() { + return function_regexs_; +} + +const std::set &VersionScriptParser::GetGlobVarRegexs() { + return globvar_regexs_; +} + bool VersionScriptParser::Parse() { std::ifstream symbol_ifstream(version_script_); if (!symbol_ifstream.is_open()) { diff --git a/vndk/tools/header-checker/merge-abi-diff/src/merge_abi_diff.cpp b/vndk/tools/header-checker/merge-abi-diff/src/merge_abi_diff.cpp new file mode 100644 index 000000000..c83fd9fb4 --- /dev/null +++ b/vndk/tools/header-checker/merge-abi-diff/src/merge_abi_diff.cpp @@ -0,0 +1,156 @@ +// Copyright (C) 2016 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-parameter" +#pragma clang diagnostic ignored "-Wnested-anon-types" +#include "proto/abi_dump.pb.h" +#include "proto/abi_diff.pb.h" +#pragma clang diagnostic pop + +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include + +static llvm::cl::OptionCategory merge_abi_diff_category( + "merge-abi-diff options"); + +static llvm::cl::list diff_report_list( + llvm::cl::Positional, llvm::cl::desc(""), llvm::cl::Required, + llvm::cl::cat(merge_abi_diff_category), llvm::cl::OneOrMore); + +static llvm::cl::opt merged_diff_report( + "o", llvm::cl::desc(""), llvm::cl::Required, + llvm::cl::cat(merge_abi_diff_category)); + +static llvm::cl::opt advice_only( + "advice-only", llvm::cl::desc("Advisory mode only"), llvm::cl::Optional, + llvm::cl::cat(merge_abi_diff_category)); + +static llvm::cl::opt do_not_break_on_extensions( + "allow-extensions", + llvm::cl::desc("Do not return a non zero status on extensions"), + llvm::cl::Optional, llvm::cl::cat(merge_abi_diff_category)); + +typedef abi_diff::CompatibilityStatus CompatibilityStatus; + +static bool IsStatusDowngraded(const CompatibilityStatus &old_status, + const CompatibilityStatus &new_status) { + bool status_downgraded = false; + switch (old_status) { + case CompatibilityStatus::EXTENSION: + if (new_status == CompatibilityStatus::INCOMPATIBLE) { + status_downgraded = true; + } + break; + case CompatibilityStatus::COMPATIBLE: + if (new_status != CompatibilityStatus::COMPATIBLE) { + status_downgraded = true; + } + break; + default: + break; + } + return status_downgraded; +} + +static CompatibilityStatus MergeDiffReports( + const std::vector &diff_reports, + const std::string &merged_diff_report) { + + abi_diff::MergedTranslationUnitDiff merged_tu_diff; + std::ofstream text_output(merged_diff_report); + google::protobuf::io::OstreamOutputStream text_os(&text_output); + CompatibilityStatus status = CompatibilityStatus::COMPATIBLE; + + for (auto &&i : diff_reports) { + abi_diff::TranslationUnitDiff diff_tu; + std::ifstream input(i); + google::protobuf::io::IstreamInputStream text_is(&input); + if (!google::protobuf::TextFormat::Parse(&text_is, &diff_tu)) { + llvm::errs() << "Failed to parse diff report\n"; + ::exit(1); + } + abi_diff::ConciseDiffReportInformation *added_tu_diff = + merged_tu_diff.add_diff_reports(); + if (!added_tu_diff) { + llvm::errs() << "Failed to add diff report to merged report\n"; + ::exit(1); + } + CompatibilityStatus new_status = diff_tu.compatibility_status(); + added_tu_diff->set_lib_name(diff_tu.lib_name()); + added_tu_diff->set_arch(diff_tu.arch()); + added_tu_diff->set_diff_report_path(i); + added_tu_diff->set_compatibility_status(new_status); + // Only, if the status is downgraded, change it. + if (IsStatusDowngraded(status, new_status)) { + status = new_status; + } + } + + if (!google::protobuf::TextFormat::Print(merged_tu_diff, &text_os)) { + llvm::errs() << "Serialization to ostream failed\n"; + ::exit(1); + } + return status; +} + +int main(int argc, const char **argv) { + GOOGLE_PROTOBUF_VERIFY_VERSION; + llvm::cl::ParseCommandLineOptions(argc, argv, "merge-abi-diff"); + CompatibilityStatus extension_or_incompatible = + MergeDiffReports(diff_report_list, merged_diff_report); + std::string status_str = ""; + switch (extension_or_incompatible) { + case CompatibilityStatus::INCOMPATIBLE: + status_str = "broken"; + break; + case CompatibilityStatus::EXTENSION: + status_str = "extended"; + break; + default: + break; + } + if (extension_or_incompatible) { + llvm::errs() << "******************************************************\n" + << "VNDK Abi " + << status_str + << ":" + << " Please check compatiblity report at : " + << merged_diff_report << "\n" + << "*****************************************************\n"; + } + + if (do_not_break_on_extensions && + extension_or_incompatible == CompatibilityStatus::EXTENSION) { + extension_or_incompatible = CompatibilityStatus::COMPATIBLE; + } + + if (!advice_only) { + return extension_or_incompatible; + } + return CompatibilityStatus::COMPATIBLE; +} diff --git a/vndk/tools/header-checker/proto/abi_diff.proto b/vndk/tools/header-checker/proto/abi_diff.proto index ababd34f1..a31f4298a 100644 --- a/vndk/tools/header-checker/proto/abi_diff.proto +++ b/vndk/tools/header-checker/proto/abi_diff.proto @@ -59,7 +59,7 @@ message ParamDeclDiff { } message FunctionDeclDiff { - required ReturnTypeDiff return_type_diffs = 1; + optional ReturnTypeDiff return_type_diffs = 1; repeated ParamDeclDiff param_diffs = 2; } @@ -67,20 +67,43 @@ message GlobalVarDeclDiff { optional BasicNamedAndTypedDeclDiff type_diff = 1; } -message TranslationUnitDiff { - // Differing Elements. - repeated RecordDeclDiff records_diff = 1; - repeated EnumDeclDiff enums_diff = 2; - repeated FunctionDeclDiff functions_diff = 3; - repeated GlobalVarDeclDiff global_vars_diff = 4; - // Removed Elements. - repeated abi_dump.RecordDecl records_removed = 5; - repeated abi_dump.FunctionDecl functions_removed = 6; - repeated abi_dump.EnumDecl enums_removed = 7; - repeated abi_dump.GlobalVarDecl global_vars_removed = 8; - // Added Elements. - repeated abi_dump.RecordDecl records_added = 9; - repeated abi_dump.FunctionDecl functions_added = 10; - repeated abi_dump.EnumDecl enums_added = 11; - repeated abi_dump.GlobalVarDecl global_vars_added = 12; +enum CompatibilityStatus { + COMPATIBLE = 0; + EXTENSION = 1; + INCOMPATIBLE = 4; +} + +message TranslationUnitDiff { + // Library Name + optional string lib_name = 1; + optional string arch = 2; + // Differing Elements. + repeated RecordDeclDiff records_diff = 3; + repeated EnumDeclDiff enums_diff = 4; + repeated FunctionDeclDiff functions_diff = 5; + repeated GlobalVarDeclDiff global_vars_diff = 6; + // Removed Elements. + repeated abi_dump.RecordDecl records_removed = 7; + repeated abi_dump.FunctionDecl functions_removed = 8; + repeated abi_dump.EnumDecl enums_removed = 9; + repeated abi_dump.GlobalVarDecl global_vars_removed = 10; + // Added Elements. + repeated abi_dump.RecordDecl records_added = 11; + repeated abi_dump.FunctionDecl functions_added = 12; + repeated abi_dump.EnumDecl enums_added = 13; + repeated abi_dump.GlobalVarDecl global_vars_added = 14; + // Compatiblity Status + optional CompatibilityStatus compatibility_status = 15; +} + +// Not merged with TranslationUnitDiff to allow future extensions. +message ConciseDiffReportInformation { + optional string lib_name = 1; + optional string arch = 2; + optional string diff_report_path = 3; + optional CompatibilityStatus compatibility_status = 4; +} + +message MergedTranslationUnitDiff { + repeated ConciseDiffReportInformation diff_reports = 1; }