From 8fe4a1577aa5bad422dd3f572cd2eedd4bd3a463 Mon Sep 17 00:00:00 2001 From: Jayant Chowdhary Date: Fri, 13 Jan 2017 15:41:48 -0800 Subject: [PATCH] Added libprotobuf to extract and dump ABI. Dump textual presentation of the ABI as well for ease of review. Test: header-abi-dumper -o example1.dump tests/example1.h -- clang -x c++ -I . -std=c++11. Change-Id: I65d90d1ec834d7c30cde4eb389862fd38a4e19e9 --- vndk/tools/header-checker/Android.bp | 58 ++++--- .../header-abi-dumper/src/ast_processing.cpp | 143 ++++++++++++++++-- .../header-abi-dumper/src/ast_processing.h | 65 +++++++- .../header-abi-dumper/src/frontend_action.cpp | 13 +- .../header-abi-dumper/src/header_checker.cpp | 1 + .../tools/header-checker/proto/abi_dump.proto | 37 +++++ 6 files changed, 271 insertions(+), 46 deletions(-) create mode 100644 vndk/tools/header-checker/proto/abi_dump.proto diff --git a/vndk/tools/header-checker/Android.bp b/vndk/tools/header-checker/Android.bp index 8a2d3629e..de04fdbae 100644 --- a/vndk/tools/header-checker/Android.bp +++ b/vndk/tools/header-checker/Android.bp @@ -25,7 +25,14 @@ cc_defaults { "-Wall", "-Werror", "-std=c++11", + "-DGOOGLE_PROTOBUF_NO_RTTI", ], + + target: { + windows: { + enabled: false + } + } } cc_defaults { @@ -34,6 +41,7 @@ cc_defaults { shared_libs: [ "libclang", "libLLVM", + "libprotobuf-cpp-full", ], } @@ -42,6 +50,7 @@ cc_defaults { static_libs: [ "libclangTooling", + "libclangToolingCore", "libclangFrontendTool", "libclangFrontend", "libclangDriver", @@ -74,6 +83,30 @@ cc_defaults { "libLLVMMCDisassembler", "libLLVMSupport", ], + + shared_libs: [ + "libprotobuf-cpp-full", + ], +} + +cc_library_static { + name: "libheader-checker-proto", + host_supported: true, + export_include_dirs: ["."], + + srcs: [ + "proto/abi_dump.proto", + ], + + proto: { + export_proto_headers: true, + }, + + cflags: [ + "-Wcast-qual", + "-Wno-long-long", + "-Wno-unused-parameter", + ], } cc_binary_host { @@ -84,20 +117,15 @@ cc_binary_host { "header-checker-lib-debug-defaults", ], - srcs: ["header-abi-dumper/src/*.cpp"], + srcs: [ + "header-abi-dumper/src/*.cpp", + ], + + static_libs: [ + "libheader-checker-proto", + ], target: { - windows: { - host_ldlibs: [ - "-limagehlp", - "-lole32", - "-lversion", - ], - cflags: [ - // Skip missing-field-initializer warnings for mingw. - "-Wno-error=missing-field-initializers", - ], - }, linux: { host_ldlibs: [ "-ldl", @@ -111,10 +139,4 @@ cc_binary_host { ], }, }, - - product_variables: { - unbundled_build: { - enabled: false, - }, - }, } diff --git a/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.cpp b/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.cpp index 19750e3f6..fd5d98aa9 100644 --- a/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.cpp +++ b/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.cpp @@ -1,37 +1,154 @@ +// 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. + #include "ast_processing.h" -bool HeaderASTVisitor::VisitRecordDecl(const clang::RecordDecl *decl) { - llvm::errs() << "struct: " << decl->getName() << "\n"; - return true; -} +#include +#include -bool HeaderASTVisitor::VisitCXXRecordDecl(const clang::CXXRecordDecl *decl) { - llvm::errs() << "class: " << decl->getName() << "\n"; +#include + +#include +#include +#include + +HeaderASTVisitor::HeaderASTVisitor( + abi_dump::TranslationUnit *tu_ptr, + clang::MangleContext *mangle_contextp, + const clang::ASTContext *ast_contextp, + const clang::CompilerInstance *compiler_instance_p) + : tu_ptr_(tu_ptr), + mangle_contextp_(mangle_contextp), + ast_contextp_(ast_contextp), + cip_(compiler_instance_p) { } + +bool HeaderASTVisitor::VisitRecordDecl(const clang::RecordDecl *decl) { + abi_dump::RecordDecl *record_decl = tu_ptr_->add_classes(); + SetupClassFields(record_decl, decl); return true; } bool HeaderASTVisitor::VisitFunctionDecl(const clang::FunctionDecl *decl) { - llvm::errs() << "func: " << decl->getName() << "\n"; + abi_dump::FunctionDecl *function_decl = tu_ptr_->add_functions(); + // FIXME: Use return value. + SetupFunction(function_decl, decl); return true; } +HeaderASTConsumer::HeaderASTConsumer( + const std::string &file_name, + clang::CompilerInstance *compiler_instancep, + const std::string &out_dump_name) + : file_name_(file_name), + cip_(compiler_instancep), + out_dump_name_(out_dump_name) { } + void HeaderASTConsumer::HandleTranslationUnit(clang::ASTContext &ctx) { - llvm::errs() << "HandleTranslationUnit ------------------------------\n"; clang::TranslationUnitDecl* translation_unit = ctx.getTranslationUnitDecl(); - HeaderASTVisitor v; + std::unique_ptr mangle_contextp( + ctx.createMangleContext()); + abi_dump::TranslationUnit tu; + HeaderASTVisitor v(&tu, mangle_contextp.get(), &ctx, cip_); v.TraverseDecl(translation_unit); + std::ofstream text_output(out_dump_name_ + ".txt"); + std::fstream binary_output( + (out_dump_name_).c_str(), + std::ios::out | std::ios::trunc | std::ios::binary); + std::string str_out; + google::protobuf::TextFormat::PrintToString(tu, &str_out); + text_output << str_out; + if (!tu.SerializeToOstream(&binary_output)) { + llvm::errs() << "Serialization to ostream failed\n"; + } } void HeaderASTConsumer::HandleVTable(clang::CXXRecordDecl *crd) { llvm::errs() << "HandleVTable: " << crd->getName() << "\n"; } -llvm::StringRef HeaderASTPPCallbacks::ToString(const clang::Token &tok) { - return tok.getIdentifierInfo()->getName(); +std::string HeaderASTVisitor::GetDeclSourceFile(const clang::NamedDecl *decl) { + clang::SourceManager &SM = cip_->getSourceManager(); + clang::SourceLocation location = decl->getLocation(); + llvm::StringRef file_name= SM.getFilename(location); + return file_name.str(); +} + +std::string HeaderASTVisitor::GetMangledNameDecl(const clang::NamedDecl *decl) { + std::string mangled_or_demangled_name = decl->getName(); + if (mangle_contextp_->shouldMangleDeclName(decl)) { + llvm::raw_string_ostream ostream(mangled_or_demangled_name); + mangle_contextp_->mangleName(decl, ostream); + ostream.flush(); + } + return mangled_or_demangled_name; +} + +bool HeaderASTVisitor::SetupFunction(abi_dump::FunctionDecl *functionp, + const clang::FunctionDecl *decl) { + // Go through all the parameters in the method and add them to the fields. + // Also get the fully qualfied name and mangled name and store them. + functionp->set_function_name(decl->getQualifiedNameAsString()); + functionp->set_mangled_function_name(GetMangledNameDecl(decl)); + functionp->set_source_file(GetDeclSourceFile(decl)); + clang::QualType return_type = + decl->getReturnType().getDesugaredType(*ast_contextp_); + functionp->set_return_type( + clang::TypeName::getFullyQualifiedName(return_type, *ast_contextp_)); + clang::FunctionDecl::param_const_iterator param_it = decl->param_begin(); + while (param_it != decl->param_end()) { + abi_dump::FieldDecl *function_fieldp = functionp->add_parameters(); + if (!function_fieldp) { + llvm::errs() << "Couldn't add parameter to method. Aborting"; + return false; + } + function_fieldp->set_field_name((*param_it)->getName()); + clang::QualType field_type = + (*param_it)->getType().getDesugaredType(*ast_contextp_); + + function_fieldp->set_field_type( + clang::TypeName::getFullyQualifiedName(field_type, *ast_contextp_)); + + param_it++; + } + return true; +} + +bool HeaderASTVisitor::SetupClassFields(abi_dump::RecordDecl *classp, + const clang::RecordDecl *decl) { + classp->set_fully_qualified_name(decl->getQualifiedNameAsString()); + classp->set_source_file(GetDeclSourceFile(decl)); + classp->set_entity_type("class"); + clang::RecordDecl::field_iterator field = decl->field_begin(); + while (field != decl->field_end()) { + abi_dump::FieldDecl *class_fieldp = classp->add_fields(); + if (!class_fieldp) { + llvm::errs() << " Couldn't add class field: " << field->getName() + << " to reference dump\n"; + return false; + } + class_fieldp->set_field_name(field->getName()); + //FIXME: This needs to change. Resolve typedef, class name, built-in etc. + clang::QualType field_type = + field->getType().getDesugaredType(*ast_contextp_); + class_fieldp->set_field_type( + clang::TypeName::getFullyQualifiedName(field_type, *ast_contextp_)); + field++; + } + return true; } void HeaderASTPPCallbacks::MacroDefined(const clang::Token ¯o_name_tok, - const clang::MacroDirective *) { + const clang::MacroDirective *) { assert(macro_name_tok.isAnyIdentifier()); - llvm::errs() << "defines: " << ToString(macro_name_tok) << "\n"; } diff --git a/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.h b/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.h index 4aaf77da2..fc1940ae4 100644 --- a/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.h +++ b/vndk/tools/header-checker/header-abi-dumper/src/ast_processing.h @@ -1,28 +1,83 @@ +// 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. + +#ifndef AST_PROCESSING_H_ +#define AST_PROCESSING_H_ + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-parameter" +#pragma clang diagnostic ignored "-Wnested-anon-types" +#include "proto/abi_dump.pb.h" +#pragma clang diagnostic pop + #include #include +#include #include +#include #include -#include class HeaderASTVisitor : public clang::RecursiveASTVisitor { public: + HeaderASTVisitor(abi_dump::TranslationUnit *tu_ptr, + clang::MangleContext *mangle_contextp, + const clang::ASTContext *ast_contextp, + const clang::CompilerInstance *compiler_instance_p); + bool VisitRecordDecl(const clang::RecordDecl *decl); - bool VisitCXXRecordDecl(const clang::CXXRecordDecl *decl); + bool VisitFunctionDecl(const clang::FunctionDecl *decl); + + private: + bool SetupFunction(abi_dump::FunctionDecl *methodp, + const clang::FunctionDecl *decl); + + bool SetupClassFields(abi_dump::RecordDecl *classp, + const clang::RecordDecl *decl); + + std::string GetDeclSourceFile(const clang::NamedDecl *decl); + + std::string GetMangledNameDecl(const clang::NamedDecl *decl); + + private: + abi_dump::TranslationUnit *tu_ptr_; + clang::MangleContext *mangle_contextp_; + const clang::ASTContext *ast_contextp_; + const clang::CompilerInstance *cip_; }; class HeaderASTConsumer : public clang::ASTConsumer { public: + HeaderASTConsumer(const std::string &file_name, + clang::CompilerInstance *compiler_instancep, + const std::string &out_dump_name); + void HandleTranslationUnit(clang::ASTContext &ctx) override; + void HandleVTable(clang::CXXRecordDecl *crd) override; + + private: + std::string file_name_; + clang::CompilerInstance *cip_; + std::string out_dump_name_; }; class HeaderASTPPCallbacks : public clang::PPCallbacks { - private: - llvm::StringRef ToString(const clang::Token &tok); - public: void MacroDefined(const clang::Token ¯o_name_tok, const clang::MacroDirective *) override; }; + +#endif // AST_PROCESSING_H_ diff --git a/vndk/tools/header-checker/header-abi-dumper/src/frontend_action.cpp b/vndk/tools/header-checker/header-abi-dumper/src/frontend_action.cpp index 48bd89b5e..e004fa23e 100644 --- a/vndk/tools/header-checker/header-abi-dumper/src/frontend_action.cpp +++ b/vndk/tools/header-checker/header-abi-dumper/src/frontend_action.cpp @@ -13,16 +13,13 @@ // limitations under the License. #include "frontend_action.h" + #include "ast_processing.h" -#include #include #include -#include -#include -#include +#include #include -#include #include #include @@ -39,9 +36,5 @@ HeaderCheckerFrontendAction::CreateASTConsumer(clang::CompilerInstance &ci, pp.addPPCallbacks(llvm::make_unique()); // Create AST consumers. - std::vector> consumers; - consumers.push_back(llvm::make_unique()); - // Still have a MultiplexConsumer in case other consumers need to be - // added later. - return llvm::make_unique(std::move(consumers)); + return llvm::make_unique(header_file, &ci, dump_name_); } diff --git a/vndk/tools/header-checker/header-abi-dumper/src/header_checker.cpp b/vndk/tools/header-checker/header-abi-dumper/src/header_checker.cpp index 570cc7cce..dae76f6c4 100644 --- a/vndk/tools/header-checker/header-abi-dumper/src/header_checker.cpp +++ b/vndk/tools/header-checker/header-abi-dumper/src/header_checker.cpp @@ -24,6 +24,7 @@ #include #include +#include #include diff --git a/vndk/tools/header-checker/proto/abi_dump.proto b/vndk/tools/header-checker/proto/abi_dump.proto new file mode 100644 index 000000000..a18d6b60f --- /dev/null +++ b/vndk/tools/header-checker/proto/abi_dump.proto @@ -0,0 +1,37 @@ +syntax = "proto2"; + +package abi_dump; + +message FunctionDecl { + // Fully Qualified Name. + required string function_name = 1 [default = "NONE"]; + + // Mangled name. + required string mangled_function_name = 2 [default = "NONE"]; + + required string source_file = 3; + required string parent_name = 4 [default = "NONE"]; + repeated string template_arguments = 5 ; + repeated FieldDecl parameters = 6; + required string return_type = 7 [default = "VOID"]; +} + +message FieldDecl { + required string field_name = 1 [default = "NONE"]; + required string field_type = 2 [default = "VOID"]; +} + +message RecordDecl { + repeated FieldDecl fields = 2; + repeated string inner_classes = 3; + repeated string base_classes = 4; + required string fully_qualified_name = 5 [default = "NONE"]; + required int64 id = 6 [default = 0]; + required string entity_type = 7 [default = "NONE"]; + required string source_file = 9; +} + +message TranslationUnit { + repeated RecordDecl classes = 1; + repeated FunctionDecl functions = 2; +}