Skip to content

Conversation

@chelcassanova
Copy link
Contributor

This commit upstreams the client side emitters for the lldb-rpc-gen tool alongside tests for its functionality and heuristics.

https://discourse.llvm.org/t/rfc-upstreaming-lldb-rpc/85804

@llvmbot
Copy link
Member

llvmbot commented Jul 9, 2025

@llvm/pr-subscribers-lldb

Author: Chelsea Cassanova (chelcassanova)

Changes

This commit upstreams the client side emitters for the lldb-rpc-gen tool alongside tests for its functionality and heuristics.

https://discourse.llvm.org/t/rfc-upstreaming-lldb-rpc/85804


Patch is 55.20 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/147655.diff

17 Files Affected:

  • (added) lldb/test/Shell/RPC/Generator/Inputs/Client/CheckArrayPointer.h (+20)
  • (added) lldb/test/Shell/RPC/Generator/Inputs/Client/CheckConstCharPtrPtrWithLen.h (+19)
  • (added) lldb/test/Shell/RPC/Generator/Inputs/Client/CheckConstSBRef.h (+19)
  • (added) lldb/test/Shell/RPC/Generator/Inputs/Client/CheckNonConstSBRef.h (+20)
  • (added) lldb/test/Shell/RPC/Generator/Inputs/Client/CheckSBPointer.h (+21)
  • (added) lldb/test/Shell/RPC/Generator/Inputs/Client/CheckVoidPtr.h (+19)
  • (added) lldb/test/Shell/RPC/Generator/Tests/Client/CheckArrayPointer.test (+11)
  • (added) lldb/test/Shell/RPC/Generator/Tests/Client/CheckConstCharPtrPtrWithLen.test (+10)
  • (added) lldb/test/Shell/RPC/Generator/Tests/Client/CheckConstSBRef.test (+14)
  • (added) lldb/test/Shell/RPC/Generator/Tests/Client/CheckNonConstSBRef.test (+13)
  • (added) lldb/test/Shell/RPC/Generator/Tests/Client/CheckSBPointer.test (+15)
  • (added) lldb/test/Shell/RPC/Generator/Tests/Client/CheckVoidPtr.test (+10)
  • (added) lldb/tools/lldb-rpc/lldb-rpc-gen/client/RPCLibraryHeaderEmitter.cpp (+124)
  • (added) lldb/tools/lldb-rpc/lldb-rpc-gen/client/RPCLibraryHeaderEmitter.h (+44)
  • (added) lldb/tools/lldb-rpc/lldb-rpc-gen/client/RPCLibrarySourceEmitter.cpp (+542)
  • (added) lldb/tools/lldb-rpc/lldb-rpc-gen/client/RPCLibrarySourceEmitter.h (+92)
  • (added) lldb/tools/lldb-rpc/lldb-rpc-gen/lldb-rpc-gen.cpp (+414)
diff --git a/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckArrayPointer.h b/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckArrayPointer.h
new file mode 100644
index 0000000000000..18ec92a976b8a
--- /dev/null
+++ b/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckArrayPointer.h
@@ -0,0 +1,20 @@
+#ifndef LLDB_API_SBRPC_CHECKARRAYPTR_H
+#define LLDB_API_SBRPC_CHECKARRAYPTR_H
+
+#include <cstddef>
+#include <cstdio>
+
+#include "lldb/API/SBDefines.h"
+
+namespace lldb {
+class LLDB_API SBRPC_CHECKARRAYPTR {
+public:
+  // Pointers to arrays followed by length must use a
+  // Bytes object constructed using that pointer and the sizeof()
+  // the array object.
+  int CheckArrayPtr(uint64_t *array, size_t array_len);
+
+}; // class SBRPC_CHECKARRAYPTR
+} // namespace lldb
+
+#endif // LLDB_API_SBRPC_CHECKARRAYPTR_H
diff --git a/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckConstCharPtrPtrWithLen.h b/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckConstCharPtrPtrWithLen.h
new file mode 100644
index 0000000000000..fa64492cf5aa7
--- /dev/null
+++ b/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckConstCharPtrPtrWithLen.h
@@ -0,0 +1,19 @@
+#ifndef LLDB_API_SBRPC_CHECKCONSTCHARPTRPTRWITHLEN_H
+#define LLDB_API_SBRPC_CHECKCONSTCHARPTRPTRWITHLEN_H
+
+#include <cstddef>
+#include <cstdio>
+
+#include "lldb/API/SBDefines.h"
+
+namespace lldb {
+class LLDB_API SBRPC_CHECKCONSTCHARPTRPTRWITHLEN {
+public:
+  // const char ** followed by len must use a StringList
+  // when being encoded.
+  int CheckConstCharPtrPtrWithLen(const char **arg1, size_t len);
+
+}; // class SBRPC_CHECKCONSTCHARPTRPTRWITHLEN
+} // namespace lldb
+
+#endif // LLDB_API_SBRPC_CHECKCONSTCHARPTRPTRWITHLEN_H
diff --git a/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckConstSBRef.h b/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckConstSBRef.h
new file mode 100644
index 0000000000000..153fdad60a494
--- /dev/null
+++ b/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckConstSBRef.h
@@ -0,0 +1,19 @@
+#ifndef LLDB_API_SBRPC_CHECKCONSTSBREF_H
+#define LLDB_API_SBRPC_CHECKCONSTSBREF_H
+
+#include <cstddef>
+#include <cstdio>
+
+#include "lldb/API/SBDefines.h"
+
+namespace lldb {
+class LLDB_API SBRPC_CHECKCONSTSBREF {
+public:
+  // Const references to SB classes should be encoded as usual without
+  // needing to create a new object with its own connection.
+  int CheckConstSBRef(const SBDebugger &debugger_ref);
+
+}; // class SBRPC_CHECKCONSTSBREF
+} // namespace lldb
+
+#endif // LLDB_API_SBRPC_CHECKCONSTSBREF_H
diff --git a/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckNonConstSBRef.h b/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckNonConstSBRef.h
new file mode 100644
index 0000000000000..90ee3d3a7fbd6
--- /dev/null
+++ b/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckNonConstSBRef.h
@@ -0,0 +1,20 @@
+#ifndef LLDB_API_SBRPC_CHECKNONCONSTSBREF_H
+#define LLDB_API_SBRPC_CHECKNONCONSTSBREF_H
+
+#include <cstddef>
+#include <cstdio>
+
+#include "lldb/API/SBDefines.h"
+
+namespace lldb {
+class LLDB_API SBRPC_CHECKNONCONSTSBREF {
+public:
+  // Non-const references to SB classes will have new objects
+  // of that class constructed with the connection as the first parameter
+  // before being encoded if their existing connection is invalid.
+  int CheckNonConstSBRef(SBDebugger &debugger_ref);
+
+}; // class SBRPC_CHECKNONCONSTSBREF
+} // namespace lldb
+
+#endif // LLDB_API_SBRPC_CHECKNONCONSTSBREF_H
diff --git a/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckSBPointer.h b/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckSBPointer.h
new file mode 100644
index 0000000000000..d9b7efa820f53
--- /dev/null
+++ b/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckSBPointer.h
@@ -0,0 +1,21 @@
+#ifndef LLDB_API_SBRPC_CHECKSBPTR_H
+#define LLDB_API_SBRPC_CHECKSBPTR_H
+
+#include <cstddef>
+#include <cstdio>
+
+#include "lldb/API/SBDefines.h"
+
+namespace lldb {
+class LLDB_API SBRPC_CHECKSBPTR {
+public:
+  // Pointers to SB objects must be checked to
+  // see if they're null. If so, then a new object of the given
+  // class must be created and encoded. Otherwise, the original
+  // parameter will be encoded.
+  int CheckSBPtr(SBDebugger *debugger_ptr);
+
+}; // class SBRPC_CHECKSBPTR
+} // namespace lldb
+
+#endif // LLDB_API_SBRPC_CHECKSBPTR_H
diff --git a/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckVoidPtr.h b/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckVoidPtr.h
new file mode 100644
index 0000000000000..fa6484fc8d7cd
--- /dev/null
+++ b/lldb/test/Shell/RPC/Generator/Inputs/Client/CheckVoidPtr.h
@@ -0,0 +1,19 @@
+#ifndef LLDB_API_SBRPC_CHECKVOIDPTR_H
+#define LLDB_API_SBRPC_CHECKVOIDPTR_H
+
+#include <cstddef>
+#include <cstdio>
+
+#include "lldb/API/SBDefines.h"
+
+namespace lldb {
+class LLDB_API SBRPC_CHECKVOIDPTR {
+public:
+  // void * followed by length must use a Bytes object
+  // when being encoded.
+  int CheckVoidPtr(void *buf, size_t len);
+
+}; // class SBRPC_CHECKVOIDPTR
+} // namespace lldb
+
+#endif // LLDB_API_SBRPC_CHECKVOIDPTR_H
diff --git a/lldb/test/Shell/RPC/Generator/Tests/Client/CheckArrayPointer.test b/lldb/test/Shell/RPC/Generator/Tests/Client/CheckArrayPointer.test
new file mode 100644
index 0000000000000..1b58b389d612e
--- /dev/null
+++ b/lldb/test/Shell/RPC/Generator/Tests/Client/CheckArrayPointer.test
@@ -0,0 +1,11 @@
+RUN: mkdir -p %t/server
+RUN: mkdir -p %t/lib
+RUN: %lldb-rpc-gen --output-dir=%t %S/../../Inputs/CheckArrayPointer.h
+
+RUN: cat %t/lib/CheckArrayPointer.cpp | FileCheck %s
+
+# Pointers to arrays followed by length must use a
+# Bytes object constructed using that pointer and the sizeof()
+# the array object.
+CHECK: lldb_rpc::SBRPC_CHECKARRAYPTR::CheckArrayPtr
+CHECK: Bytes array_buffer(array, sizeof(uint64_t));
diff --git a/lldb/test/Shell/RPC/Generator/Tests/Client/CheckConstCharPtrPtrWithLen.test b/lldb/test/Shell/RPC/Generator/Tests/Client/CheckConstCharPtrPtrWithLen.test
new file mode 100644
index 0000000000000..84d18c95fa7d2
--- /dev/null
+++ b/lldb/test/Shell/RPC/Generator/Tests/Client/CheckConstCharPtrPtrWithLen.test
@@ -0,0 +1,10 @@
+RUN: mkdir -p %t/server
+RUN: mkdir -p %t/lib
+RUN: %lldb-rpc-gen --output-dir=%t %S/../../Inputs/CheckConstCharPtrPtrWithLen.h
+
+RUN: cat %t/lib/CheckConstCharPtrPtrWithLen.cpp | FileCheck %s
+
+# const char ** followed by len must use a StringList
+# when being encoded.
+CHECK: lldb_rpc::SBRPC_CHECKCONSTCHARPTRPTRWITHLEN::CheckConstCharPtrPtrWithLen
+CHECK: StringList arg1_list
diff --git a/lldb/test/Shell/RPC/Generator/Tests/Client/CheckConstSBRef.test b/lldb/test/Shell/RPC/Generator/Tests/Client/CheckConstSBRef.test
new file mode 100644
index 0000000000000..9b10d1711a9bc
--- /dev/null
+++ b/lldb/test/Shell/RPC/Generator/Tests/Client/CheckConstSBRef.test
@@ -0,0 +1,14 @@
+RUN: mkdir -p %t/server
+RUN: mkdir -p %t/lib
+RUN: %lldb-rpc-gen --output-dir=%t %S/../../Inputs/CheckConstSBRef.h
+
+RUN: cat %t/lib/CheckConstSBRef.cpp | FileCheck %s
+
+# Const references to SB classes should be encoded as usual without
+# needing to create a new object with its own connection. Here
+# we're checking to make sure that the given SB object ref will get
+# encoded immediately after the previous argument gets encoded without
+# anything happening in between.
+CHECK: lldb_rpc::SBRPC_CHECKCONSTSBREF::CheckConstSBRef
+CHECK: RPCValueEncoder(send, rpc_common::RPCPacket::ValueType::Argument, *this);
+CHECK: RPCValueEncoder(send, rpc_common::RPCPacket::ValueType::Argument, debugger_ref)
diff --git a/lldb/test/Shell/RPC/Generator/Tests/Client/CheckNonConstSBRef.test b/lldb/test/Shell/RPC/Generator/Tests/Client/CheckNonConstSBRef.test
new file mode 100644
index 0000000000000..d396e927b46d2
--- /dev/null
+++ b/lldb/test/Shell/RPC/Generator/Tests/Client/CheckNonConstSBRef.test
@@ -0,0 +1,13 @@
+RUN: mkdir -p %t/server
+RUN: mkdir -p %t/lib
+RUN: %lldb-rpc-gen --output-dir=%t %S/../../Inputs/CheckNonConstSBRef.h
+
+RUN: cat %t/lib/CheckNonConstSBRef.cpp | FileCheck %s
+
+# Non-const references to SB classes will have new objects
+# of that class constructed with the connection as the first parameter
+# before being encoded if their existing connection is invalid.
+CHECK: lldb_rpc::SBRPC_CHECKNONCONSTSBREF::CheckNonConstSBRef
+CHECK: if (connection_sp && !debugger_ref.ObjectRefIsValid())
+CHECK:     debugger_ref = lldb_rpc::SBDebugger(connection_sp);
+CHECK:   RPCValueEncoder(send, rpc_common::RPCPacket::ValueType::Argument, debugger_ref);
diff --git a/lldb/test/Shell/RPC/Generator/Tests/Client/CheckSBPointer.test b/lldb/test/Shell/RPC/Generator/Tests/Client/CheckSBPointer.test
new file mode 100644
index 0000000000000..5f355138182a8
--- /dev/null
+++ b/lldb/test/Shell/RPC/Generator/Tests/Client/CheckSBPointer.test
@@ -0,0 +1,15 @@
+RUN: mkdir -p %t/server
+RUN: mkdir -p %t/lib
+RUN: %lldb-rpc-gen --output-dir=%t %S/../../Inputs/CheckSBPointer.h
+
+RUN: cat %t/lib/CheckSBPointer.cpp | FileCheck %s
+
+# Pointers to SB objects must be checked to
+# see if they're null. If so, then a new object of the given
+# class must be created and encoded. Otherwise, the original
+# parameter will be encoded.
+CHECK: lldb_rpc::SBRPC_CHECKSBPTR::CheckSBPtr
+CHECK: if (debugger_ptr)
+CHECK:   RPCValueEncoder(send, rpc_common::RPCPacket::ValueType::Argument, *debugger_ptr);
+CHECK: else
+CHECK:    RPCValueEncoder(send, rpc_common::RPCPacket::ValueType::Argument, rpc::ObjectRef(ObjectRefGetConnectionID(), eClass_lldb_SBDebugger, LLDB_RPC_INVALID_OBJECT_ID));
diff --git a/lldb/test/Shell/RPC/Generator/Tests/Client/CheckVoidPtr.test b/lldb/test/Shell/RPC/Generator/Tests/Client/CheckVoidPtr.test
new file mode 100644
index 0000000000000..9907126a28fa6
--- /dev/null
+++ b/lldb/test/Shell/RPC/Generator/Tests/Client/CheckVoidPtr.test
@@ -0,0 +1,10 @@
+RUN: mkdir -p %t/server
+RUN: mkdir -p %t/lib
+RUN: %lldb-rpc-gen --output-dir=%t %S/../../Inputs/CheckVoidPtr.h
+
+RUN: cat %t/lib/CheckVoidPtr.cpp | FileCheck %s
+
+# void * followed by length must use a Bytes object
+# when being encoded.
+CHECK: lldb_rpc::SBRPC_CHECKVOIDPTR::CheckVoidPtr
+CHECK: Bytes buf_buffer(buf, len);
diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/client/RPCLibraryHeaderEmitter.cpp b/lldb/tools/lldb-rpc/lldb-rpc-gen/client/RPCLibraryHeaderEmitter.cpp
new file mode 100644
index 0000000000000..9537b490856f1
--- /dev/null
+++ b/lldb/tools/lldb-rpc/lldb-rpc-gen/client/RPCLibraryHeaderEmitter.cpp
@@ -0,0 +1,124 @@
+#include "RPCLibraryHeaderEmitter.h"
+#include "RPCCommon.h"
+
+#include "clang/AST/AST.h"
+#include "clang/AST/Mangle.h"
+#include "clang/Frontend/CompilerInstance.h"
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace lldb_rpc_gen;
+
+void RPCLibraryHeaderEmitter::StartClass(std::string ClassName) {
+  CurrentClass = std::move(ClassName);
+  std::string BaseClass =
+      lldb_rpc_gen::SBClassInheritsFromObjectRef(CurrentClass)
+          ? "ObjectRef"
+          : "LocalObjectRef";
+  EmitLine("class " + CurrentClass + " : public rpc::" + BaseClass + " {");
+  EmitLine("public:");
+  IndentLevel++;
+  if (lldb_rpc_gen::SBClassRequiresDefaultCtor(CurrentClass))
+    EmitLine(CurrentClass + "();");
+
+  // NOTE: There's currently only one RPC-specific extension that is actually
+  // used AFAICT. We can generalize this if we need more.
+  if (CurrentClass == "SBDebugger")
+    EmitLine("int SetIOFile(const char *path);");
+}
+
+void RPCLibraryHeaderEmitter::EndClass() {
+  if (lldb_rpc_gen::SBClassRequiresCopyCtorAssign(CurrentClass)) {
+    if (!CopyCtorEmitted)
+      EmitLine(CurrentClass + "(const lldb_rpc::" + CurrentClass + " &rhs);");
+    if (!CopyAssignEmitted)
+      EmitLine(CurrentClass + " &operator=(const lldb_rpc::" + CurrentClass +
+               " &rhs);");
+  }
+  if (!MoveCtorEmitted)
+    EmitLine(CurrentClass + "(lldb_rpc::" + CurrentClass + " &&rhs);");
+  if (!MoveAssignEmitted)
+    EmitLine(CurrentClass + " &operator=(" + CurrentClass + " &&rhs);");
+
+  IndentLevel--;
+  EmitLine("}; // class " + CurrentClass);
+  CurrentClass.clear();
+}
+
+void RPCLibraryHeaderEmitter::EmitMethod(const Method &method) {
+  std::string DeclarationLine;
+  llvm::raw_string_ostream DeclarationLineStream(DeclarationLine);
+
+  if (method.IsCopyCtor)
+    CopyCtorEmitted = true;
+  else if (method.IsCopyAssign)
+    CopyAssignEmitted = true;
+  else if (method.IsMoveCtor)
+    MoveCtorEmitted = true;
+  else if (method.IsMoveAssign)
+    MoveAssignEmitted = true;
+
+  if (method.IsExplicitCtorOrConversionMethod)
+    DeclarationLineStream << "explicit ";
+  else if (!method.IsInstance)
+    DeclarationLineStream << "static ";
+
+  if (!method.IsDtor && !method.IsConversionMethod && !method.IsCtor)
+    DeclarationLineStream << lldb_rpc_gen::ReplaceLLDBNamespaceWithRPCNamespace(
+                                 method.ReturnType.getAsString(method.Policy))
+                          << " ";
+  DeclarationLineStream << method.BaseName << "("
+                        << method.CreateParamListAsString(
+                               eLibrary, /*IncludeDefaultValue = */ true)
+                        << ")";
+  if (method.IsConst)
+    DeclarationLineStream << " const";
+  DeclarationLineStream << ";";
+
+  EmitLine(DeclarationLine);
+}
+
+void RPCLibraryHeaderEmitter::EmitEnum(EnumDecl *E) {
+  // NOTE: All of the enumerations embedded in SB classes are currently
+  // anonymous and backed by an unsigned int.
+  EmitLine("enum : unsigned {");
+  IndentLevel++;
+  for (const EnumConstantDecl *EC : E->enumerators()) {
+    std::string EnumValue = EC->getNameAsString();
+    SmallString<16> ValueStr;
+    EC->getInitVal().toString(ValueStr);
+    EnumValue += " = " + ValueStr.str().str() + ", ";
+    EmitLine(EnumValue);
+  }
+
+  IndentLevel--;
+  EmitLine("};");
+}
+
+std::string RPCLibraryHeaderEmitter::GetHeaderGuard() {
+  const std::string UpperFilenameNoExt =
+      llvm::sys::path::stem(
+          llvm::sys::path::filename(OutputFile->getFilename()))
+          .upper();
+  return "GENERATED_LLDB_RPC_LIBRARY_" + UpperFilenameNoExt + "_H";
+}
+
+void RPCLibraryHeaderEmitter::Begin() {
+  const std::string HeaderGuard = GetHeaderGuard();
+  EmitLine("#ifndef " + HeaderGuard);
+  EmitLine("#define " + HeaderGuard);
+  EmitLine("");
+  EmitLine("#include <lldb-rpc/common/RPCPublic.h>");
+  EmitLine("#include \"SBDefines.h\"");
+  EmitLine("#include \"LLDBRPC.h\"");
+  EmitLine("");
+  EmitLine("namespace lldb_rpc {");
+}
+
+void RPCLibraryHeaderEmitter::End() {
+  EmitLine("} // namespace lldb_rpc");
+  EmitLine("#endif // " + GetHeaderGuard());
+}
diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/client/RPCLibraryHeaderEmitter.h b/lldb/tools/lldb-rpc/lldb-rpc-gen/client/RPCLibraryHeaderEmitter.h
new file mode 100644
index 0000000000000..b9f22585c7955
--- /dev/null
+++ b/lldb/tools/lldb-rpc/lldb-rpc-gen/client/RPCLibraryHeaderEmitter.h
@@ -0,0 +1,44 @@
+#ifndef LLDB_RPC_GEN_RPCLIBRARYHEADEREMITTER_H
+#define LLDB_RPC_GEN_RPCLIBRARYHEADEREMITTER_H
+
+#include "RPCCommon.h"
+
+#include "clang/AST/AST.h"
+#include "llvm/Support/ToolOutputFile.h"
+
+using namespace clang;
+
+namespace lldb_rpc_gen {
+
+class RPCLibraryHeaderEmitter : public FileEmitter {
+public:
+  RPCLibraryHeaderEmitter(std::unique_ptr<llvm::ToolOutputFile> &&OutputFile)
+      : FileEmitter(std::move(OutputFile)), CurrentClass() {
+    Begin();
+  }
+
+  ~RPCLibraryHeaderEmitter() { End(); }
+
+  void StartClass(std::string ClassName);
+
+  void EndClass();
+
+  void EmitMethod(const Method &method);
+
+  void EmitEnum(EnumDecl *E);
+
+private:
+  std::string GetHeaderGuard();
+
+  void Begin();
+
+  void End();
+
+  std::string CurrentClass;
+  bool CopyCtorEmitted = false;
+  bool CopyAssignEmitted = false;
+  bool MoveCtorEmitted = false;
+  bool MoveAssignEmitted = false;
+};
+} // namespace lldb_rpc_gen
+#endif // LLDB_RPC_GEN_RPCLIBRARYHEADEREMITTER_H
diff --git a/lldb/tools/lldb-rpc/lldb-rpc-gen/client/RPCLibrarySourceEmitter.cpp b/lldb/tools/lldb-rpc/lldb-rpc-gen/client/RPCLibrarySourceEmitter.cpp
new file mode 100644
index 0000000000000..571fedc6edcce
--- /dev/null
+++ b/lldb/tools/lldb-rpc/lldb-rpc-gen/client/RPCLibrarySourceEmitter.cpp
@@ -0,0 +1,542 @@
+#include "RPCLibrarySourceEmitter.h"
+#include "RPCCommon.h"
+
+#include "clang/AST/AST.h"
+#include "clang/Frontend/CompilerInstance.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/raw_ostream.h"
+#include <string>
+
+using namespace clang;
+using namespace lldb_rpc_gen;
+
+static constexpr llvm::StringRef ReturnVariableName("__result");
+
+// This map stores any method that needs custom logic with a struct that
+// tells us where the logic needs to be inserted and what code needs to be
+// inserted. The code here is stored as a raw string literal.
+const llvm::StringMap<RPCLibrarySourceEmitter::CustomLogic>
+    CustomLogicForMethods = {
+        {"_ZN4lldb10SBDebugger6CreateEbPFvPKcPvES3_",
+         {RPCLibrarySourceEmitter::CustomLogicLocation::eAfterDecode, R"code(
+    // Now source the .lldbinit files manually since we can't rely on the
+    // LLDB.framework on the other side to have special support for sourcing the right file
+    // since it would try to source "~/.lldbinit-lldb-rpc-server" followed by
+    // "~/.lldbinit". We want it to try "~.lldbinit-%s" where %s is the
+    // current program basename followed by "~/.lldbinit".
+
+    if (source_init_files && __result.ObjectRefIsValid()) {
+      const char *program_basename = rpc::GetProgramBasename();
+      if (program_basename) {
+        char init_path[PATH_MAX];
+        snprintf(init_path, sizeof(init_path), "~/.lldbinit-%s",
+                 program_basename);
+        lldb_rpc::SBFileSpec program_init_file(connection, init_path, true);
+        if (program_init_file.Exists()) {
+          char command_str[PATH_MAX];
+          snprintf(command_str, sizeof(command_str),
+                   "command source -s 1 -c 1 -e 0 '%s'", init_path);
+          __result.HandleCommand(command_str);
+        } else {
+          __result.HandleCommand("command source -s 1 -c 1 -e 0 '~/.lldbinit'");
+        }
+      }
+    })code"}},
+};
+
+static std::string GetLocalObjectRefCtor(const std::string &ClassName) {
+  return "rpc::LocalObjectRef(LLDB_RPC_INVALID_CONNECTION_ID, eClass_lldb_" +
+         ClassName + ", LLDB_RPC_INVALID_OBJECT_ID)";
+}
+
+static std::string GetObjectRefCtor(const std::string &ClassName) {
+  return "rpc::ObjectRef(LLDB_RPC_INVALID_CONNECTION_ID, eClass_lldb_" +
+         ClassName + ", LLDB_RPC_INVALID_OBJECT_ID)";
+}
+
+void RPCLibrarySourceEmitter::EmitCopyCtor() {
+  EmitLine("lldb_rpc::" + CurrentClass + "::" + CurrentClass +
+           "(const lldb_rpc::" + CurrentClass + " &rhs) = default;");
+}
+
+void RPCLibrarySourceEmitter::EmitCopyAssign() {
+  EmitLine("lldb_rpc::" + CurrentClass + " &lldb_rpc::" + CurrentClass +
+           "::operator=(const lldb_rpc::" + CurrentClass + " &rhs) = default;");
+}
+
+void RPCLibrarySourceEmitter::EmitMoveCtor() {
+  EmitLine("lldb_rpc::" + CurrentClass + "::" + CurrentClass +
+           "(lldb_rpc::" + CurrentClass + " &&rhs) = default;");
+}
+
+void RPCLibrarySourceEmitter::EmitMoveAssign() {
+  EmitLine("lldb_rpc::" + CurrentClass + " &lldb_rpc::" + CurrentClass +
+           "::operator=(lldb_rpc::" + CurrentClass + " &&rhs) = default;");
+}
+
+void RPCLibrarySourceEmitter::EmitMethod(const Method &method) {
+  if (method.IsCopyCtor) {
+    CopyCtorEmitted = true;
+    EmitCopyCtor();
+    return;
+  } else if (method.IsCopyAssign) {
+    CopyAssignEmitted = true;
+    EmitCopyAssign();
+    return;
+  } else if (method.IsMoveCtor) {
+    MoveCtorEmitted = true;
+    EmitMoveCtor();
+    return;
+  } else if (method.IsMoveAssign) {
+    MoveAssignEmitted = true;
+    EmitMoveAssign();
+    return;
+  }
+
+  EmitCommentHeader(method);
+  EmitFunctionHeader(method);
+  EmitFunctionBody(method);
+  EmitFunctionFooter();
+}
+
+void RPCLibrarySourceEmitter::StartClass(std::string ClassName) {
+  CurrentClass = std::move(ClassName);
+  if (lldb_rpc_gen::SBClassRequiresDefaultCtor(CurrentClass)) {
+    std::string BaseClassCtor =
+        lldb_rpc_gen::SBClassInheritsFromObjectRef(CurrentClass)
+            ? GetObjectRefCtor(CurrentClass)
+            : GetLocalObjectRefCtor(CurrentClass);
+    EmitLine("lldb_rpc...
[truncated]

@chelcassanova chelcassanova force-pushed the upstream-lldb-rpc/add-lldb-rpc-client-emitters branch from cbcab91 to 899439d Compare July 9, 2025 16:17
@chelcassanova chelcassanova changed the title [DRAFT][lldb][rpc] Upstream RPC Client Library Emitters [lldb][rpc] Upstream RPC Client Library Emitters Jul 15, 2025
@chelcassanova
Copy link
Contributor Author

I've undrafted this PR and updated the design doc to reflect what the client-side emitters are doing here.

@DavidSpickett Would you able to take a look at this PR and the updated design doc when you can?

@chelcassanova chelcassanova force-pushed the upstream-lldb-rpc/add-lldb-rpc-client-emitters branch from 899439d to 3923ba0 Compare July 18, 2025 07:25
@github-actions
Copy link

github-actions bot commented Jul 18, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@chelcassanova chelcassanova force-pushed the upstream-lldb-rpc/add-lldb-rpc-client-emitters branch 5 times, most recently from 9b0772d to cac3268 Compare July 24, 2025 23:44
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is still link still needed ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this can be removed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I was wondering if we should make the template argument here be PATH_MAX but that's only defined on unix platforms, on windows is another macro called MAX_PATH which is hard coded to 260 I believe. May be we should go with that to match the smallest path length.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

which is hard coded to 260 I believe. May be we should go with that to match the smallest path length.

This can be done, especially since the server output dir path is 256.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what "Amalgamated" means here, I'd like a more self-explaining name or a least a comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For RPC, the amalgamated library header is LLDBRPC.h, similar to LLDB.h. I can add a comment here.

@medismailben
Copy link
Member

Looks like there is a linking issue on Linux:

2025-07-24T23:47:48.0834355Z [6326/6332] Linking CXX executable bin/lldb-rpc-gen
2025-07-24T23:47:48.0834814Z FAILED: bin/lldb-rpc-gen 
2025-07-24T23:47:48.0852723Z : && /opt/llvm/bin/clang++ -gmlt -fPIC -fno-semantic-interposition -fvisibility-inlines-hidden -Werror=date-time -Werror=unguarded-availability-new -Wall -Wextra -Wno-unused-parameter -Wwrite-strings -Wcast-qual -Wmissing-field-initializers -pedantic -Wno-long-long -Wc++98-compat-extra-semi -Wimplicit-fallthrough -Wcovered-switch-default -Wno-noexcept-type -Wnon-virtual-dtor -Wdelete-non-virtual-dtor -Wsuggest-override -Wstring-conversion -Wmisleading-indentation -Wctad-maybe-unsupported -fdiagnostics-color -ffunction-sections -fdata-sections -Wno-unknown-pragmas -Wno-strict-aliasing -Wno-vla-extension -O3 -DNDEBUG -fuse-ld=lld -Wl,--color-diagnostics    -Wl,--gc-sections tools/lldb/tools/lldb-rpc-gen/CMakeFiles/lldb-rpc-gen.dir/RPCCommon.cpp.o tools/lldb/tools/lldb-rpc-gen/CMakeFiles/lldb-rpc-gen.dir/server/RPCServerHeaderEmitter.cpp.o tools/lldb/tools/lldb-rpc-gen/CMakeFiles/lldb-rpc-gen.dir/server/RPCServerSourceEmitter.cpp.o tools/lldb/tools/lldb-rpc-gen/CMakeFiles/lldb-rpc-gen.dir/lldb-rpc-gen.cpp.o -o bin/lldb-rpc-gen  -Wl,-rpath,"\$ORIGIN/../lib:"  lib/libLLVMSupport.a  lib/libclangAST.a  lib/libclangBasic.a  lib/libclangCodeGen.a  lib/libclangFrontend.a  lib/libclangLex.a  lib/libclangRewrite.a  lib/libclangSerialization.a  lib/libclangTooling.a  lib/libLLVMCoverage.a  lib/libLLVMFrontendDriver.a  lib/libLLVMLTO.a  lib/libLLVMExtensions.a  lib/libLLVMPasses.a  lib/libLLVMCoroutines.a  lib/libLLVMHipStdPar.a  lib/libLLVMipo.a  lib/libLLVMLinker.a  lib/libLLVMIRPrinter.a  lib/libLLVMInstrumentation.a  lib/libLLVMVectorize.a  lib/libLLVMSandboxIR.a  lib/libLLVMCFGuard.a  lib/libLLVMGlobalISel.a  lib/libLLVMSelectionDAG.a  lib/libLLVMCodeGen.a  lib/libLLVMCodeGenTypes.a  lib/libLLVMObjCARCOpts.a  lib/libLLVMCGData.a  lib/libLLVMBitWriter.a  lib/libLLVMTarget.a  lib/libclangFrontend.a  lib/libclangParse.a  lib/libclangSerialization.a  lib/libclangSema.a  lib/libclangAnalysis.a  lib/libclangAPINotes.a  lib/libclangEdit.a  lib/libclangSupport.a  lib/libclangDriver.a  lib/libLLVMWindowsDriver.a  lib/libLLVMOption.a  lib/libclangASTMatchers.a  lib/libclangAST.a  lib/libLLVMFrontendHLSL.a  lib/libclangFormat.a  lib/libclangToolingInclusions.a  lib/libclangToolingCore.a  lib/libclangRewrite.a  lib/libclangLex.a  lib/libclangBasic.a  lib/libLLVMFrontendOpenMP.a  lib/libLLVMFrontendOffloading.a  lib/libLLVMObjectYAML.a  lib/libLLVMScalarOpts.a  lib/libLLVMAggressiveInstCombine.a  lib/libLLVMInstCombine.a  lib/libLLVMTransformUtils.a  lib/libLLVMFrontendAtomic.a  lib/libLLVMAnalysis.a  lib/libLLVMProfileData.a  lib/libLLVMSymbolize.a  lib/libLLVMDebugInfoGSYM.a  lib/libLLVMDebugInfoPDB.a  lib/libLLVMDebugInfoCodeView.a  lib/libLLVMDebugInfoMSF.a  lib/libLLVMDebugInfoBTF.a  lib/libLLVMDebugInfoDWARF.a  lib/libLLVMObject.a  lib/libLLVMIRReader.a  lib/libLLVMBitReader.a  lib/libLLVMAsmParser.a  lib/libLLVMCore.a  lib/libLLVMRemarks.a  lib/libLLVMBitstreamReader.a  lib/libLLVMMCParser.a  lib/libLLVMMC.a  lib/libLLVMTextAPI.a  lib/libLLVMDebugInfoDWARFLowLevel.a  lib/libLLVMBinaryFormat.a  lib/libLLVMFrontendDirective.a  lib/libLLVMTargetParser.a  lib/libLLVMSupport.a  -lrt  -ldl  -lm  /usr/lib/x86_64-linux-gnu/libz.so  lib/libLLVMDemangle.a && :
2025-07-24T23:47:48.0865497Z ld.lld: error: undefined symbol: lldb_rpc_gen::RPCLibraryHeaderEmitter::Begin()
2025-07-24T23:47:48.0866311Z >>> referenced by RPCLibraryHeaderEmitter.h:17 (/home/gha/actions-runner/_work/llvm-project/llvm-project/lldb/tools/lldb-rpc-gen/client/RPCLibraryHeaderEmitter.h:17)
2025-07-24T23:47:48.0867384Z >>>               tools/lldb/tools/lldb-rpc-gen/CMakeFiles/lldb-rpc-gen.dir/lldb-rpc-gen.cpp.o:(SBAction::CreateASTConsumer(clang::CompilerInstance&, llvm::StringRef))
2025-07-24T23:47:48.0867932Z 
2025-07-24T23:47:48.0870989Z ld.lld: error: undefined symbol: lldb_rpc_gen::RPCLibraryHeaderEmitter::End()
2025-07-24T23:47:48.0872025Z >>> referenced by RPCLibraryHeaderEmitter.h:20 (/home/gha/actions-runner/_work/llvm-project/llvm-project/lldb/tools/lldb-rpc-gen/client/RPCLibraryHeaderEmitter.h:20)
2025-07-24T23:47:48.0872953Z >>>               tools/lldb/tools/lldb-rpc-gen/CMakeFiles/lldb-rpc-gen.dir/lldb-rpc-gen.cpp.o:(SBVisitor::~SBVisitor())
2025-07-24T23:47:48.0873420Z 
2025-07-24T23:47:48.0874065Z ld.lld: error: undefined symbol: lldb_rpc_gen::RPCLibraryHeaderEmitter::StartClass(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>)
2025-07-24T23:47:48.0874978Z >>> referenced by lldb-rpc-gen.cpp:111 (/home/gha/actions-runner/_work/llvm-project/llvm-project/lldb/tools/lldb-rpc-gen/lldb-rpc-gen.cpp:111)
2025-07-24T23:47:48.0875858Z >>>               tools/lldb/tools/lldb-rpc-gen/CMakeFiles/lldb-rpc-gen.dir/lldb-rpc-gen.cpp.o:(SBVisitor::VisitCXXRecordDecl(clang::CXXRecordDecl*))
2025-07-24T23:47:48.0876318Z 
2025-07-24T23:47:48.0876746Z ld.lld: error: undefined symbol: lldb_rpc_gen::RPCLibrarySourceEmitter::StartClass(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char>>)
2025-07-24T23:47:48.0877625Z >>> referenced by lldb-rpc-gen.cpp:112 (/home/gha/actions-runner/_work/llvm-project/llvm-project/lldb/tools/lldb-rpc-gen/lldb-rpc-gen.cpp:112)
2025-07-24T23:47:48.0878469Z >>>               tools/lldb/tools/lldb-rpc-gen/CMakeFiles/lldb-rpc-gen.dir/lldb-rpc-gen.cpp.o:(SBVisitor::VisitCXXRecordDecl(clang::CXXRecordDecl*))
2025-07-24T23:47:48.0878921Z 
2025-07-24T23:47:48.0879166Z ld.lld: error: undefined symbol: lldb_rpc_gen::RPCLibraryHeaderEmitter::EmitEnum(clang::EnumDecl*)
2025-07-24T23:47:48.0880029Z >>> referenced by lldb-rpc-gen.cpp:115 (/home/gha/actions-runner/_work/llvm-project/llvm-project/lldb/tools/lldb-rpc-gen/lldb-rpc-gen.cpp:115)
2025-07-24T23:47:48.0880851Z >>>               tools/lldb/tools/lldb-rpc-gen/CMakeFiles/lldb-rpc-gen.dir/lldb-rpc-gen.cpp.o:(SBVisitor::VisitCXXRecordDecl(clang::CXXRecordDecl*))
2025-07-24T23:47:48.0881306Z 
2025-07-24T23:47:48.0881521Z ld.lld: error: undefined symbol: lldb_rpc_gen::RPCLibraryHeaderEmitter::EndClass()
2025-07-24T23:47:48.0882169Z >>> referenced by lldb-rpc-gen.cpp:133 (/home/gha/actions-runner/_work/llvm-project/llvm-project/lldb/tools/lldb-rpc-gen/lldb-rpc-gen.cpp:133)
2025-07-24T23:47:48.0883004Z >>>               tools/lldb/tools/lldb-rpc-gen/CMakeFiles/lldb-rpc-gen.dir/lldb-rpc-gen.cpp.o:(SBVisitor::VisitCXXRecordDecl(clang::CXXRecordDecl*))
2025-07-24T23:47:48.0883516Z 
2025-07-24T23:47:48.0883901Z ld.lld: error: undefined symbol: lldb_rpc_gen::RPCLibrarySourceEmitter::EndClass()
2025-07-24T23:47:48.0884564Z >>> referenced by lldb-rpc-gen.cpp:134 (/home/gha/actions-runner/_work/llvm-project/llvm-project/lldb/tools/lldb-rpc-gen/lldb-rpc-gen.cpp:134)
2025-07-24T23:47:48.0885399Z >>>               tools/lldb/tools/lldb-rpc-gen/CMakeFiles/lldb-rpc-gen.dir/lldb-rpc-gen.cpp.o:(SBVisitor::VisitCXXRecordDecl(clang::CXXRecordDecl*))
2025-07-24T23:47:48.0885851Z 
2025-07-24T23:47:48.0886140Z ld.lld: error: undefined symbol: lldb_rpc_gen::RPCLibrarySourceEmitter::EmitMethod(lldb_rpc_gen::Method const&)
2025-07-24T23:47:48.0886873Z >>> referenced by lldb-rpc-gen.cpp:127 (/home/gha/actions-runner/_work/llvm-project/llvm-project/lldb/tools/lldb-rpc-gen/lldb-rpc-gen.cpp:127)
2025-07-24T23:47:48.0887705Z >>>               tools/lldb/tools/lldb-rpc-gen/CMakeFiles/lldb-rpc-gen.dir/lldb-rpc-gen.cpp.o:(SBVisitor::VisitCXXRecordDecl(clang::CXXRecordDecl*))
2025-07-24T23:47:48.0888151Z 
2025-07-24T23:47:48.0888441Z ld.lld: error: undefined symbol: lldb_rpc_gen::RPCLibraryHeaderEmitter::EmitMethod(lldb_rpc_gen::Method const&)
2025-07-24T23:47:48.0889174Z >>> referenced by lldb-rpc-gen.cpp:128 (/home/gha/actions-runner/_work/llvm-project/llvm-project/lldb/tools/lldb-rpc-gen/lldb-rpc-gen.cpp:128)
2025-07-24T23:47:48.0890010Z >>>               tools/lldb/tools/lldb-rpc-gen/CMakeFiles/lldb-rpc-gen.dir/lldb-rpc-gen.cpp.o:(SBVisitor::VisitCXXRecordDecl(clang::CXXRecordDecl*))
2025-07-24T23:47:48.0890655Z clang++: error: linker command failed with exit code 1 (use -v to see invocation)

Copy link
Member

@medismailben medismailben left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, LGTM with comments and linking failure addressed.

@chelcassanova chelcassanova force-pushed the upstream-lldb-rpc/add-lldb-rpc-client-emitters branch 4 times, most recently from 339c14b to 619de01 Compare July 30, 2025 07:51
@chelcassanova
Copy link
Contributor Author

@DavidSpickett Is it possible to take another look at this?

Copy link
Collaborator

@DavidSpickett DavidSpickett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please go over the outstanding comments and make sure they have been done. I see several that have not.

@chelcassanova chelcassanova force-pushed the upstream-lldb-rpc/add-lldb-rpc-client-emitters branch from 619de01 to bf1a181 Compare August 1, 2025 20:15
Adds shell tests for the LLDB RPC client library.
Adds the RPC client library sources and header emitters.
Updates the CMake files for RPC to account for the client emitters
Updates the lldb-rpc-gen tool to account for everything necessary to
emit the client library code.
@chelcassanova chelcassanova force-pushed the upstream-lldb-rpc/add-lldb-rpc-client-emitters branch from bf1a181 to 3437470 Compare August 5, 2025 18:37
@chelcassanova
Copy link
Contributor Author

@DavidSpickett I should've addressed all comments on the patch, is it possible to take another look at this?

Copy link
Collaborator

@DavidSpickett DavidSpickett left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants