[Lldb-commits] [lldb] [lldb][rpc] Use Clang attributes to keep track of pointer plus len (PR #148981)
Chelsea Cassanova via lldb-commits
lldb-commits at lists.llvm.org
Thu Jul 17 10:32:38 PDT 2025
================
@@ -0,0 +1,469 @@
+//===-- 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/Attrs.inc"
+#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>
+#include <iostream>
+
+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",
+};
+
+// 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;
+
+ // 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 **`.
+ 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;
+}
+
+// Methods that have a pointer parameter followed by a length param need to be
+// kept track of in RPC. Such methods are annotated in the main SB API using
+// this Clang attribute: [[clang::annotate("lldb-rpc-gen pointer plus len")]].
+// This method checks that a given method has this attribute.
+//
+// NOTE: In the event that we end up using the Clang annotate attribute for
+// other things in RPC, we'd have to check the actual text for the annotation
+// and not just check if the method has an annotation at all.
----------------
chelcassanova wrote:
Good point, this annotation can also be placed behind a macro (similar to what we do with LLDB_DEPRECATED) such that we have:
"are we using lldb-rpc? if so, expand the macro to this attribute. otherwise, no-op"
And yes, it's better for futureproofing if we check the actual underlying text here.
https://github.com/llvm/llvm-project/pull/148981
More information about the lldb-commits
mailing list