Path: blob/main/contrib/llvm-project/lldb/tools/lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp
213799 views
//===-- lldb-rpc-gen.cpp ----------------------------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "RPCCommon.h"9#include "RPCServerHeaderEmitter.h"10#include "RPCServerSourceEmitter.h"1112#include "clang/AST/AST.h"13#include "clang/AST/ASTConsumer.h"14#include "clang/AST/ASTContext.h"15#include "clang/AST/RecursiveASTVisitor.h"16#include "clang/Basic/SourceManager.h"17#include "clang/CodeGen/ObjectFilePCHContainerWriter.h"18#include "clang/Frontend/CompilerInstance.h"19#include "clang/Frontend/FrontendAction.h"20#include "clang/Frontend/FrontendActions.h"21#include "clang/Serialization/ObjectFilePCHContainerReader.h"22#include "clang/Tooling/CommonOptionsParser.h"23#include "clang/Tooling/Tooling.h"2425#include "llvm/ADT/StringRef.h"26#include "llvm/Support/CommandLine.h"27#include "llvm/Support/Path.h"28#include "llvm/Support/ToolOutputFile.h"29#include "llvm/Support/raw_ostream.h"3031using namespace clang;32using namespace clang::driver;33using namespace clang::tooling;3435static llvm::cl::OptionCategory RPCGenCategory("Tool for generating LLDBRPC");3637static llvm::cl::opt<std::string>38OutputDir("output-dir",39llvm::cl::desc("Directory to output generated files to"),40llvm::cl::init(""), llvm::cl::cat(RPCGenCategory));4142static std::string GetLibraryOutputDirectory() {43llvm::SmallString<128> Path(OutputDir.getValue());44llvm::sys::path::append(Path, "lib");45return std::string(Path);46}4748static std::unique_ptr<llvm::ToolOutputFile>49CreateOutputFile(llvm::StringRef OutputDir, llvm::StringRef Filename) {50llvm::SmallString<128> Path(OutputDir);51llvm::sys::path::append(Path, Filename);5253std::error_code EC;54auto OutputFile =55std::make_unique<llvm::ToolOutputFile>(Path, EC, llvm::sys::fs::OF_None);56if (EC) {57llvm::errs() << "Failed to create output file: " << Path << "!\n";58return nullptr;59}60return OutputFile;61}6263struct GeneratedByproducts {64std::set<std::string> ClassNames;65std::set<std::string> MangledMethodNames;66std::set<std::string> SkippedMethodNames;67std::set<lldb_rpc_gen::Method> CallbackMethods;68};6970enum SupportLevel {71eUnsupported,72eUnimplemented,73eImplemented,74};7576class SBVisitor : public RecursiveASTVisitor<SBVisitor> {77public:78SBVisitor(GeneratedByproducts &Byproducts, SourceManager &Manager,79ASTContext &Context,80std::unique_ptr<llvm::ToolOutputFile> &&ServerMethodOutputFile,81std::unique_ptr<llvm::ToolOutputFile> &&ServerHeaderOutputFile)82: Byproducts(Byproducts), Manager(Manager), Context(Context),83ServerSourceEmitter(std::move(ServerMethodOutputFile)),84ServerHeaderEmitter(std::move(ServerHeaderOutputFile)) {}8586~SBVisitor() {}8788bool VisitCXXRecordDecl(CXXRecordDecl *RDecl) {89if (ShouldSkipRecord(RDecl))90return true;9192const std::string ClassName = RDecl->getNameAsString();93Byproducts.ClassNames.insert(ClassName);9495// Print 'bool' instead of '_Bool'.96PrintingPolicy Policy(Context.getLangOpts());97Policy.Bool = true;9899for (CXXMethodDecl *MDecl : RDecl->methods()) {100const std::string MangledName =101lldb_rpc_gen::GetMangledName(Context, MDecl);102const bool IsDisallowed = lldb_rpc_gen::MethodIsDisallowed(MangledName);103const bool HasCallbackParameter =104lldb_rpc_gen::HasCallbackParameter(MDecl);105SupportLevel MethodSupportLevel = GetMethodSupportLevel(MDecl);106if (MethodSupportLevel == eImplemented && !IsDisallowed) {107const lldb_rpc_gen::Method Method(MDecl, Policy, Context);108ServerSourceEmitter.EmitMethod(Method);109ServerHeaderEmitter.EmitMethod(Method);110Byproducts.MangledMethodNames.insert(MangledName);111} else if (MethodSupportLevel == eUnimplemented)112Byproducts.SkippedMethodNames.insert(MangledName);113}114return true;115}116117private:118/// Determines whether we should skip a RecordDecl.119/// Conditions for skipping:120/// - Anything not in the header itself121/// - Certain inconvenient classes122/// - Records without definitions (forward declarations)123bool ShouldSkipRecord(CXXRecordDecl *Decl) {124const Type *DeclType = Decl->getTypeForDecl();125QualType CanonicalType = DeclType->getCanonicalTypeInternal();126return !Manager.isInMainFile(Decl->getBeginLoc()) ||127!Decl->hasDefinition() || Decl->getDefinition() != Decl ||128lldb_rpc_gen::TypeIsDisallowedClass(CanonicalType);129}130131/// Check the support level for a type132/// Known unsupported types:133/// - FILE * (We do not want to expose this primitive)134/// - Types that are internal to LLDB135SupportLevel GetTypeSupportLevel(QualType Type) {136const std::string TypeName = Type.getAsString();137if (TypeName == "FILE *" || lldb_rpc_gen::TypeIsFromLLDBPrivate(Type))138return eUnsupported;139140if (lldb_rpc_gen::TypeIsDisallowedClass(Type))141return eUnsupported;142143return eImplemented;144}145146/// Determine the support level of a given method.147/// Known unsupported methods:148/// - Non-public methods (lldb-rpc is a client and can only see public149/// things)150/// - Copy assignment operators (the client side will handle this)151/// - Move assignment operators (the client side will handle this)152/// - Methods involving unsupported types.153/// Known unimplemented methods:154/// - No variadic functions, e.g. Printf155SupportLevel GetMethodSupportLevel(CXXMethodDecl *MDecl) {156AccessSpecifier AS = MDecl->getAccess();157if (AS != AccessSpecifier::AS_public)158return eUnsupported;159if (MDecl->isCopyAssignmentOperator())160return eUnsupported;161if (MDecl->isMoveAssignmentOperator())162return eUnsupported;163164if (MDecl->isVariadic())165return eUnimplemented;166167SupportLevel ReturnTypeLevel = GetTypeSupportLevel(MDecl->getReturnType());168if (ReturnTypeLevel != eImplemented)169return ReturnTypeLevel;170171for (auto *ParamDecl : MDecl->parameters()) {172SupportLevel ParamTypeLevel = GetTypeSupportLevel(ParamDecl->getType());173if (ParamTypeLevel != eImplemented)174return ParamTypeLevel;175}176177// FIXME: If a callback does not take a `void *baton` parameter, it is178// considered unsupported at this time. On the server-side, we hijack the179// baton argument in order to pass additional information to the server-side180// callback so we can correctly perform a reverse RPC call back to the181// client. Without this baton, we would need the server-side callback to182// have some side channel by which it obtained that information, and183// spending time designing that doesn't outweight the cost of doing it at184// the moment.185bool HasCallbackParameter = false;186bool HasBatonParameter = false;187auto End = MDecl->parameters().end();188for (auto Iter = MDecl->parameters().begin(); Iter != End; Iter++) {189if ((*Iter)->getType()->isFunctionPointerType()) {190HasCallbackParameter = true;191continue;192}193194// FIXME: We assume that if we have a function pointer and a void pointer195// together in the same parameter list, that it is not followed by a196// length argument. If that changes, we will need to revisit this197// implementation.198if ((*Iter)->getType()->isVoidPointerType())199HasBatonParameter = true;200}201202if (HasCallbackParameter && !HasBatonParameter)203return eUnimplemented;204205return eImplemented;206}207208GeneratedByproducts &Byproducts;209SourceManager &Manager;210ASTContext &Context;211lldb_rpc_gen::RPCServerSourceEmitter ServerSourceEmitter;212lldb_rpc_gen::RPCServerHeaderEmitter ServerHeaderEmitter;213};214215class SBConsumer : public ASTConsumer {216public:217SBConsumer(GeneratedByproducts &Byproducts, SourceManager &Manager,218ASTContext &Context,219std::unique_ptr<llvm::ToolOutputFile> &&ServerMethodOutputFile,220std::unique_ptr<llvm::ToolOutputFile> &&ServerHeaderOutputFile)221: Visitor(Byproducts, Manager, Context, std::move(ServerMethodOutputFile),222std::move(ServerHeaderOutputFile)) {}223bool HandleTopLevelDecl(DeclGroupRef DR) override {224for (Decl *D : DR)225Visitor.TraverseDecl(D);226227return true;228}229230private:231SBVisitor Visitor;232};233234class SBAction : public ASTFrontendAction {235public:236SBAction(GeneratedByproducts &Byproducts) : Byproducts(Byproducts) {}237238std::unique_ptr<ASTConsumer>239CreateASTConsumer(CompilerInstance &CI, llvm::StringRef File) override {240llvm::StringRef FilenameNoExt =241llvm::sys::path::stem(llvm::sys::path::filename(File));242243const std::string ServerMethodFilename =244"Server_" + FilenameNoExt.str() + ".cpp";245std::unique_ptr<llvm::ToolOutputFile> ServerMethodOutputFile =246CreateOutputFile(GetServerOutputDirectory(), ServerMethodFilename);247if (!ServerMethodOutputFile)248return nullptr;249250const std::string ServerHeaderFilename =251"Server_" + FilenameNoExt.str() + ".h";252std::unique_ptr<llvm::ToolOutputFile> ServerHeaderOutputFile =253CreateOutputFile(GetServerOutputDirectory(), ServerHeaderFilename);254if (!ServerHeaderOutputFile)255return nullptr;256257ServerMethodOutputFile->keep();258ServerHeaderOutputFile->keep();259return std::make_unique<SBConsumer>(260Byproducts, CI.getSourceManager(), CI.getASTContext(),261std::move(ServerMethodOutputFile), std::move(ServerHeaderOutputFile));262}263264private:265GeneratedByproducts &Byproducts;266};267268class SBActionFactory : public FrontendActionFactory {269public:270SBActionFactory(GeneratedByproducts &Byproducts) : Byproducts(Byproducts) {}271272std::unique_ptr<FrontendAction> create() override {273return std::make_unique<SBAction>(Byproducts);274}275276private:277GeneratedByproducts &Byproducts;278};279280bool EmitAmalgamatedServerHeader(const std::vector<std::string> &Files) {281// Create the file282static constexpr llvm::StringLiteral AmalgamatedServerHeaderName = "SBAPI.h";283std::unique_ptr<llvm::ToolOutputFile> AmalgamatedServerHeader =284CreateOutputFile(GetServerOutputDirectory(), AmalgamatedServerHeaderName);285if (!AmalgamatedServerHeader)286return false;287288// Write the header289AmalgamatedServerHeader->os()290<< "#ifndef GENERATED_LLDB_RPC_SERVER_SBAPI_H\n";291AmalgamatedServerHeader->os()292<< "#define GENERATED_LLDB_RPC_SERVER_SBAPI_H\n";293for (const auto &File : Files) {294llvm::StringRef FilenameNoExt =295llvm::sys::path::stem(llvm::sys::path::filename(File));296const std::string ServerHeaderFilename =297"Server_" + FilenameNoExt.str() + ".h";298299AmalgamatedServerHeader->os()300<< "#include \"" + ServerHeaderFilename + "\"\n";301}302AmalgamatedServerHeader->os() << "#include \"SBAPIExtensions.h\"\n";303AmalgamatedServerHeader->os()304<< "#endif // GENERATED_LLDB_RPC_SERVER_SBAPI_H\n";305AmalgamatedServerHeader->keep();306return true;307}308309bool EmitClassNamesFile(std::set<std::string> &ClassNames) {310static constexpr llvm::StringLiteral ClassNamesFileName = "SBClasses.def";311std::unique_ptr<llvm::ToolOutputFile> ClassNamesFile =312CreateOutputFile(OutputDir.getValue(), ClassNamesFileName);313if (!ClassNamesFile)314return false;315316ClassNamesFile->os() << "#ifndef SBCLASS\n";317ClassNamesFile->os() << "#error \"SBClass must be defined\"\n";318ClassNamesFile->os() << "#endif\n";319320for (const auto &ClassName : ClassNames) {321if (ClassName == "SBStream" || ClassName == "SBProgress")322ClassNamesFile->os() << "#if !defined(SBCLASS_EXCLUDE_NONCOPYABLE)\n";323else if (ClassName == "SBReproducer")324ClassNamesFile->os() << "#if !defined(SBCLASS_EXCLUDE_STATICONLY)\n";325326ClassNamesFile->os() << "SBCLASS(" << ClassName << ")\n";327if (ClassName == "SBStream" || ClassName == "SBReproducer" ||328ClassName == "SBProgress")329ClassNamesFile->os() << "#endif\n";330}331ClassNamesFile->keep();332return true;333}334335bool EmitMethodNamesFile(std::set<std::string> &MangledMethodNames) {336static constexpr llvm::StringLiteral MethodNamesFileName = "SBAPI.def";337std::unique_ptr<llvm::ToolOutputFile> MethodNamesFile =338CreateOutputFile(OutputDir.getValue(), MethodNamesFileName);339if (!MethodNamesFile)340return false;341342MethodNamesFile->os() << "#ifndef GENERATE_SBAPI\n";343MethodNamesFile->os() << "#error \"GENERATE_SBAPI must be defined\"\n";344MethodNamesFile->os() << "#endif\n";345346for (const auto &MangledName : MangledMethodNames) {347MethodNamesFile->os() << "GENERATE_SBAPI(" << MangledName << ")\n";348}349MethodNamesFile->keep();350return true;351}352353bool EmitSkippedMethodsFile(std::set<std::string> &SkippedMethodNames) {354static constexpr llvm::StringLiteral FileName = "SkippedMethods.txt";355std::unique_ptr<llvm::ToolOutputFile> File =356CreateOutputFile(OutputDir.getValue(), FileName);357if (!File)358return false;359360for (const auto &Skipped : SkippedMethodNames) {361File->os() << Skipped << "\n";362}363File->keep();364return true;365}366367int main(int argc, const char *argv[]) {368auto ExpectedParser = CommonOptionsParser::create(369argc, argv, RPCGenCategory, llvm::cl::OneOrMore,370"Tool for generating LLDBRPC interfaces and implementations");371372if (!ExpectedParser) {373llvm::errs() << ExpectedParser.takeError();374return 1;375}376377if (OutputDir.empty()) {378llvm::errs() << "Please specify an output directory for the generated "379"files with --output-dir!\n";380return 1;381}382383CommonOptionsParser &OP = ExpectedParser.get();384auto PCHOpts = std::make_shared<PCHContainerOperations>();385PCHOpts->registerWriter(std::make_unique<ObjectFilePCHContainerWriter>());386PCHOpts->registerReader(std::make_unique<ObjectFilePCHContainerReader>());387388ClangTool T(OP.getCompilations(), OP.getSourcePathList(), PCHOpts);389390if (!EmitAmalgamatedServerHeader(OP.getSourcePathList())) {391llvm::errs() << "Failed to create amalgamated server header\n";392return 1;393}394395GeneratedByproducts Byproducts;396397SBActionFactory Factory(Byproducts);398auto Result = T.run(&Factory);399if (!EmitClassNamesFile(Byproducts.ClassNames)) {400llvm::errs() << "Failed to create SB Class file\n";401return 1;402}403if (!EmitMethodNamesFile(Byproducts.MangledMethodNames)) {404llvm::errs() << "Failed to create Method Names file\n";405return 1;406}407if (!EmitSkippedMethodsFile(Byproducts.SkippedMethodNames)) {408llvm::errs() << "Failed to create Skipped Methods file\n";409return 1;410}411412return Result;413}414415416