https://github.com/chelcassanova updated 
https://github.com/llvm/llvm-project/pull/138031

>From e948fcf7a890d3ebcde5eb3586577099ba4ee2c0 Mon Sep 17 00:00:00 2001
From: Chelsea Cassanova <chelsea_cassan...@apple.com>
Date: Wed, 30 Apr 2025 14:15:39 -0700
Subject: [PATCH] [lldb][RPC] Upstream lldb-rpc-gen tool

This commit upstreams the `lldb-rpc-gen` tool, a ClangTool that
generates the LLDB RPC client and server interfaces.

https://discourse.llvm.org/t/rfc-upstreaming-lldb-rpc/85804
---
 .../Tests/CheckRPCGenToolByproducts.test      |  11 +
 lldb/test/Shell/helper/toolchain.py           |   6 +
 lldb/tools/CMakeLists.txt                     |   1 +
 lldb/tools/lldb-rpc/CMakeLists.txt            |  22 +
 lldb/tools/lldb-rpc/LLDBRPCGeneration.cmake   |  58 ++
 lldb/tools/lldb-rpc/LLDBRPCHeaders.cmake      | 101 ++++
 .../lldb-rpc/lldb-rpc-gen/CMakeLists.txt      |  21 +
 .../tools/lldb-rpc/lldb-rpc-gen/RPCCommon.cpp | 519 ++++++++++++++++++
 lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.h  | 108 ++++
 .../lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp    | 341 ++++++++++++
 10 files changed, 1188 insertions(+)
 create mode 100644 
lldb/test/Shell/RPC/Generator/Tests/CheckRPCGenToolByproducts.test
 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
 create mode 100644 lldb/tools/lldb-rpc/lldb-rpc-gen/CMakeLists.txt
 create mode 100644 lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.cpp
 create mode 100644 lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.h
 create mode 100644 lldb/tools/lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp

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..be3eefc69b20a
--- /dev/null
+++ b/lldb/test/Shell/RPC/Generator/Tests/CheckRPCGenToolByproducts.test
@@ -0,0 +1,11 @@
+# For this test, we're not checking any specific output from a generated file,
+# but we do need a file to pass into lldb-rpc-gen so use SBAddress.h from 
source.
+RUN: %lldb-rpc-gen --output-dir=%t 
%S/../../../../../include/lldb/API/SBAddress.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/helper/toolchain.py 
b/lldb/test/Shell/helper/toolchain.py
index 42968128f2702..159692487595e 100644
--- a/lldb/test/Shell/helper/toolchain.py
+++ b/lldb/test/Shell/helper/toolchain.py
@@ -156,6 +156,12 @@ def use_lldb_substitutions(config):
             extra_args=["platform"],
             unresolved="ignore",
         ),
+        ToolSubst(
+            "%lldb-rpc-gen",
+            command=FindTool("lldb-rpc-gen"),
+            extra_args=['--extra-arg="-resource-dir=' + 
config.clang_resource_dir + '"'],
+            unresolved="ignore",
+        ),
         "lldb-test",
         "lldb-dap",
         ToolSubst(
diff --git a/lldb/tools/CMakeLists.txt b/lldb/tools/CMakeLists.txt
index 6804dc234555b..47e994b9b8a7a 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-rpc)
 
 if (CMAKE_SYSTEM_NAME MATCHES "Darwin")
   add_lldb_tool_subdirectory(darwin-debug)
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..8864e7234fc98
--- /dev/null
+++ b/lldb/tools/lldb-rpc/LLDBRPCGeneration.cmake
@@ -0,0 +1,58 @@
+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")
+
+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
+)
+
+# 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 (LLDB_BUILT_STANDALONE)
+  if (TARGET clang-resource-headers)
+    set(clang_resource_headers_dir
+      $<TARGET_PROPERTY:clang-resource-headers,INTERFACE_INCLUDE_DIRECTORIES>)
+    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()
+endif()
+
+add_custom_command(OUTPUT ${lldb_rpc_gen_byproducts}
+  COMMAND ${CMAKE_COMMAND} -E make_directory
+    ${lldb_rpc_generated_dir}
+
+  COMMAND ${LLDB_RPC_GEN_EXE}
+    -p ${CMAKE_BINARY_DIR}
+    --output-dir=${lldb_rpc_generated_dir}
+    ${clang_resource_dir_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)
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
+)
diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/CMakeLists.txt 
b/lldb/tools/lldb-rpc/lldb-rpc-gen/CMakeLists.txt
new file mode 100644
index 0000000000000..0a7d80519c564
--- /dev/null
+++ b/lldb/tools/lldb-rpc/lldb-rpc-gen/CMakeLists.txt
@@ -0,0 +1,21 @@
+add_lldb_tool(lldb-rpc-gen
+    RPCCommon.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 $<TARGET_FILE:lldb-rpc-gen> CACHE STRING "Executable 
that generates lldb-rpc-server")
+endif()
diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.cpp 
b/lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.cpp
new file mode 100644
index 0000000000000..231eb614c50ba
--- /dev/null
+++ b/lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.cpp
@@ -0,0 +1,519 @@
+//===-- 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 <cstring>
+
+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 methods should take a connection parameter according to our logic in
+// RequiresConnectionParameter() but in the handwritten version they
+// don't take a connection. These methods need to have their implementation
+// changed but for now, we just have an exception list of functions that will
+// never be a given connection parameter.
+//
+// FIXME: Change the implementation of these methods so that they can be given 
a
+// connection parameter.
+static constexpr llvm::StringRef
+    MethodsThatUnconditionallyDoNotNeedConnection[] = {
+        "_ZN4lldb16SBBreakpointNameC1ERNS_8SBTargetEPKc",
+        "_ZN4lldb10SBDebugger7DestroyERS0_",
+        "_ZN4lldb18SBExecutionContextC1ENS_8SBThreadE",
+};
+
+// 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<CXXConstructorDecl>(MDecl))
+    GDecl = GlobalDecl(CtorDecl, Ctor_Complete);
+  else if (const auto *DtorDecl = dyn_cast<CXXDestructorDecl>(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<NamespaceDecl>(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<lldb_private::Debugger>".
+  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<ClassTemplateSpecializationDecl>(
+              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;
+
+  // FIXME: 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 **`.
+  return UnderlyingType->isSpecificBuiltinType(BuiltinType::Char_S) ||
+         UnderlyingType->isSpecificBuiltinType(BuiltinType::SChar);
+}
+
+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;
+}
+
+// FIXME: Find a better way to do this. 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<CXXConstructorDecl>(MDecl)),
+      IsCopyAssign(MDecl->isCopyAssignmentOperator()),
+      IsMoveAssign(MDecl->isMoveAssignmentOperator()),
+      IsDtor(isa<CXXDestructorDecl>(MDecl)),
+      IsConversionMethod(isa<CXXConversionDecl>(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<CXXConstructorDecl>(MDecl)) {
+    IsExplicitCtorOrConversionMethod = CtorDecl->isExplicit();
+    IsCopyCtor = CtorDecl->isCopyConstructor();
+    IsMoveCtor = CtorDecl->isMoveConstructor();
+  } else if (const auto *ConversionDecl = dyn_cast<CXXConversionDecl>(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<std::string> 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 (llvm::is_contained(MethodsThatUnconditionallyDoNotNeedConnection,
+                         MangledName)) {
+    return false;
+  }
+  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/lldb-rpc-gen/RPCCommon.h 
b/lldb/tools/lldb-rpc/lldb-rpc-gen/RPCCommon.h
new file mode 100644
index 0000000000000..edc03b4f81a3d
--- /dev/null
+++ b/lldb/tools/lldb-rpc/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 <string>
+
+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<Param> 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<llvm::ToolOutputFile> &&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<llvm::ToolOutputFile> 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/lldb-rpc-gen/lldb-rpc-gen.cpp
new file mode 100644
index 0000000000000..a38122bc30a32
--- /dev/null
+++ b/lldb/tools/lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp
@@ -0,0 +1,341 @@
+//===-- lldb-rpc-gen.cpp ----------------------------------------*- C++ 
-*-===//
+//
+// 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/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/CodeGen/ObjectFilePCHContainerWriter.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Frontend/FrontendActions.h"
+#include "clang/Serialization/ObjectFilePCHContainerReader.h"
+#include "clang/Tooling/CommonOptionsParser.h"
+#include "clang/Tooling/Tooling.h"
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace clang::driver;
+using namespace clang::tooling;
+
+static llvm::cl::OptionCategory RPCGenCategory("Tool for generating LLDBRPC");
+
+static llvm::cl::opt<std::string>
+    OutputDir("output-dir",
+              llvm::cl::desc("Directory to output generated files to"),
+              llvm::cl::init(""), llvm::cl::cat(RPCGenCategory));
+
+static std::unique_ptr<llvm::ToolOutputFile>
+CreateOutputFile(llvm::StringRef OutputDir, llvm::StringRef Filename) {
+  llvm::SmallString<128> Path(OutputDir);
+  llvm::sys::path::append(Path, Filename);
+
+  std::error_code EC;
+  auto OutputFile =
+      std::make_unique<llvm::ToolOutputFile>(Path, EC, llvm::sys::fs::OF_None);
+  if (EC) {
+    llvm::errs() << "Failed to create output file: " << Path << "!\n";
+    return nullptr;
+  }
+  return OutputFile;
+}
+
+struct GeneratedByproducts {
+  std::set<std::string> ClassNames;
+  std::set<std::string> MangledMethodNames;
+  std::set<std::string> SkippedMethodNames;
+};
+
+enum SupportLevel {
+  eUnsupported,
+  eUnimplemented,
+  eImplemented,
+};
+
+class SBVisitor : public RecursiveASTVisitor<SBVisitor> {
+public:
+  SBVisitor(GeneratedByproducts &Byproducts, SourceManager &Manager,
+            ASTContext &Context)
+      : Byproducts(Byproducts), Manager(Manager), Context(Context) {}
+
+  ~SBVisitor() {}
+
+  bool VisitCXXRecordDecl(CXXRecordDecl *RDecl) {
+    if (ShouldSkipRecord(RDecl))
+      return true;
+
+    const std::string ClassName = RDecl->getNameAsString();
+    Byproducts.ClassNames.insert(ClassName);
+
+    // Print 'bool' instead of '_Bool'.
+    PrintingPolicy Policy(Context.getLangOpts());
+    Policy.Bool = true;
+
+    for (CXXMethodDecl *MDecl : RDecl->methods()) {
+      const std::string MangledName =
+          lldb_rpc_gen::GetMangledName(Context, MDecl);
+      const bool IsDisallowed =
+          lldb_rpc_gen::MethodIsDisallowed(Context, MDecl);
+      SupportLevel MethodSupportLevel = GetMethodSupportLevel(MDecl);
+      if (MethodSupportLevel == eImplemented && !IsDisallowed) {
+        const lldb_rpc_gen::Method Method(MDecl, Policy, Context);
+        Byproducts.MangledMethodNames.insert(MangledName);
+      } else if (MethodSupportLevel == eUnimplemented)
+        Byproducts.SkippedMethodNames.insert(MangledName);
+    }
+    return true;
+  }
+
+private:
+  /// Determines whether we should skip a RecordDecl.
+  /// Conditions for skipping:
+  ///   - Anything not in the header itself
+  ///   - Certain inconvenient classes
+  ///   - Records without definitions (forward declarations)
+  bool ShouldSkipRecord(CXXRecordDecl *Decl) {
+    const Type *DeclType = Decl->getTypeForDecl();
+    QualType CanonicalType = DeclType->getCanonicalTypeInternal();
+    return !Manager.isInMainFile(Decl->getBeginLoc()) ||
+           !Decl->hasDefinition() || Decl->getDefinition() != Decl ||
+           lldb_rpc_gen::TypeIsDisallowedClass(CanonicalType);
+  }
+
+  /// Check the support level for a type
+  /// Known unsupported types:
+  ///  - FILE * (We do not want to expose this primitive)
+  ///  - Types that are internal to LLDB
+  SupportLevel GetTypeSupportLevel(QualType Type) {
+    const std::string TypeName = Type.getAsString();
+    if (TypeName == "FILE *" || lldb_rpc_gen::TypeIsFromLLDBPrivate(Type))
+      return eUnsupported;
+
+    if (lldb_rpc_gen::TypeIsDisallowedClass(Type))
+      return eUnsupported;
+
+    return eImplemented;
+  }
+
+  /// Determine the support level of a given method.
+  /// Known unsupported methods:
+  ///   - Non-public methods (lldb-rpc is a client and can only see public
+  ///     things)
+  ///   - Copy assignment operators (the client side will handle this)
+  ///   - Move assignment operators (the client side will handle this)
+  ///   - Methods involving unsupported types.
+  /// Known unimplemented methods:
+  ///   - No variadic functions, e.g. Printf
+  SupportLevel GetMethodSupportLevel(CXXMethodDecl *MDecl) {
+    AccessSpecifier AS = MDecl->getAccess();
+    if (AS != AccessSpecifier::AS_public)
+      return eUnsupported;
+    if (MDecl->isCopyAssignmentOperator())
+      return eUnsupported;
+    if (MDecl->isMoveAssignmentOperator())
+      return eUnsupported;
+
+    if (MDecl->isVariadic())
+      return eUnimplemented;
+
+    SupportLevel ReturnTypeLevel = GetTypeSupportLevel(MDecl->getReturnType());
+    if (ReturnTypeLevel != eImplemented)
+      return ReturnTypeLevel;
+
+    for (auto *ParamDecl : MDecl->parameters()) {
+      SupportLevel ParamTypeLevel = GetTypeSupportLevel(ParamDecl->getType());
+      if (ParamTypeLevel != eImplemented)
+        return ParamTypeLevel;
+    }
+
+    // FIXME: If a callback does not take a `void *baton` parameter, it is
+    // considered unsupported at this time. On the server-side, we hijack the
+    // baton argument in order to pass additional information to the 
server-side
+    // callback so we can correctly perform a reverse RPC call back to the
+    // client. Without this baton, we would need the server-side callback to
+    // have some side channel by which it obtained that information, and
+    // spending time designing that doesn't outweight the cost of doing it at
+    // the moment.
+    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;
+        continue;
+      }
+
+      // FIXME: We assume that if we have a function pointer and a void pointer
+      // together in the same parameter list, that it is not followed by a
+      // length argument. If that changes, we will need to revisit this
+      // implementation.
+      if ((*Iter)->getType()->isVoidPointerType())
+        HasBatonParameter = true;
+    }
+
+    if (HasCallbackParameter && !HasBatonParameter)
+      return eUnimplemented;
+
+    return eImplemented;
+  }
+
+  GeneratedByproducts &Byproducts;
+  SourceManager &Manager;
+  ASTContext &Context;
+};
+
+class SBConsumer : public ASTConsumer {
+public:
+  SBConsumer(GeneratedByproducts &Byproducts, SourceManager &Manager,
+             ASTContext &Context)
+      : Visitor(Byproducts, Manager, Context) {}
+  bool HandleTopLevelDecl(DeclGroupRef DR) override {
+    for (Decl *D : DR)
+      Visitor.TraverseDecl(D);
+
+    return true;
+  }
+
+private:
+  SBVisitor Visitor;
+};
+
+class SBAction : public ASTFrontendAction {
+public:
+  SBAction(GeneratedByproducts &Byproducts) : Byproducts(Byproducts) {}
+
+  std::unique_ptr<ASTConsumer>
+  CreateASTConsumer(CompilerInstance &CI, llvm::StringRef File) override {
+    llvm::StringRef FilenameNoExt =
+        llvm::sys::path::stem(llvm::sys::path::filename(File));
+
+    return std::make_unique<SBConsumer>(Byproducts, CI.getSourceManager(),
+                                        CI.getASTContext());
+  }
+
+private:
+  GeneratedByproducts &Byproducts;
+};
+
+class SBActionFactory : public FrontendActionFactory {
+public:
+  SBActionFactory(GeneratedByproducts &Byproducts) : Byproducts(Byproducts) {}
+
+  std::unique_ptr<FrontendAction> create() override {
+    return std::make_unique<SBAction>(Byproducts);
+  }
+
+private:
+  GeneratedByproducts &Byproducts;
+};
+
+bool EmitClassNamesFile(std::set<std::string> &ClassNames) {
+  static constexpr llvm::StringLiteral ClassNamesFileName = "SBClasses.def";
+  std::unique_ptr<llvm::ToolOutputFile> ClassNamesFile =
+      CreateOutputFile(OutputDir.getValue(), ClassNamesFileName);
+  if (!ClassNamesFile)
+    return false;
+
+  ClassNamesFile->os() << "#ifndef SBCLASS\n"
+                       << "#error \"SBClass must be defined\"\n"
+                       << "#endif\n";
+
+  for (const auto &ClassName : ClassNames) {
+    if (ClassName == "SBStream" || ClassName == "SBProgress")
+      ClassNamesFile->os() << "#if !defined(SBCLASS_EXCLUDE_NONCOPYABLE)\n";
+    else if (ClassName == "SBReproducer")
+      ClassNamesFile->os() << "#if !defined(SBCLASS_EXCLUDE_STATICONLY)\n";
+
+    ClassNamesFile->os() << "SBCLASS(" << ClassName << ")\n";
+    if (ClassName == "SBStream" || ClassName == "SBReproducer" ||
+        ClassName == "SBProgress")
+      ClassNamesFile->os() << "#endif\n";
+  }
+  ClassNamesFile->keep();
+  return true;
+}
+
+bool EmitMethodNamesFile(std::set<std::string> &MangledMethodNames) {
+  static constexpr llvm::StringLiteral MethodNamesFileName = "SBAPI.def";
+  std::unique_ptr<llvm::ToolOutputFile> MethodNamesFile =
+      CreateOutputFile(OutputDir.getValue(), MethodNamesFileName);
+  if (!MethodNamesFile)
+    return false;
+
+  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";
+  }
+  MethodNamesFile->keep();
+  return true;
+}
+
+bool EmitSkippedMethodsFile(std::set<std::string> &SkippedMethodNames) {
+  static constexpr llvm::StringLiteral FileName = "SkippedMethods.txt";
+  std::unique_ptr<llvm::ToolOutputFile> File =
+      CreateOutputFile(OutputDir.getValue(), FileName);
+  if (!File)
+    return false;
+
+  for (const auto &Skipped : SkippedMethodNames)
+    File->os() << Skipped << "\n";
+  File->keep();
+  return true;
+}
+
+int main(int argc, const char *argv[]) {
+  auto ExpectedParser = CommonOptionsParser::create(
+      argc, argv, RPCGenCategory, llvm::cl::OneOrMore,
+      "Tool for generating LLDBRPC interfaces and implementations");
+
+  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;
+  }
+
+  CommonOptionsParser &OP = ExpectedParser.get();
+  auto PCHOpts = std::make_shared<PCHContainerOperations>();
+  PCHOpts->registerWriter(std::make_unique<ObjectFilePCHContainerWriter>());
+  PCHOpts->registerReader(std::make_unique<ObjectFilePCHContainerReader>());
+
+  ClangTool T(OP.getCompilations(), OP.getSourcePathList(), PCHOpts);
+
+  GeneratedByproducts Byproducts;
+
+  SBActionFactory Factory(Byproducts);
+  auto Result = T.run(&Factory);
+  if (!EmitClassNamesFile(Byproducts.ClassNames)) {
+    llvm::errs() << "Failed to create SB Class file\n";
+    return 1;
+  }
+  if (!EmitMethodNamesFile(Byproducts.MangledMethodNames)) {
+    llvm::errs() << "Failed to create Method Names file\n";
+    return 1;
+  }
+  if (!EmitSkippedMethodsFile(Byproducts.SkippedMethodNames)) {
+    llvm::errs() << "Failed to create Skipped Methods file\n";
+    return 1;
+  }
+
+  return Result;
+}

_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to