From 9693eb0ed9898d0853910e0091388221fe5f7902 Mon Sep 17 00:00:00 2001 From: Chelsea Cassanova Date: Wed, 23 Jul 2025 18:10:16 -0700 Subject: [PATCH 1/8] Reland "[lldb][RPC] Upstream lldb-rpc-gen tool" (#146969)" Attempt 2 (#148996) Second attempt at relanding the lldb-rpc-gen tool. This should fix 2 issues: - An assert that was hitting when building on Linux. The assert would hit in the server source emitter, specifically when attemping to determine the storage size for a return type is that is a pointer, but isn't a const char *, const char ** or void pointer. The assert would hit when attempting to generate SBAttachInfo::GetProcessPluginName, which returns a const char * (meaning it shouldn't have been in the code block for the assert at all). The reason that it was hitting the assert when generating this function is that lldb_rpc_gen::TypeIsConstCharPtr was returning false for this function even though it did return a const char *. This was happening because when checking the return type for a const char *, TypeIsConstCharPtr would only check that the underlying type was a signed char. This failed on Linux (but was fine on Darwin), as the underlying type also needs to be checked for being an unsigned char. - Cross compiling support The build for lldb-rpc-gen had no support for cross compiling and as such, the sources generated for lldb-rpc-gen would get compiled too early in phase 2 when cross compiling, before the Clang toolchain was built and this led to an error when trying to include stdlib files. This reland splits this build into 2 by building the tool first and then compiling the sources in the second stage of the cross-compiled build. Original PR Description: This commit upstreams the lldb-rpc-gen tool, a ClangTool that generates the LLDB RPC client and server interfaces. This tool, as well as LLDB RPC itself is built by default. If it needs to be disabled, put -DLLDB_BUILD_LLDBRPC=OFF in your CMake invocation. https://discourse.llvm.org/t/rfc-upstreaming-lldb-rpc/85804 Original PR Link: github.com/llvm/llvm-project/pull/138031 (cherry picked from commit 68c8c8ceeba6da96189335236e3ec80a082e4d7b) --- lldb/cmake/modules/LLDBConfig.cmake | 19 + lldb/test/CMakeLists.txt | 7 +- .../test/Shell/RPC/Generator/Inputs/SBDummy.h | 0 .../Tests/CheckRPCGenToolByproducts.test | 9 + lldb/test/Shell/RPC/Generator/lit.local.cfg | 3 + lldb/test/Shell/helper/toolchain.py | 10 + lldb/test/Shell/lit.site.cfg.py.in | 1 + lldb/tools/CMakeLists.txt | 6 + lldb/tools/lldb-rpc-gen/CMakeLists.txt | 23 + lldb/tools/lldb-rpc-gen/RPCCommon.cpp | 508 ++++++++++++++++++ lldb/tools/lldb-rpc-gen/RPCCommon.h | 108 ++++ .../lldb-rpc-gen/lldb-rpc-gen.cpp | 36 +- .../server/RPCServerHeaderEmitter.cpp | 0 .../server/RPCServerHeaderEmitter.h | 0 .../server/RPCServerSourceEmitter.cpp | 0 .../server/RPCServerSourceEmitter.h | 0 lldb/tools/lldb-rpc/CMakeLists.txt | 22 + lldb/tools/lldb-rpc/LLDBRPCGeneration.cmake | 80 +++ lldb/tools/lldb-rpc/LLDBRPCHeaders.cmake | 101 ++++ 19 files changed, 918 insertions(+), 15 deletions(-) create mode 100644 lldb/test/Shell/RPC/Generator/Inputs/SBDummy.h create mode 100644 lldb/test/Shell/RPC/Generator/Tests/CheckRPCGenToolByproducts.test create mode 100644 lldb/test/Shell/RPC/Generator/lit.local.cfg create mode 100644 lldb/tools/lldb-rpc-gen/CMakeLists.txt create mode 100644 lldb/tools/lldb-rpc-gen/RPCCommon.cpp create mode 100644 lldb/tools/lldb-rpc-gen/RPCCommon.h rename lldb/tools/{lldb-rpc => }/lldb-rpc-gen/lldb-rpc-gen.cpp (93%) rename lldb/tools/{lldb-rpc => }/lldb-rpc-gen/server/RPCServerHeaderEmitter.cpp (100%) rename lldb/tools/{lldb-rpc => }/lldb-rpc-gen/server/RPCServerHeaderEmitter.h (100%) rename lldb/tools/{lldb-rpc => }/lldb-rpc-gen/server/RPCServerSourceEmitter.cpp (100%) rename lldb/tools/{lldb-rpc => }/lldb-rpc-gen/server/RPCServerSourceEmitter.h (100%) create mode 100644 lldb/tools/lldb-rpc/CMakeLists.txt create mode 100644 lldb/tools/lldb-rpc/LLDBRPCGeneration.cmake create mode 100644 lldb/tools/lldb-rpc/LLDBRPCHeaders.cmake diff --git a/lldb/cmake/modules/LLDBConfig.cmake b/lldb/cmake/modules/LLDBConfig.cmake index 20395df757901..98ba6e0b705f6 100644 --- a/lldb/cmake/modules/LLDBConfig.cmake +++ b/lldb/cmake/modules/LLDBConfig.cmake @@ -380,4 +380,23 @@ else() set(LLDB_CAN_USE_DEBUGSERVER OFF) endif() +# In a cross-compile build, we need to skip building the generated +# lldb-rpc sources in the first phase of host build so that they can +# get built using the just-built Clang toolchain in the second phase. +if (NOT DEFINED LLDB_CAN_USE_LLDB_RPC_SERVER) + if ((CMAKE_CROSSCOMPILING OR LLVM_HOST_TRIPLE MATCHES "${LLVM_DEFAULT_TARGET_TRIPLE}") AND + CMAKE_SYSTEM_NAME MATCHES "AIX|Android|Darwin|FreeBSD|Linux|NetBSD|OpenBSD|Windows") + set(LLDB_CAN_USE_LLDB_RPC_SERVER ON) + else() + set(LLDB_CAN_USE_LLDB_RPC_SERVER OFF) + endif() +endif() + +if (CMAKE_CROSSCOMPILING) + set(LLDB_BUILD_LLDBRPC OFF CACHE BOOL "") + get_host_tool_path(lldb-rpc-gen LLDB_RPC_GEN_EXE lldb_rpc_gen_exe lldb_rpc_gen_target) +else() + set(LLDB_BUILD_LLDBRPC ON CACHE BOOL "") +endif() + include(LLDBGenerateConfig) diff --git a/lldb/test/CMakeLists.txt b/lldb/test/CMakeLists.txt index 4eab922a32205..76a2e323510df 100644 --- a/lldb/test/CMakeLists.txt +++ b/lldb/test/CMakeLists.txt @@ -134,6 +134,10 @@ if(TARGET lldb-framework) add_lldb_test_dependency(lldb-framework) endif() +if (LLDB_CAN_USE_LLDB_RPC_SERVER) + add_lldb_test_dependency(lldb-rpc-generate-sources) +endif() + # Add dependencies that are not exported targets when building standalone. if(NOT LLDB_BUILT_STANDALONE) add_lldb_test_dependency( @@ -260,7 +264,8 @@ llvm_canonicalize_cmake_booleans( LLDB_TEST_SHELL_DISABLE_REMOTE LLDB_TOOL_LLDB_SERVER_BUILD LLDB_USE_SYSTEM_DEBUGSERVER - LLDB_IS_64_BITS) + LLDB_IS_64_BITS + LLDB_BUILD_LLDBRPC) # BEGIN SWIFT llvm_canonicalize_cmake_booleans( diff --git a/lldb/test/Shell/RPC/Generator/Inputs/SBDummy.h b/lldb/test/Shell/RPC/Generator/Inputs/SBDummy.h new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/lldb/test/Shell/RPC/Generator/Tests/CheckRPCGenToolByproducts.test b/lldb/test/Shell/RPC/Generator/Tests/CheckRPCGenToolByproducts.test new file mode 100644 index 0000000000000..15fcf8fb39c7d --- /dev/null +++ b/lldb/test/Shell/RPC/Generator/Tests/CheckRPCGenToolByproducts.test @@ -0,0 +1,9 @@ +RUN: %lldb-rpc-gen --output-dir=%t %S/../Inputs/SBDummy.h + +RUN: ls %t | FileCheck %s + +# We're just making sure that the tool emits the class names, +# methods and skipped methods file in the output directory. +CHECK: SBAPI.def +CHECK: SBClasses.def +CHECK: SkippedMethods.txt diff --git a/lldb/test/Shell/RPC/Generator/lit.local.cfg b/lldb/test/Shell/RPC/Generator/lit.local.cfg new file mode 100644 index 0000000000000..db9494781c00c --- /dev/null +++ b/lldb/test/Shell/RPC/Generator/lit.local.cfg @@ -0,0 +1,3 @@ +# All tests for the tool need lldb-rpc-gen to be built. +if not config.lldb_has_lldbrpc: + config.unsupported = True diff --git a/lldb/test/Shell/helper/toolchain.py b/lldb/test/Shell/helper/toolchain.py index 00f5d522d45bd..07abb9cee180b 100644 --- a/lldb/test/Shell/helper/toolchain.py +++ b/lldb/test/Shell/helper/toolchain.py @@ -156,6 +156,16 @@ def use_lldb_substitutions(config): extra_args=["platform"], unresolved="ignore", ), + ToolSubst( + "%lldb-rpc-gen", + command=FindTool("lldb-rpc-gen"), + # We need the LLDB build directory root to pass into the tool, not the test build root. + extra_args=[ + "-p " + config.lldb_build_directory + "/..", + '--extra-arg="-resource-dir=' + config.clang_resource_dir + '"', + ], + unresolved="ignore", + ), "lldb-test", "lldb-dap", ToolSubst( diff --git a/lldb/test/Shell/lit.site.cfg.py.in b/lldb/test/Shell/lit.site.cfg.py.in index a78c0d876e16c..0eff04f22d9e3 100644 --- a/lldb/test/Shell/lit.site.cfg.py.in +++ b/lldb/test/Shell/lit.site.cfg.py.in @@ -36,6 +36,7 @@ config.lldb_build_directory = "@LLDB_TEST_BUILD_DIRECTORY@" config.have_lldb_server = @LLDB_TOOL_LLDB_SERVER_BUILD@ config.lldb_system_debugserver = @LLDB_USE_SYSTEM_DEBUGSERVER@ config.llvm_use_sanitizer = "@LLVM_USE_SANITIZER@" +config.lldb_has_lldbrpc = @LLDB_BUILD_LLDBRPC@ # The shell tests use their own module caches. config.lldb_module_cache = os.path.join("@LLDB_TEST_MODULE_CACHE_LLDB@", "lldb-shell") config.clang_module_cache = os.path.join("@LLDB_TEST_MODULE_CACHE_CLANG@", "lldb-shell") diff --git a/lldb/tools/CMakeLists.txt b/lldb/tools/CMakeLists.txt index edbc855f49cae..a15082fe0b48b 100644 --- a/lldb/tools/CMakeLists.txt +++ b/lldb/tools/CMakeLists.txt @@ -10,6 +10,12 @@ add_subdirectory(lldb-fuzzer EXCLUDE_FROM_ALL) add_lldb_tool_subdirectory(lldb-instr) add_lldb_tool_subdirectory(lldb-dap) +if (LLDB_BUILD_LLDBRPC) + add_lldb_tool_subdirectory(lldb-rpc-gen) +endif() +if (LLDB_CAN_USE_LLDB_RPC_SERVER) + add_subdirectory(lldb-rpc) +endif() if (CMAKE_SYSTEM_NAME MATCHES "Darwin") add_lldb_tool_subdirectory(darwin-debug) diff --git a/lldb/tools/lldb-rpc-gen/CMakeLists.txt b/lldb/tools/lldb-rpc-gen/CMakeLists.txt new file mode 100644 index 0000000000000..65b76431d1bea --- /dev/null +++ b/lldb/tools/lldb-rpc-gen/CMakeLists.txt @@ -0,0 +1,23 @@ +add_lldb_tool(lldb-rpc-gen + RPCCommon.cpp + server/RPCServerHeaderEmitter.cpp + server/RPCServerSourceEmitter.cpp + lldb-rpc-gen.cpp + + CLANG_LIBS + clangAST + clangBasic + clangCodeGen + clangFrontend + clangLex + clangRewrite + clangSerialization + clangTooling + + LINK_COMPONENTS + Support + ) + +if (NOT DEFINED LLDB_RPC_GEN_EXE) + set(LLDB_RPC_GEN_EXE $ CACHE STRING "Executable that generates lldb-rpc-server") +endif() diff --git a/lldb/tools/lldb-rpc-gen/RPCCommon.cpp b/lldb/tools/lldb-rpc-gen/RPCCommon.cpp new file mode 100644 index 0000000000000..34791fa8ef231 --- /dev/null +++ b/lldb/tools/lldb-rpc-gen/RPCCommon.cpp @@ -0,0 +1,508 @@ +//===-- RPCCommon.cpp -----------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "RPCCommon.h" + +#include "clang/AST/AST.h" +#include "clang/AST/Attr.h" +#include "clang/AST/DeclBase.h" +#include "clang/AST/Mangle.h" +#include "clang/Lex/Lexer.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" + +#include + +using namespace clang; + +// We intentionally do not generate some classes because they are currently +// inconvenient, they aren't really used by most consumers, or we're not sure +// why they exist. +static constexpr llvm::StringRef DisallowedClasses[] = { + "SBCommunication", // This class is pretty much unused by consumers, so we + // skip it. + "SBInputReader", // This class is pretty much unused by consumers, so we + // skip it. + "SBCommandPluginInterface", // This class uses virtual functions, and the SB + // API should not have those, so we skip this + // class. + "SBCommand", // There's nothing too difficult about this one, but many of + // its methods take a SBCommandPluginInterface pointer so + // there's no reason to support this. +}; + +// NOTE: In lldb-rpc-gen, we use mangled names when we need to work with +// functions. We do this because we support many functions that have overloads, +// and mangled names have no ambiguity which makes it easier to keep track of. +// This is also possible since the LLDB SB API is stable. + +// We intentionally avoid generating certain methods either because they are +// difficult to support correctly or they aren't really used much from C++. +// NOTE: These methods are marked as deprecated using LLDB_DEPRECATED. +// Normally this macro defines to the deprecated annotation, but this +// functionality is removed in SBDefines.h when generating SWIG bindings which +// we use for testing. Because of this, there is no annotation for the tool to +// pick up on so this list will be used while we have this restriction in +// SBDefines.h. +static constexpr llvm::StringRef DisallowedMethods[] = { + // The threading functionality in SBHostOS is deprecated and thus we do not + // generate them. It would be ideal to add the annotations to the methods + // and then support not generating deprecated methods. However, without + // annotations the generator generates most things correctly. This one is + // problematic because it returns a pointer to an "opaque" structure + // (thread_t) that is not `void *`, so special casing it is more effort than + // it's worth. + "_ZN4lldb8SBHostOS10ThreadJoinEP17_opaque_pthread_tPPvPNS_7SBErrorE", + "_ZN4lldb8SBHostOS12ThreadCancelEP17_opaque_pthread_tPNS_7SBErrorE", + "_ZN4lldb8SBHostOS12ThreadCreateEPKcPFPvS3_ES3_PNS_7SBErrorE", + "_ZN4lldb8SBHostOS12ThreadDetachEP17_opaque_pthread_tPNS_7SBErrorE", + "_ZN4lldb8SBHostOS13ThreadCreatedEPKc", +}; + +static constexpr llvm::StringRef ClassesWithoutDefaultCtor[] = { + "SBHostOS", + "SBReproducer", +}; + +static constexpr llvm::StringRef ClassesWithoutCopyOperations[] = { + "SBHostOS", + "SBReproducer", + "SBStream", + "SBProgress", +}; + +static constexpr llvm::StringRef MethodsWithPointerPlusLen[] = { + "_ZN4lldb6SBData11ReadRawDataERNS_7SBErrorEyPvm", + "_ZN4lldb6SBData7SetDataERNS_7SBErrorEPKvmNS_9ByteOrderEh", + "_ZN4lldb6SBData20SetDataWithOwnershipERNS_7SBErrorEPKvmNS_9ByteOrderEh", + "_ZN4lldb6SBData25CreateDataFromUInt64ArrayENS_9ByteOrderEjPym", + "_ZN4lldb6SBData25CreateDataFromUInt32ArrayENS_9ByteOrderEjPjm", + "_ZN4lldb6SBData25CreateDataFromSInt64ArrayENS_9ByteOrderEjPxm", + "_ZN4lldb6SBData25CreateDataFromSInt32ArrayENS_9ByteOrderEjPim", + "_ZN4lldb6SBData25CreateDataFromDoubleArrayENS_9ByteOrderEjPdm", + "_ZN4lldb6SBData22SetDataFromUInt64ArrayEPym", + "_ZN4lldb6SBData22SetDataFromUInt32ArrayEPjm", + "_ZN4lldb6SBData22SetDataFromSInt64ArrayEPxm", + "_ZN4lldb6SBData22SetDataFromSInt32ArrayEPim", + "_ZN4lldb6SBData22SetDataFromDoubleArrayEPdm", + "_ZN4lldb10SBDebugger22GetDefaultArchitectureEPcm", + "_ZN4lldb10SBDebugger13DispatchInputEPvPKvm", + "_ZN4lldb10SBDebugger13DispatchInputEPKvm", + "_ZN4lldb6SBFile4ReadEPhmPm", + "_ZN4lldb6SBFile5WriteEPKhmPm", + "_ZNK4lldb10SBFileSpec7GetPathEPcm", + "_ZN4lldb10SBFileSpec11ResolvePathEPKcPcm", + "_ZN4lldb8SBModule10GetVersionEPjj", + "_ZN4lldb12SBModuleSpec12SetUUIDBytesEPKhm", + "_ZNK4lldb9SBProcess9GetSTDOUTEPcm", + "_ZNK4lldb9SBProcess9GetSTDERREPcm", + "_ZNK4lldb9SBProcess19GetAsyncProfileDataEPcm", + "_ZN4lldb9SBProcess10ReadMemoryEyPvmRNS_7SBErrorE", + "_ZN4lldb9SBProcess11WriteMemoryEyPKvmRNS_7SBErrorE", + "_ZN4lldb9SBProcess21ReadCStringFromMemoryEyPvmRNS_7SBErrorE", + "_ZNK4lldb16SBStructuredData14GetStringValueEPcm", + "_ZN4lldb8SBTarget23BreakpointCreateByNamesEPPKcjjRKNS_" + "14SBFileSpecListES6_", + "_ZN4lldb8SBTarget10ReadMemoryENS_9SBAddressEPvmRNS_7SBErrorE", + "_ZN4lldb8SBTarget15GetInstructionsENS_9SBAddressEPKvm", + "_ZN4lldb8SBTarget25GetInstructionsWithFlavorENS_9SBAddressEPKcPKvm", + "_ZN4lldb8SBTarget15GetInstructionsEyPKvm", + "_ZN4lldb8SBTarget25GetInstructionsWithFlavorEyPKcPKvm", + "_ZN4lldb8SBThread18GetStopDescriptionEPcm", + // The below mangled names are used for dummy methods in shell tests + // that test the emitters' output. If you're adding any new mangled names + // from the actual SB API to this list please add them above. + "_ZN4lldb33SBRPC_" + "CHECKCONSTCHARPTRPTRWITHLEN27CheckConstCharPtrPtrWithLenEPPKcm", + "_ZN4lldb19SBRPC_CHECKARRAYPTR13CheckArrayPtrEPPKcm", + "_ZN4lldb18SBRPC_CHECKVOIDPTR12CheckVoidPtrEPvm", +}; + +// These classes inherit from rpc::ObjectRef directly (as opposed to +// rpc::LocalObjectRef). Changing them from ObjectRef to LocalObjectRef is ABI +// breaking, so we preserve that compatibility here. +// +// lldb-rpc-gen emits classes as LocalObjectRefs by default. +// +// FIXME: Does it matter which one it emits by default? +static constexpr llvm::StringRef ClassesThatInheritFromObjectRef[] = { + "SBAddress", + "SBBreakpointName", + "SBCommandInterpreter", + "SBCommandReturnObject", + "SBError", + "SBExecutionContext", + "SBExpressionOptions", + "SBFileSpec", + "SBFileSpecList", + "SBFormat", + "SBFunction", + "SBHistoricalFrame", + "SBHistoricalLineEntry", + "SBHistoricalLineEntryList", + "SBLineEntry", + "SBStream", + "SBStringList", + "SBStructuredData", + "SBSymbolContext", + "SBSymbolContextList", + "SBTypeMember", + "SBTypeSummaryOptions", + "SBValueList", +}; + +QualType lldb_rpc_gen::GetUnderlyingType(QualType T) { + QualType UnderlyingType; + if (T->isPointerType()) + UnderlyingType = T->getPointeeType(); + else if (T->isReferenceType()) + UnderlyingType = T.getNonReferenceType(); + else + UnderlyingType = T; + + return UnderlyingType; +} + +QualType lldb_rpc_gen::GetUnqualifiedUnderlyingType(QualType T) { + return GetUnderlyingType(T).getUnqualifiedType(); +} + +std::string lldb_rpc_gen::GetMangledName(ASTContext &Context, + CXXMethodDecl *MDecl) { + std::string Mangled; + llvm::raw_string_ostream MangledStream(Mangled); + + GlobalDecl GDecl; + if (const auto *CtorDecl = dyn_cast(MDecl)) + GDecl = GlobalDecl(CtorDecl, Ctor_Complete); + else if (const auto *DtorDecl = dyn_cast(MDecl)) + GDecl = GlobalDecl(DtorDecl, Dtor_Deleting); + else + GDecl = GlobalDecl(MDecl); + + MangleContext *MC = Context.createMangleContext(); + MC->mangleName(GDecl, MangledStream); + return Mangled; +} + +static auto CheckTypeForLLDBPrivate = [](const Type *Ty) {}; +bool lldb_rpc_gen::TypeIsFromLLDBPrivate(QualType T) { + auto CheckTypeForLLDBPrivate = [](const Type *Ty) { + if (!Ty) + return false; + const auto *CXXRDecl = Ty->getAsCXXRecordDecl(); + if (!CXXRDecl) + return false; + const auto *NSDecl = + llvm::dyn_cast(CXXRDecl->getDeclContext()); + if (!NSDecl) + return false; + return NSDecl->getName() == "lldb_private"; + }; + + // First, get the underlying type (remove qualifications and strip off any + // pointers/references). Then we'll need to desugar this type. This will + // remove things like typedefs, so instead of seeing "lldb::DebuggerSP" we'll + // actually see something like "std::shared_ptr". + QualType UnqualifiedUnderlyingType = GetUnqualifiedUnderlyingType(T); + const Type *DesugaredType = + UnqualifiedUnderlyingType->getUnqualifiedDesugaredType(); + assert(DesugaredType && "DesugaredType from a valid Type is nullptr!"); + + // Check the type itself. + if (CheckTypeForLLDBPrivate(DesugaredType)) + return true; + + // If that didn't work, it's possible that the type has a template argument + // that is an lldb_private type. + if (const auto *TemplateSDecl = + llvm::dyn_cast_or_null( + DesugaredType->getAsCXXRecordDecl())) { + for (const TemplateArgument &TA : + TemplateSDecl->getTemplateArgs().asArray()) { + if (TA.getKind() != TemplateArgument::Type) + continue; + if (CheckTypeForLLDBPrivate(TA.getAsType().getTypePtr())) + return true; + } + } + return false; +} + +bool lldb_rpc_gen::TypeIsSBClass(QualType T) { + QualType UnqualifiedUnderlyingType = GetUnqualifiedUnderlyingType(T); + const auto *CXXRDecl = UnqualifiedUnderlyingType->getAsCXXRecordDecl(); + if (!CXXRDecl) + return false; // SB Classes are always C++ classes + + return CXXRDecl->getName().starts_with("SB"); +} + +bool lldb_rpc_gen::TypeIsConstCharPtr(QualType T) { + if (!T->isPointerType()) + return false; + + QualType UnderlyingType = T->getPointeeType(); + if (!UnderlyingType.isConstQualified()) + return false; + + // NOTE: We should be able to do `UnderlyingType->isCharType` but that will + // return true for `const uint8_t *` since that is effectively an unsigned + // char pointer. We currently do not support pointers other than `const char + // *` and `const char **`. + + // NOTE: Checking that the underlying type is a signed integer works on Darwin + // platforms, but Linux platforms expect that the underlying type is an + // unsigned integer. + return UnderlyingType->isSpecificBuiltinType(BuiltinType::Char_S) || + UnderlyingType->isSpecificBuiltinType(BuiltinType::SChar) || + UnderlyingType->isSpecificBuiltinType(BuiltinType::Char_U) || + UnderlyingType->isSpecificBuiltinType(BuiltinType::UChar); +} + +bool lldb_rpc_gen::TypeIsConstCharPtrPtr(QualType T) { + if (!T->isPointerType()) + return false; + + return TypeIsConstCharPtr(T->getPointeeType()); +} + +bool lldb_rpc_gen::TypeIsDisallowedClass(QualType T) { + QualType UUT = GetUnqualifiedUnderlyingType(T); + const auto *CXXRDecl = UUT->getAsCXXRecordDecl(); + if (!CXXRDecl) + return false; + + llvm::StringRef DeclName = CXXRDecl->getName(); + for (const llvm::StringRef DisallowedClass : DisallowedClasses) + if (DeclName == DisallowedClass) + return true; + return false; +} + +bool lldb_rpc_gen::TypeIsCallbackFunctionPointer(QualType T) { + return T->isFunctionPointerType(); +} + +bool lldb_rpc_gen::MethodIsDisallowed(ASTContext &Context, + CXXMethodDecl *MDecl) { + bool isDisallowed = false; + std::string MangledName = lldb_rpc_gen::GetMangledName(Context, MDecl); + if (llvm::is_contained(DisallowedMethods, MangledName)) + isDisallowed = true; + + if (MDecl->hasAttrs()) { + for (auto *attr : MDecl->getAttrs()) { + if (strcmp(attr->getAttrName()->getNameStart(), "deprecated") == 0) + isDisallowed = true; + } + } + return isDisallowed; +} + +bool lldb_rpc_gen::HasCallbackParameter(CXXMethodDecl *MDecl) { + bool HasCallbackParameter = false; + bool HasBatonParameter = false; + auto End = MDecl->parameters().end(); + for (auto Iter = MDecl->parameters().begin(); Iter != End; Iter++) { + if ((*Iter)->getType()->isFunctionPointerType()) + HasCallbackParameter = true; + else if ((*Iter)->getType()->isVoidPointerType()) + HasBatonParameter = true; + } + + return HasCallbackParameter && HasBatonParameter; +} + +// NOTE: There's possibly a more clever way to do this, but we're keeping +// the string replacement way here. Here is why it is written this way: +// By the time we have already created a `Method` object, we have extracted the +// `QualifiedName` and the relevant QualTypes for parameters/return types, many +// of which contains "lldb::" in them. To change it in a way that would be +// friendly to liblldbrpc, we would need to have a way of replacing that +// namespace at the time of creating a Method, and only for liblldbrpc methods. +// IMO this would complicate Method more than what I'm doing here, and not +// necessarily for any more benefit. +// In clang-tools-extra, there is a ChangeNamespaces tool which tries to do +// something similar to this. It also operates primarily on string replacement, +// but uses more sophisticated clang tooling to do so. +// For now, this will do what we need it to do. +std::string +lldb_rpc_gen::ReplaceLLDBNamespaceWithRPCNamespace(std::string Name) { + const char *lldb_namespace = "lldb::"; + auto Pos = Name.find(lldb_namespace); + while (Pos != std::string::npos) { + constexpr size_t SizeOfLLDBNamespace = 6; + Name.replace(Pos, SizeOfLLDBNamespace, "lldb_rpc::"); + Pos = Name.find(lldb_namespace); + } + return Name; +} + +std::string lldb_rpc_gen::StripLLDBNamespace(std::string Name) { + const char *lldb_namespace = "lldb::"; + auto Pos = Name.find(lldb_namespace); + if (Pos != std::string::npos) { + constexpr size_t SizeOfLLDBNamespace = 6; + Name = Name.substr(Pos + SizeOfLLDBNamespace); + } + return Name; +} + +bool lldb_rpc_gen::SBClassRequiresDefaultCtor(const std::string &ClassName) { + return !llvm::is_contained(ClassesWithoutDefaultCtor, ClassName); +} + +bool lldb_rpc_gen::SBClassRequiresCopyCtorAssign(const std::string &ClassName) { + return !llvm::is_contained(ClassesWithoutCopyOperations, ClassName); +} + +bool lldb_rpc_gen::SBClassInheritsFromObjectRef(const std::string &ClassName) { + return llvm::is_contained(ClassesThatInheritFromObjectRef, ClassName); +} + +std::string lldb_rpc_gen::GetSBClassNameFromType(QualType T) { + assert(lldb_rpc_gen::TypeIsSBClass(T) && + "Cannot get SBClass name from non-SB class type!"); + + QualType UnqualifiedUnderlyingType = GetUnqualifiedUnderlyingType(T); + const auto *CXXRDecl = UnqualifiedUnderlyingType->getAsCXXRecordDecl(); + assert(CXXRDecl && "SB class was not CXXRecordDecl!"); + if (!CXXRDecl) + return std::string(); + + return CXXRDecl->getName().str(); +} +lldb_rpc_gen::Method::Method(CXXMethodDecl *MDecl, const PrintingPolicy &Policy, + ASTContext &Context) + : Policy(Policy), Context(Context), + QualifiedName(MDecl->getQualifiedNameAsString()), + BaseName(MDecl->getNameAsString()), + MangledName(lldb_rpc_gen::GetMangledName(Context, MDecl)), + ReturnType(MDecl->getReturnType()), IsConst(MDecl->isConst()), + IsInstance(MDecl->isInstance()), IsCtor(isa(MDecl)), + IsCopyAssign(MDecl->isCopyAssignmentOperator()), + IsMoveAssign(MDecl->isMoveAssignmentOperator()), + IsDtor(isa(MDecl)), + IsConversionMethod(isa(MDecl)) { + uint8_t UnnamedArgIdx = 0; + bool PrevParamWasPointer = false; + for (const auto *ParamDecl : MDecl->parameters()) { + Param param; + if (ParamDecl->hasDefaultArg()) + param.DefaultValueText = + Lexer::getSourceText( + CharSourceRange::getTokenRange( + ParamDecl->getDefaultArg()->getSourceRange()), + Context.getSourceManager(), Context.getLangOpts()) + .str(); + + param.IsFollowedByLen = false; + param.Name = ParamDecl->getNameAsString(); + // If the parameter has no name, we'll generate one + if (param.Name.empty()) { + param.Name = "arg" + std::to_string(UnnamedArgIdx); + UnnamedArgIdx++; + } + param.Type = ParamDecl->getType(); + + // FIXME: Instead of using this heuristic, the ideal thing would be to add + // annotations to the SBAPI methods themselves. For now, we have a list of + // methods that we know will need this. + if (PrevParamWasPointer) { + PrevParamWasPointer = false; + const bool IsIntegerType = param.Type->isIntegerType() && + !param.Type->isBooleanType() && + !param.Type->isEnumeralType(); + if (IsIntegerType && llvm::is_contained(MethodsWithPointerPlusLen, + llvm::StringRef(MangledName))) + Params.back().IsFollowedByLen = true; + } + + if (param.Type->isPointerType() && + !lldb_rpc_gen::TypeIsConstCharPtr(param.Type) && + !param.Type->isFunctionPointerType()) + PrevParamWasPointer = true; + + if (param.Type->isFunctionPointerType()) + ContainsFunctionPointerParameter = true; + + Params.push_back(param); + } + + if (IsInstance) + ThisType = MDecl->getThisType(); + + if (const auto *CtorDecl = dyn_cast(MDecl)) { + IsExplicitCtorOrConversionMethod = CtorDecl->isExplicit(); + IsCopyCtor = CtorDecl->isCopyConstructor(); + IsMoveCtor = CtorDecl->isMoveConstructor(); + } else if (const auto *ConversionDecl = dyn_cast(MDecl)) + IsExplicitCtorOrConversionMethod = ConversionDecl->isExplicit(); +} + +// Adding a '<' allows us to use Methods in ordered containers. +// The ordering is on memory addresses. +bool lldb_rpc_gen::Method::operator<(const lldb_rpc_gen::Method &rhs) const { + return this < &rhs; +} + +std::string +lldb_rpc_gen::Method::CreateParamListAsString(GenerationKind Generation, + bool IncludeDefaultValue) const { + assert((!IncludeDefaultValue || Generation == eLibrary) && + "Default values should only be emitted on the library side!"); + + std::vector ParamList; + + if (Generation == eLibrary && RequiresConnectionParameter()) + ParamList.push_back("const rpc::Connection &connection"); + + for (const auto &Param : Params) { + std::string ParamString; + llvm::raw_string_ostream ParamStringStream(ParamString); + + if (Generation == eLibrary) + ParamStringStream << lldb_rpc_gen::ReplaceLLDBNamespaceWithRPCNamespace( + Param.Type.getAsString(Policy)); + else + ParamStringStream << Param.Type.getAsString(Policy); + + ParamStringStream << " " << Param.Name; + if (IncludeDefaultValue && Generation == eLibrary && + !Param.DefaultValueText.empty()) + ParamStringStream << " = " + << lldb_rpc_gen::ReplaceLLDBNamespaceWithRPCNamespace( + Param.DefaultValueText); + + ParamList.push_back(ParamString); + } + + return llvm::join(ParamList, ", "); +} + +bool lldb_rpc_gen::Method::RequiresConnectionParameter() const { + if (!IsCtor && IsInstance) + return false; + if (IsCopyCtor || IsMoveCtor) + return false; + for (const auto &Param : Params) { + // We can re-use the connection from our parameter if possible. + // Const-qualified parameters are input parameters and already + // have a valid connection to provide to the current method. + if (TypeIsSBClass(Param.Type) && + GetUnderlyingType(Param.Type).isConstQualified()) + return false; + } + + return true; +} diff --git a/lldb/tools/lldb-rpc-gen/RPCCommon.h b/lldb/tools/lldb-rpc-gen/RPCCommon.h new file mode 100644 index 0000000000000..edc03b4f81a3d --- /dev/null +++ b/lldb/tools/lldb-rpc-gen/RPCCommon.h @@ -0,0 +1,108 @@ +//===-- RPCCommon.h -------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_RPC_GEN_RPCCOMMON_H +#define LLDB_RPC_GEN_RPCCOMMON_H + +#include "clang/AST/AST.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclCXX.h" +#include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/raw_ostream.h" + +#include + +using namespace clang; + +namespace lldb_rpc_gen { +QualType GetUnderlyingType(QualType T); +QualType GetUnqualifiedUnderlyingType(QualType T); +std::string GetMangledName(ASTContext &Context, CXXMethodDecl *MDecl); + +bool TypeIsFromLLDBPrivate(QualType T); +bool TypeIsSBClass(QualType T); +bool TypeIsConstCharPtr(QualType T); +bool TypeIsConstCharPtrPtr(QualType T); +bool TypeIsDisallowedClass(QualType T); +bool TypeIsCallbackFunctionPointer(QualType T); + +bool MethodIsDisallowed(ASTContext &Context, CXXMethodDecl *MDecl); +bool HasCallbackParameter(CXXMethodDecl *MDecl); + +std::string ReplaceLLDBNamespaceWithRPCNamespace(std::string Name); +std::string StripLLDBNamespace(std::string Name); +bool SBClassRequiresDefaultCtor(const std::string &ClassName); +bool SBClassRequiresCopyCtorAssign(const std::string &ClassName); +bool SBClassInheritsFromObjectRef(const std::string &ClassName); +std::string GetSBClassNameFromType(QualType T); +struct Param { + std::string Name; + QualType Type; + std::string DefaultValueText; + bool IsFollowedByLen; +}; + +enum GenerationKind : bool { eServer, eLibrary }; + +struct Method { + enum Type { eOther, eConstructor, eDestructor }; + + Method(CXXMethodDecl *MDecl, const PrintingPolicy &Policy, + ASTContext &Context); + + // Adding a '<' allows us to use Methods in ordered containers. + // The ordering is on memory addresses. + bool operator<(const lldb_rpc_gen::Method &rhs) const; + const PrintingPolicy &Policy; + const ASTContext &Context; + std::string QualifiedName; + std::string BaseName; + std::string MangledName; + QualType ReturnType; + QualType ThisType; + std::vector Params; + bool IsConst = false; + bool IsInstance = false; + bool IsCtor = false; + bool IsCopyCtor = false; + bool IsCopyAssign = false; + bool IsMoveCtor = false; + bool IsMoveAssign = false; + bool IsDtor = false; + bool IsConversionMethod = false; + bool IsExplicitCtorOrConversionMethod = false; + bool ContainsFunctionPointerParameter = false; + + std::string CreateParamListAsString(GenerationKind Generation, + bool IncludeDefaultValue = false) const; + + bool RequiresConnectionParameter() const; +}; + +std::string +GetDefaultArgumentsForConstructor(std::string ClassName, + const lldb_rpc_gen::Method &method); + +class FileEmitter { +protected: + FileEmitter(std::unique_ptr &&OutputFile) + : OutputFile(std::move(OutputFile)), IndentLevel(0) {} + void EmitLine(const std::string &line) { + for (auto i = 0; i < IndentLevel; i++) + OutputFile->os() << " "; + + OutputFile->os() << line << "\n"; + } + + void EmitNewLine() { OutputFile->os() << "\n"; } + + std::unique_ptr OutputFile; + uint8_t IndentLevel; +}; +} // namespace lldb_rpc_gen +#endif // LLDB_RPC_GEN_RPCCOMMON_H diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp b/lldb/tools/lldb-rpc-gen/lldb-rpc-gen.cpp similarity index 93% rename from lldb/tools/lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp rename to lldb/tools/lldb-rpc-gen/lldb-rpc-gen.cpp index e6b601ea13012..fdcfee96a387e 100644 --- a/lldb/tools/lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp +++ b/lldb/tools/lldb-rpc-gen/lldb-rpc-gen.cpp @@ -7,8 +7,8 @@ //===----------------------------------------------------------------------===// #include "RPCCommon.h" -#include "RPCServerHeaderEmitter.h" -#include "RPCServerSourceEmitter.h" +#include "server/RPCServerHeaderEmitter.h" +#include "server/RPCServerSourceEmitter.h" #include "clang/AST/AST.h" #include "clang/AST/ASTConsumer.h" @@ -40,15 +40,15 @@ static llvm::cl::opt llvm::cl::desc("Directory to output generated files to"), llvm::cl::init(""), llvm::cl::cat(RPCGenCategory)); -static std::string GetLibraryOutputDirectory() { +static std::string GetServerOutputDirectory() { llvm::SmallString<128> Path(OutputDir.getValue()); - llvm::sys::path::append(Path, "lib"); + llvm::sys::path::append(Path, "server"); return std::string(Path); } static std::unique_ptr CreateOutputFile(llvm::StringRef OutputDir, llvm::StringRef Filename) { - llvm::SmallString<128> Path(OutputDir); + llvm::SmallString<256> Path(OutputDir); llvm::sys::path::append(Path, Filename); std::error_code EC; @@ -100,7 +100,8 @@ class SBVisitor : public RecursiveASTVisitor { for (CXXMethodDecl *MDecl : RDecl->methods()) { const std::string MangledName = lldb_rpc_gen::GetMangledName(Context, MDecl); - const bool IsDisallowed = lldb_rpc_gen::MethodIsDisallowed(MangledName); + const bool IsDisallowed = + lldb_rpc_gen::MethodIsDisallowed(Context, MDecl); const bool HasCallbackParameter = lldb_rpc_gen::HasCallbackParameter(MDecl); SupportLevel MethodSupportLevel = GetMethodSupportLevel(MDecl); @@ -314,9 +315,9 @@ bool EmitClassNamesFile(std::set &ClassNames) { if (!ClassNamesFile) return false; - ClassNamesFile->os() << "#ifndef SBCLASS\n"; - ClassNamesFile->os() << "#error \"SBClass must be defined\"\n"; - ClassNamesFile->os() << "#endif\n"; + ClassNamesFile->os() << "#ifndef SBCLASS\n" + << "#error \"SBClass must be defined\"\n" + << "#endif\n"; for (const auto &ClassName : ClassNames) { if (ClassName == "SBStream" || ClassName == "SBProgress") @@ -340,9 +341,9 @@ bool EmitMethodNamesFile(std::set &MangledMethodNames) { if (!MethodNamesFile) return false; - MethodNamesFile->os() << "#ifndef GENERATE_SBAPI\n"; - MethodNamesFile->os() << "#error \"GENERATE_SBAPI must be defined\"\n"; - MethodNamesFile->os() << "#endif\n"; + MethodNamesFile->os() << "#ifndef GENERATE_SBAPI\n" + << "#error \"GENERATE_SBAPI must be defined\"\n" + << "#endif\n"; for (const auto &MangledName : MangledMethodNames) { MethodNamesFile->os() << "GENERATE_SBAPI(" << MangledName << ")\n"; @@ -358,9 +359,8 @@ bool EmitSkippedMethodsFile(std::set &SkippedMethodNames) { if (!File) return false; - for (const auto &Skipped : SkippedMethodNames) { + for (const auto &Skipped : SkippedMethodNames) File->os() << Skipped << "\n"; - } File->keep(); return true; } @@ -381,6 +381,14 @@ int main(int argc, const char *argv[]) { 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()); + } + + if (!llvm::sys::fs::exists(GetServerOutputDirectory())) { + llvm::sys::fs::create_directory(GetServerOutputDirectory()); + } CommonOptionsParser &OP = ExpectedParser.get(); auto PCHOpts = std::make_shared(); PCHOpts->registerWriter(std::make_unique()); diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerHeaderEmitter.cpp b/lldb/tools/lldb-rpc-gen/server/RPCServerHeaderEmitter.cpp similarity index 100% rename from lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerHeaderEmitter.cpp rename to lldb/tools/lldb-rpc-gen/server/RPCServerHeaderEmitter.cpp diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerHeaderEmitter.h b/lldb/tools/lldb-rpc-gen/server/RPCServerHeaderEmitter.h similarity index 100% rename from lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerHeaderEmitter.h rename to lldb/tools/lldb-rpc-gen/server/RPCServerHeaderEmitter.h diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerSourceEmitter.cpp b/lldb/tools/lldb-rpc-gen/server/RPCServerSourceEmitter.cpp similarity index 100% rename from lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerSourceEmitter.cpp rename to lldb/tools/lldb-rpc-gen/server/RPCServerSourceEmitter.cpp diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerSourceEmitter.h b/lldb/tools/lldb-rpc-gen/server/RPCServerSourceEmitter.h similarity index 100% rename from lldb/tools/lldb-rpc/lldb-rpc-gen/server/RPCServerSourceEmitter.h rename to lldb/tools/lldb-rpc-gen/server/RPCServerSourceEmitter.h diff --git a/lldb/tools/lldb-rpc/CMakeLists.txt b/lldb/tools/lldb-rpc/CMakeLists.txt new file mode 100644 index 0000000000000..fdd6cf9163e96 --- /dev/null +++ b/lldb/tools/lldb-rpc/CMakeLists.txt @@ -0,0 +1,22 @@ +include(CheckCXXCompilerFlag) +# Umbrella target for the entire framework is a default target. +add_custom_target(lldb-rpc ALL) + +if(LLDB_CODESIGN_IDENTITY) + # Use explicit LLDB identity + set(LLVM_CODESIGNING_IDENTITY ${LLDB_CODESIGN_IDENTITY}) +else() + # Use explicit LLVM identity or default to ad-hoc signing if empty + if(NOT LLVM_CODESIGNING_IDENTITY) + set(LLVM_CODESIGNING_IDENTITY -) + endif() +endif() + +# LLDBRPCGeneration.cmake needs the LLDB_RPC_GEN_EXE variable +# which gets defined in the lldb-rpc-gen folder, so we're adding +# this folder before we add that file. +add_lldb_tool_subdirectory(lldb-rpc-gen) +include(${CMAKE_CURRENT_SOURCE_DIR}/LLDBRPCGeneration.cmake) +include(${CMAKE_CURRENT_SOURCE_DIR}/LLDBRPCHeaders.cmake) + +add_dependencies(lldb-rpc lldb-rpc-generate-sources liblldbrpc-headers) diff --git a/lldb/tools/lldb-rpc/LLDBRPCGeneration.cmake b/lldb/tools/lldb-rpc/LLDBRPCGeneration.cmake new file mode 100644 index 0000000000000..a4cacf8692a85 --- /dev/null +++ b/lldb/tools/lldb-rpc/LLDBRPCGeneration.cmake @@ -0,0 +1,80 @@ +if (NOT DEFINED LLDB_RPC_GEN_EXE) + message(FATAL_ERROR + "Unable to generate lldb-rpc sources because LLDB_RPC_GEN_EXE is not + defined. If you are cross-compiling, please build lldb-rpc-gen for your host + platform.") +endif() +set(lldb_rpc_generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated") +set(lldb_rpc_server_generated_source_dir "${lldb_rpc_generated_dir}/server") + +file(GLOB api_headers ${LLDB_SOURCE_DIR}/include/lldb/API/SB*.h) +# We don't generate SBCommunication +list(REMOVE_ITEM api_headers ${LLDB_SOURCE_DIR}/include/lldb/API/SBCommunication.h) +# SBDefines.h is mostly definitions and forward declarations, nothing to +# generate. +list(REMOVE_ITEM api_headers ${LLDB_SOURCE_DIR}/include/lldb/API/SBDefines.h) + +# Generate the list of byproducts. Note that we cannot just glob the files in +# the directory with the generated sources because BYPRODUCTS needs to be known +# at configure time but the files are generated at build time. +set(lldb_rpc_gen_byproducts + ${lldb_rpc_generated_dir}/SBClasses.def + ${lldb_rpc_generated_dir}/SBAPI.def + ${lldb_rpc_generated_dir}/lldb.py + ${lldb_rpc_server_generated_source_dir}/SBAPI.h +) + +set(lldb_rpc_gen_server_impl_files) +foreach(path ${api_headers}) + get_filename_component(filename_no_ext ${path} NAME_WLE) + + set(server_header_file "Server_${filename_no_ext}.h") + list(APPEND lldb_rpc_gen_byproducts "${lldb_rpc_server_generated_source_dir}/${server_header_file}") + + set(server_impl_file "Server_${filename_no_ext}.cpp") + list(APPEND lldb_rpc_gen_byproducts "${lldb_rpc_server_generated_source_dir}/${server_impl_file}") + list(APPEND lldb_rpc_gen_server_impl_files "${lldb_rpc_server_generated_source_dir}/${server_impl_file}") + +endforeach() + +# Make sure that the clang-resource-dir is set correctly or else the tool will +# fail to run. This is only needed when we do a standalone build. +set(clang_resource_dir_arg) +if (TARGET clang-resource-headers) + set(clang_resource_headers_dir + $) + set(clang_resource_dir_arg --extra-arg="-resource-dir=${clang_resource_headers_dir}/..") +else() + set(clang_resource_dir_arg --extra-arg="-resource-dir=${LLDB_EXTERNAL_CLANG_RESOURCE_DIR}") +endif() + +set(sysroot_arg) +if (DEFINED TOOLCHAIN_TARGET_SYSROOTFS) + set(sysroot_arg --extra-arg="-resource-dir=${TOOLCHAIN_TARGET_SYSROOTFS}") +endif() + +add_custom_command(OUTPUT ${lldb_rpc_gen_byproducts} + COMMAND ${CMAKE_COMMAND} -E make_directory + ${lldb_rpc_generated_dir} + + COMMAND ${CMAKE_COMMAND} -E make_directory + ${lldb_rpc_server_generated_source_dir} + + COMMAND ${LLDB_RPC_GEN_EXE} + -p ${CMAKE_BINARY_DIR} + --output-dir=${lldb_rpc_generated_dir} + ${sysroot_arg} + --extra-arg="-USWIG" + ${api_headers} + + DEPENDS ${LLDB_RPC_GEN_EXE} ${api_headers} + COMMENT "Generating sources for lldb-rpc-server..." + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} +) + +add_custom_target(lldb-rpc-generate-sources + DEPENDS + ${lldb_rpc_gen_byproducts} + lldb-sbapi-dwarf-enums) + +add_dependencies(lldb-rpc-generate-sources clang-resource-headers) diff --git a/lldb/tools/lldb-rpc/LLDBRPCHeaders.cmake b/lldb/tools/lldb-rpc/LLDBRPCHeaders.cmake new file mode 100644 index 0000000000000..97ad481140248 --- /dev/null +++ b/lldb/tools/lldb-rpc/LLDBRPCHeaders.cmake @@ -0,0 +1,101 @@ +set(derived_headers_location "${CMAKE_CURRENT_BINARY_DIR}/DerivedHeaders") + +# Obtain the original headers from their staged location in the build directory. +set(original_headers_location "${CMAKE_BINARY_DIR}/include/lldb") +set(headers_to_process + SBDefines.h + lldb-defines.h + lldb-enumerations.h + lldb-types.h +) + +file(MAKE_DIRECTORY ${derived_headers_location}) + +# Take the original headers and convert them RPC as necessary using the conversion script. +set(original_headers) +set(derived_headers) +foreach(header ${headers_to_process}) + set(original_header "${original_headers_location}/${header}") + + get_filename_component(header_filename ${header} NAME) + string(REPLACE "lldb-" "lldb-rpc-" rpc_header_filename "${header_filename}") + set(derived_header "${derived_headers_location}/${rpc_header_filename}") + + list(APPEND original_headers "${original_header}") + list(APPEND derived_headers "${derived_header}") + add_custom_command(OUTPUT ${derived_header} + COMMAND ${Python3_EXECUTABLE} ${LLDB_SOURCE_DIR}/scripts/convert-lldb-header-to-rpc-header.py + ${original_header} ${derived_header} + DEPENDS ${original_header} + + COMMENT "Creating ${derived_header}" + ) +endforeach() + +# Do the same thing for any header files that were autogenerated. +set(generated_headers_to_process + API/SBLanguages.h +) +foreach(header ${generated_headers_to_process}) + set(original_header "${LLDB_OBJ_DIR}/include/lldb/${header}") + + get_filename_component(header_filename ${header} NAME) + string(REPLACE "lldb-" "lldb-rpc-" rpc_header_filename "${header_filename}") + set(derived_header "${derived_headers_location}/${rpc_header_filename}") + + list(APPEND original_headers "${original_header}") + list(APPEND derived_headers "${derived_header}") + add_custom_command(OUTPUT ${derived_header} + COMMAND ${CMAKE_COMMAND} -E copy ${original_header} ${derived_header} + COMMAND ${Python3_EXECUTABLE} ${LLDB_SOURCE_DIR}/scripts/convert-lldb-header-to-rpc-header.py + ${original_header} ${derived_header} + DEPENDS lldb-sbapi-dwarf-enums + + COMMENT "Creating ${derived_header}" + ) +endforeach() + +add_custom_target(copy-aux-rpc-headers DEPENDS ${derived_headers}) +add_dependencies(copy-aux-rpc-headers liblldb-header-staging) + +list(APPEND public_headers + ${derived_headers_location}/SBDefines.h + ${derived_headers_location}/SBLanguages.h + ${derived_headers_location}/lldb-rpc-enumerations.h + ${derived_headers_location}/lldb-rpc-types.h + ${derived_headers_location}/lldb-rpc-defines.h +) + +# Collect and preprocess headers for the framework bundle +set(version_header + ${derived_headers_location}/lldb-rpc-defines.h +) + +function(FixIncludePaths in subfolder out) + get_filename_component(base_name ${in} NAME) + set(parked_header ${CMAKE_CURRENT_BINARY_DIR}/ParkedHeaders/${subfolder}/${base_name}) + set(${out} ${parked_header} PARENT_SCOPE) + find_program(unifdef_EXECUTABLE unifdef) + + add_custom_command(OUTPUT ${parked_header} + COMMAND ${LLDB_SOURCE_DIR}/scripts/framework-header-fix.py + -f lldb_rpc -i ${in} -o ${parked_header} -p ${unifdef_EXECUTABLE} USWIG + DEPENDS ${in} + COMMENT "Fixing includes in ${in}" + ) +endfunction() + +set(preprocessed_headers) + +# Apply include-paths fix and any version fix on all headers and park them. +foreach(source_header ${public_headers}) + FixIncludePaths(${source_header} Headers parked_header) + list(APPEND preprocessed_headers ${parked_header}) +endforeach() + +# Wrap header preprocessing in a target, so liblldbrpc can depend on. +add_custom_target(liblldbrpc-headers DEPENDS ${preprocessed_headers}) +add_dependencies(liblldbrpc-headers copy-aux-rpc-headers liblldb-header-staging) +set_target_properties(liblldbrpc-headers PROPERTIES + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/ParkedHeaders +) From 8209113012d4d92031283eb635772ef9113d3819 Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere Date: Thu, 11 Sep 2025 10:48:50 -0700 Subject: [PATCH 2/8] Revert "[lldb][cmake] Create dependencies for LLDB header targets (#150995)" This reverts commit c162846f8bd23b8e3d4e6a1e7737dd1cefb91f0d. --- lldb/source/API/CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/lldb/source/API/CMakeLists.txt b/lldb/source/API/CMakeLists.txt index c8fdb130cf6d4..64535117d4d4b 100644 --- a/lldb/source/API/CMakeLists.txt +++ b/lldb/source/API/CMakeLists.txt @@ -361,7 +361,6 @@ foreach(header endif() add_custom_target(liblldb-stage-header-${basename} DEPENDS ${staged_header}) - add_dependencies(liblldb-stage-header-${basename} lldb-sbapi-dwarf-enums) add_dependencies(liblldb-header-staging liblldb-stage-header-${basename}) add_custom_command( DEPENDS ${header} OUTPUT ${staged_header} @@ -374,7 +373,6 @@ foreach(header set(output_header $/Headers/${basename}) add_custom_target(lldb-framework-fixup-header-${basename} DEPENDS ${staged_header}) - add_dependencies(lldb-framework-fixup-header-${basename} liblldb-stage-header-${basename}) add_dependencies(lldb-framework-fixup-all-headers lldb-framework-fixup-header-${basename}) add_custom_command(TARGET lldb-framework-fixup-header-${basename} POST_BUILD From 8e90feddc8bde95017a3e58fa5fa831e8919325b Mon Sep 17 00:00:00 2001 From: Chelsea Cassanova Date: Wed, 23 Jul 2025 18:41:01 -0700 Subject: [PATCH 3/8] [lldb][headers] Fix header staging target for RPC (#150355) This commit fixes the target that stages headers in the build dir's include directory so that the headers are staged correctly so that the target for liblldbrpc-headers can depend on it properly (cherry picked from commit dfe9fcc9a6b40dfa01ce5884fdc185912ccda8e3) --- lldb/source/API/CMakeLists.txt | 2 +- lldb/tools/lldb-rpc/LLDBRPCHeaders.cmake | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lldb/source/API/CMakeLists.txt b/lldb/source/API/CMakeLists.txt index 64535117d4d4b..09e4c7a3d0733 100644 --- a/lldb/source/API/CMakeLists.txt +++ b/lldb/source/API/CMakeLists.txt @@ -336,7 +336,7 @@ list(REMOVE_ITEM root_public_headers ${root_private_headers}) find_program(unifdef_EXECUTABLE unifdef) -add_custom_target(liblldb-header-staging DEPENDS ${lldb_staged_headers} ${lldb_header_staging_dir}/lldb-defines.h) +add_custom_target(liblldb-header-staging) if (LLDB_BUILD_FRAMEWORK) add_custom_target(lldb-framework-fixup-all-headers) diff --git a/lldb/tools/lldb-rpc/LLDBRPCHeaders.cmake b/lldb/tools/lldb-rpc/LLDBRPCHeaders.cmake index 97ad481140248..6c363f411511a 100644 --- a/lldb/tools/lldb-rpc/LLDBRPCHeaders.cmake +++ b/lldb/tools/lldb-rpc/LLDBRPCHeaders.cmake @@ -79,7 +79,7 @@ function(FixIncludePaths in subfolder out) add_custom_command(OUTPUT ${parked_header} COMMAND ${LLDB_SOURCE_DIR}/scripts/framework-header-fix.py - -f lldb_rpc -i ${in} -o ${parked_header} -p ${unifdef_EXECUTABLE} USWIG + -f lldb_rpc -i ${in} -o ${parked_header} -p ${unifdef_EXECUTABLE} --unifdef_guards USWIG DEPENDS ${in} COMMENT "Fixing includes in ${in}" ) From 7c1700b5923cc78306af649853391fb68fb9ee72 Mon Sep 17 00:00:00 2001 From: Chelsea Cassanova Date: Thu, 24 Jul 2025 12:43:52 -0700 Subject: [PATCH 4/8] [lldb][docs] Update instructions to build standalone (#137383) The instructions to build LLDB standalone contain a CMake configure step to build LLVM standalone. This configure step needs to also have the CMake build type in order to work. (cherry picked from commit 4396c87bfee23ccbb6a106866d28350f5cc08ded) --- lldb/docs/resources/build.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lldb/docs/resources/build.rst b/lldb/docs/resources/build.rst index 480430fede928..4319f1791fd5d 100644 --- a/lldb/docs/resources/build.rst +++ b/lldb/docs/resources/build.rst @@ -204,12 +204,13 @@ checked out above, but now we will have multiple build-trees: Run CMake with ``-B`` pointing to a new directory for the provided build-tree\ :sup:`1` and the positional argument pointing to the ``llvm`` -directory in the source-tree. Note that we leave out LLDB here and only include +directory in the source-tree.\ :sup:`2` Note that we leave out LLDB here and only include Clang. Then we build the ``ALL`` target with ninja: :: $ cmake -B /path/to/llvm-build -G Ninja \ + -DCMAKE_BUILD_TYPE=[] \ -DLLVM_ENABLE_PROJECTS=clang \ [] /path/to/llvm-project/llvm $ ninja @@ -236,6 +237,8 @@ remove it from the Ninja command. #. The ``-B`` argument was undocumented for a while and is only officially supported since `CMake version 3.14 `_ + #. If you want to have a standalone LLDB build with tests enabled, you also + need to pass in ``-DLLVM_ENABLE_RUNTIME='libcxx;libcxxabi;libunwind'`` to your CMake invocation when configuring your LLVM standalone build. .. _CommonCMakeOptions: From 89d0c853a2552a6ab9c1b6164e26e98d76e9033c Mon Sep 17 00:00:00 2001 From: Chelsea Cassanova Date: Fri, 25 Jul 2025 15:25:05 -0700 Subject: [PATCH 5/8] [lldb][rpc] Disable building lldb-rpc-gen tool (#150699) Disabling the lldb-rpc-gen tool while issues with certain builds are solved: https://github.com/llvm/llvm-project/pull/148996 (cherry picked from commit 67b519577ee6b3743c6c03c6230991cede5648a5) --- lldb/cmake/modules/LLDBConfig.cmake | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/lldb/cmake/modules/LLDBConfig.cmake b/lldb/cmake/modules/LLDBConfig.cmake index 98ba6e0b705f6..9f8b1001fd91f 100644 --- a/lldb/cmake/modules/LLDBConfig.cmake +++ b/lldb/cmake/modules/LLDBConfig.cmake @@ -384,6 +384,8 @@ endif() # lldb-rpc sources in the first phase of host build so that they can # get built using the just-built Clang toolchain in the second phase. if (NOT DEFINED LLDB_CAN_USE_LLDB_RPC_SERVER) + set(LLDB_CAN_USE_LLDB_RPC_SERVER OFF) +else() if ((CMAKE_CROSSCOMPILING OR LLVM_HOST_TRIPLE MATCHES "${LLVM_DEFAULT_TARGET_TRIPLE}") AND CMAKE_SYSTEM_NAME MATCHES "AIX|Android|Darwin|FreeBSD|Linux|NetBSD|OpenBSD|Windows") set(LLDB_CAN_USE_LLDB_RPC_SERVER ON) @@ -392,11 +394,16 @@ if (NOT DEFINED LLDB_CAN_USE_LLDB_RPC_SERVER) endif() endif() -if (CMAKE_CROSSCOMPILING) - set(LLDB_BUILD_LLDBRPC OFF CACHE BOOL "") - get_host_tool_path(lldb-rpc-gen LLDB_RPC_GEN_EXE lldb_rpc_gen_exe lldb_rpc_gen_target) + +if (NOT DEFINED LLDB_BUILD_LLDBRPC) + set(LLDB_BUILD_LLDBRPC OFF) else() - set(LLDB_BUILD_LLDBRPC ON CACHE BOOL "") + if (CMAKE_CROSSCOMPILING) + set(LLDB_BUILD_LLDBRPC OFF CACHE BOOL "") + get_host_tool_path(lldb-rpc-gen LLDB_RPC_GEN_EXE lldb_rpc_gen_exe lldb_rpc_gen_target) + else() + set(LLDB_BUILD_LLDBRPC ON CACHE BOOL "") + endif() endif() include(LLDBGenerateConfig) From 554130c8bbb4797c8c4b4f23b943c0843e72cb79 Mon Sep 17 00:00:00 2001 From: Chelsea Cassanova Date: Tue, 29 Jul 2025 14:24:35 -0700 Subject: [PATCH 6/8] [lldb][cmake] Create dependencies for LLDB header targets (#150995) The LLDB standalone build using Xcode currently fails due to the headers being attached to multiple targets, but none of these targets depending on each other. This commit resolves this by creating those dependencies. (cherry picked from commit c162846f8bd23b8e3d4e6a1e7737dd1cefb91f0d) --- lldb/source/API/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lldb/source/API/CMakeLists.txt b/lldb/source/API/CMakeLists.txt index 09e4c7a3d0733..c53fc272e4e15 100644 --- a/lldb/source/API/CMakeLists.txt +++ b/lldb/source/API/CMakeLists.txt @@ -361,6 +361,7 @@ foreach(header endif() add_custom_target(liblldb-stage-header-${basename} DEPENDS ${staged_header}) + add_dependencies(liblldb-stage-header-${basename} lldb-sbapi-dwarf-enums) add_dependencies(liblldb-header-staging liblldb-stage-header-${basename}) add_custom_command( DEPENDS ${header} OUTPUT ${staged_header} @@ -373,6 +374,7 @@ foreach(header set(output_header $/Headers/${basename}) add_custom_target(lldb-framework-fixup-header-${basename} DEPENDS ${staged_header}) + add_dependencies(lldb-framework-fixup-header-${basename} liblldb-stage-header-${basename}) add_dependencies(lldb-framework-fixup-all-headers lldb-framework-fixup-header-${basename}) add_custom_command(TARGET lldb-framework-fixup-header-${basename} POST_BUILD From 170980a934cb0de11ca92179255ffc539c8e0a7b Mon Sep 17 00:00:00 2001 From: Chelsea Cassanova Date: Wed, 30 Jul 2025 23:04:22 -0700 Subject: [PATCH 7/8] [lldb][rpc] Only use guard names in framework script (#151391) Removes the U that comes before the guards passed into the framework fixup script. (cherry picked from commit cade1e29b2c9ab0d0ff4931231b6958ad04d1ebc) --- lldb/scripts/framework-header-fix.py | 2 +- lldb/test/Shell/Scripts/TestFrameworkFixScript.test | 2 +- lldb/test/Shell/Scripts/TestFrameworkFixUnifdef.test | 2 +- lldb/test/Shell/Scripts/TestRPCFrameworkFixScript.test | 2 +- lldb/tools/lldb-rpc/LLDBRPCHeaders.cmake | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lldb/scripts/framework-header-fix.py b/lldb/scripts/framework-header-fix.py index aa034db36968d..36c5c67c59d36 100755 --- a/lldb/scripts/framework-header-fix.py +++ b/lldb/scripts/framework-header-fix.py @@ -112,7 +112,7 @@ def main(): # but passing them in with dashes for this script causes argparse to think that they're # arguments in and of themself, so they need to passed in without dashes. if args.unifdef_guards: - unifdef_guards = ["-" + guard for guard in args.unifdef_guards] + unifdef_guards = ["-U" + guard for guard in args.unifdef_guards] # Create the framework's header dir if it doesn't already exist if not os.path.exists(os.path.dirname(output_file_path)): diff --git a/lldb/test/Shell/Scripts/TestFrameworkFixScript.test b/lldb/test/Shell/Scripts/TestFrameworkFixScript.test index 2b1818ef8d6c6..183ea3a80fe57 100644 --- a/lldb/test/Shell/Scripts/TestFrameworkFixScript.test +++ b/lldb/test/Shell/Scripts/TestFrameworkFixScript.test @@ -1,6 +1,6 @@ # Create a temp dir for output and run the framework fix script on the truncated version of SBAddress.h in the inputs dir. RUN: mkdir -p %t/Outputs -RUN: %python %p/../../../scripts/framework-header-fix.py -f lldb_main -i %p/Inputs/Main/SBAddress.h -o %t/Outputs/SBAddress.h -p /usr/bin/unifdef --unifdef_guards USWIG +RUN: %python %p/../../../scripts/framework-header-fix.py -f lldb_main -i %p/Inputs/Main/SBAddress.h -o %t/Outputs/SBAddress.h -p /usr/bin/unifdef --unifdef_guards SWIG # Check the output RUN: cat %t/Outputs/SBAddress.h | FileCheck %s diff --git a/lldb/test/Shell/Scripts/TestFrameworkFixUnifdef.test b/lldb/test/Shell/Scripts/TestFrameworkFixUnifdef.test index ba18b4b41d3a0..a4fffe4ed79a3 100644 --- a/lldb/test/Shell/Scripts/TestFrameworkFixUnifdef.test +++ b/lldb/test/Shell/Scripts/TestFrameworkFixUnifdef.test @@ -1,7 +1,7 @@ # REQUIRES: system-darwin # Create a temp dir for output and run the framework fix script on the truncated version of SBAddress.h in the inputs dir. RUN: mkdir -p %t/Outputs -RUN: %python %p/../../../scripts/framework-header-fix.py -f lldb_main -i %p/Inputs/Main/SBAddress.h -o %t/Outputs/SBAddress.h -p /usr/bin/unifdef --unifdef_guards USWIG +RUN: %python %p/../../../scripts/framework-header-fix.py -f lldb_main -i %p/Inputs/Main/SBAddress.h -o %t/Outputs/SBAddress.h -p /usr/bin/unifdef --unifdef_guards SWIG # Check the output RUN: cat %t/Outputs/SBAddress.h | FileCheck %s diff --git a/lldb/test/Shell/Scripts/TestRPCFrameworkFixScript.test b/lldb/test/Shell/Scripts/TestRPCFrameworkFixScript.test index e2080ca01a6fc..d7775c20bc447 100644 --- a/lldb/test/Shell/Scripts/TestRPCFrameworkFixScript.test +++ b/lldb/test/Shell/Scripts/TestRPCFrameworkFixScript.test @@ -1,6 +1,6 @@ # Create a temp dir for output and run the framework fix script on the truncated version of SBAddress.h in the inputs dir. RUN: mkdir -p %t/Outputs -RUN: %python %p/../../../scripts/framework-header-fix.py -f lldb_rpc -i %p/Inputs/RPC/RPCSBAddress.h -o %t/Outputs/RPCSBAddress.h -p /usr/bin/unifdef --unifdef_guards USWIG +RUN: %python %p/../../../scripts/framework-header-fix.py -f lldb_rpc -i %p/Inputs/RPC/RPCSBAddress.h -o %t/Outputs/RPCSBAddress.h -p /usr/bin/unifdef --unifdef_guards SWIG # Check the output RUN: cat %t/Outputs/RPCSBAddress.h | FileCheck %s diff --git a/lldb/tools/lldb-rpc/LLDBRPCHeaders.cmake b/lldb/tools/lldb-rpc/LLDBRPCHeaders.cmake index 6c363f411511a..2376e234cd832 100644 --- a/lldb/tools/lldb-rpc/LLDBRPCHeaders.cmake +++ b/lldb/tools/lldb-rpc/LLDBRPCHeaders.cmake @@ -79,7 +79,7 @@ function(FixIncludePaths in subfolder out) add_custom_command(OUTPUT ${parked_header} COMMAND ${LLDB_SOURCE_DIR}/scripts/framework-header-fix.py - -f lldb_rpc -i ${in} -o ${parked_header} -p ${unifdef_EXECUTABLE} --unifdef_guards USWIG + -f lldb_rpc -i ${in} -o ${parked_header} -p ${unifdef_EXECUTABLE} --unifdef_guards SWIG DEPENDS ${in} COMMENT "Fixing includes in ${in}" ) From c7fcb145ffdf4f3895b184e3a9aaec25982ef2d0 Mon Sep 17 00:00:00 2001 From: Chelsea Cassanova Date: Mon, 11 Aug 2025 11:56:03 -0700 Subject: [PATCH 8/8] [lldb][framework] Remove original framework fixup script (#153052) The original version of the framework fixup script is no longer being used and should be able to be removed. (cherry picked from commit 9815210db7a6b2a7e74640cf94ded9fbf46167dc) --- lldb/scripts/framework-header-fix.sh | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100755 lldb/scripts/framework-header-fix.sh diff --git a/lldb/scripts/framework-header-fix.sh b/lldb/scripts/framework-header-fix.sh deleted file mode 100755 index 345579c80cdf5..0000000000000 --- a/lldb/scripts/framework-header-fix.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh -# Usage: framework-header-fix.sh - -set -e - -for file in `find $1 -name "*.h"` -do - /usr/bin/sed -i.bak 's/\(#include\)[ ]*"lldb\/\(API\/\)\{0,1\}\(.*\)"/\1 /1' "$file" - /usr/bin/sed -i.bak 's|