From d39906095aa70f4f868d14c21ccc6833a495a87f Mon Sep 17 00:00:00 2001 From: Med Ismail Bennani Date: Sat, 5 Jul 2025 14:32:51 -0700 Subject: [PATCH] [lldb/tool] Add SBGenerator clang tool This patch introduces a new lldb SBGenerator clang tool. The main goal is to iterate over the lldb SBAPI headers and collect metadata that will be used to dynamically augment the SBAPI by adding new classes or new methods to existing classes. This first usage of this will be to generate the SBOptional class since we need to make an constructor overload and getter for every SB type. Signed-off-by: Med Ismail Bennani --- lldb/tools/CMakeLists.txt | 1 + lldb/tools/lldb-sb-gen/CMakeLists.txt | 20 +++ lldb/tools/lldb-sb-gen/SBGenerator.cpp | 161 +++++++++++++++++++++++++ 3 files changed, 182 insertions(+) create mode 100644 lldb/tools/lldb-sb-gen/CMakeLists.txt create mode 100644 lldb/tools/lldb-sb-gen/SBGenerator.cpp diff --git a/lldb/tools/CMakeLists.txt b/lldb/tools/CMakeLists.txt index 6804dc234555b..43ea252459c88 100644 --- a/lldb/tools/CMakeLists.txt +++ b/lldb/tools/CMakeLists.txt @@ -10,6 +10,7 @@ add_subdirectory(lldb-fuzzer EXCLUDE_FROM_ALL) add_lldb_tool_subdirectory(lldb-instr) add_lldb_tool_subdirectory(lldb-dap) +add_lldb_tool_subdirectory(lldb-sb-gen) if (CMAKE_SYSTEM_NAME MATCHES "Darwin") add_lldb_tool_subdirectory(darwin-debug) diff --git a/lldb/tools/lldb-sb-gen/CMakeLists.txt b/lldb/tools/lldb-sb-gen/CMakeLists.txt new file mode 100644 index 0000000000000..1609c964ffe3b --- /dev/null +++ b/lldb/tools/lldb-sb-gen/CMakeLists.txt @@ -0,0 +1,20 @@ +if(APPLE) + # add_compile_options(-isysroot ${SYSROOT}) +endif() + + +add_lldb_tool(lldb-sb-gen + SBGenerator.cpp + + LINK_COMPONENTS + Support + CLANG_LIBS + clangAST + clangBasic + clangCodeGen + clangFrontend + clangLex + clangRewrite + clangSerialization + clangTooling + ) diff --git a/lldb/tools/lldb-sb-gen/SBGenerator.cpp b/lldb/tools/lldb-sb-gen/SBGenerator.cpp new file mode 100644 index 0000000000000..db49f4b3a93b8 --- /dev/null +++ b/lldb/tools/lldb-sb-gen/SBGenerator.cpp @@ -0,0 +1,161 @@ +#include "clang/AST/AST.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/CodeGen/ObjectFilePCHContainerWriter.h" +#include "clang/Frontend/ASTConsumers.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/Serialization/ObjectFilePCHContainerReader.h" +#include "clang/Tooling/CommonOptionsParser.h" +#include "clang/Tooling/Tooling.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/ToolOutputFile.h" + +#include +#include +#include +#include + +using namespace clang; +using namespace clang::driver; +using namespace clang::tooling; + +static llvm::cl::OptionCategory SBGenCategory("LLDB Scripting Bridge Generator"); +static llvm::cl::opt + OutputDir("output-dir", + llvm::cl::desc("Directory to output generated files to"), + llvm::cl::init(""), llvm::cl::cat(SBGenCategory)); +static llvm::cl::opt GenerateOptionalClass( + "generate-optional-class", + llvm::cl::desc("Generate a header & source files for SBOptional class"), + llvm::cl::init(false), + llvm::cl::cat(SBGenCategory)); +static llvm::cl::opt Verbose( + "verbose", + llvm::cl::desc("Dumps all the visited SB classes."), + llvm::cl::init(false), + llvm::cl::cat(SBGenCategory)); + +static std::unique_ptr +CreateOutputFile(llvm::StringRef OutputDir, llvm::StringRef Filename) { + llvm::SmallString<256> Path(OutputDir); + llvm::sys::path::append(Path, Filename); + + std::error_code EC; + auto OutputFile = + std::make_unique(Path, EC, llvm::sys::fs::OF_None); + if (EC) { + llvm::errs() << "Failed to create output file: " << Path << "!\n"; + return nullptr; + } + return OutputFile; +} + +class SBVisitor : public RecursiveASTVisitor { +public: + SBVisitor(std::set& ClassNames) : ClassNames(ClassNames) {} + + bool VisitCXXRecordDecl(CXXRecordDecl *Decl) { + // Skip implicit declarations and non-definitions + if (!Decl->isThisDeclarationADefinition() || Decl->isImplicit()) + return true; + + // Skip redeclarations (we only want the canonical definition) + if (Decl != Decl->getDefinition()) + return true; + + ClassNames.insert(Decl->getNameAsString()); + return true; + } + +private: + std::set& ClassNames; +}; + +class SBConsumer : public ASTConsumer { +public: + SBConsumer(Rewriter &R, ASTContext &Context, std::set& ClassNames) : Visitor(ClassNames) {} + + void HandleTranslationUnit(ASTContext &Context) override { + Visitor.TraverseDecl(Context.getTranslationUnitDecl()); + } + +private: + SBVisitor Visitor; +}; + +class SBAction : public ASTFrontendAction { +public: + explicit SBAction(std::set& ClassNames) : ClassNames(ClassNames) {} + + bool BeginSourceFileAction(CompilerInstance &CI) override { return true; } + + void EndSourceFileAction() override { MyRewriter.overwriteChangedFiles(); } + + std::unique_ptr CreateASTConsumer(CompilerInstance &CI, + StringRef File) override { + MyRewriter.setSourceMgr(CI.getSourceManager(), CI.getLangOpts()); + return std::make_unique(MyRewriter, CI.getASTContext(), ClassNames); + } + +private: + std::set& ClassNames; + Rewriter MyRewriter; +}; + +class SBActionFactory : public clang::tooling::FrontendActionFactory { +public: + explicit SBActionFactory(std::set &ClassNames) + : ClassNames(ClassNames) {} + + std::unique_ptr create() override { + return std::make_unique(ClassNames); + } + +private: + std::set &ClassNames; +}; + +int main(int argc, const char **argv) { + auto ExpectedParser = CommonOptionsParser::create( + argc, argv, SBGenCategory, llvm::cl::OneOrMore, + "Utility for generating the scripting bridge dynamic APIs for LLDB's " + "framework."); + if (!ExpectedParser) { + llvm::errs() << ExpectedParser.takeError(); + return 1; + } + + if (OutputDir.empty()) { + llvm::errs() << "Please specify an output directory for the generated " + "files with --output-dir!\n"; + return 1; + } + + // Create the output directory if the user specified one does not exist. + if (!llvm::sys::fs::exists(OutputDir.getValue())) { + llvm::sys::fs::create_directory(OutputDir.getValue()); + } + + CommonOptionsParser &OP = ExpectedParser.get(); + + auto PCHOpts = std::make_shared(); + PCHOpts->registerWriter(std::make_unique()); + PCHOpts->registerReader(std::make_unique()); + + ClangTool T(OP.getCompilations(), OP.getSourcePathList(), PCHOpts); + + std::set ClassNames; + SBActionFactory Factory(ClassNames); + T.run(&Factory); + + if (Verbose) + for (auto& class_name: ClassNames) + std::cout << class_name << std::endl; + + return 0; +}