diff options
Diffstat (limited to 'lldb/include')
43 files changed, 673 insertions, 194 deletions
diff --git a/lldb/include/lldb/API/SBFrame.h b/lldb/include/lldb/API/SBFrame.h index 08de0605b324..e4bbcd5ddcd9 100644 --- a/lldb/include/lldb/API/SBFrame.h +++ b/lldb/include/lldb/API/SBFrame.h @@ -104,6 +104,8 @@ public: bool IsArtificial() const; + bool IsSynthetic() const; + /// Return whether a frame recognizer decided this frame should not /// be displayes in backtraces etc. bool IsHidden() const; diff --git a/lldb/include/lldb/API/SBFunction.h b/lldb/include/lldb/API/SBFunction.h index 0a8aeeff1ea5..e703ae5dd63c 100644 --- a/lldb/include/lldb/API/SBFunction.h +++ b/lldb/include/lldb/API/SBFunction.h @@ -36,6 +36,8 @@ public: const char *GetMangledName() const; + const char *GetBaseName() const; + lldb::SBInstructionList GetInstructions(lldb::SBTarget target); lldb::SBInstructionList GetInstructions(lldb::SBTarget target, diff --git a/lldb/include/lldb/API/SBSymbol.h b/lldb/include/lldb/API/SBSymbol.h index a93bc7a7ae07..580458ede212 100644 --- a/lldb/include/lldb/API/SBSymbol.h +++ b/lldb/include/lldb/API/SBSymbol.h @@ -36,6 +36,8 @@ public: const char *GetMangledName() const; + const char *GetBaseName() const; + lldb::SBInstructionList GetInstructions(lldb::SBTarget target); lldb::SBInstructionList GetInstructions(lldb::SBTarget target, diff --git a/lldb/include/lldb/API/SBSymbolContext.h b/lldb/include/lldb/API/SBSymbolContext.h index 128b0b65b786..19f29c629d09 100644 --- a/lldb/include/lldb/API/SBSymbolContext.h +++ b/lldb/include/lldb/API/SBSymbolContext.h @@ -66,6 +66,7 @@ protected: friend class SBTarget; friend class SBSymbolContextList; + friend class lldb_private::ScriptInterpreter; friend class lldb_private::python::SWIGBridge; SBSymbolContext(const lldb_private::SymbolContext &sc_ptr); diff --git a/lldb/include/lldb/Core/Architecture.h b/lldb/include/lldb/Core/Architecture.h index b6fc1a20e1e6..ed64a895717a 100644 --- a/lldb/include/lldb/Core/Architecture.h +++ b/lldb/include/lldb/Core/Architecture.h @@ -12,6 +12,7 @@ #include "lldb/Core/PluginInterface.h" #include "lldb/Target/DynamicRegisterInfo.h" #include "lldb/Target/MemoryTagManager.h" +#include "lldb/Target/RegisterContextUnwind.h" namespace lldb_private { @@ -129,6 +130,14 @@ public: RegisterContext ®_context) const { return false; } + + /// Return an UnwindPlan that allows architecture-defined rules for finding + /// saved registers, given a particular set of register values. + virtual lldb::UnwindPlanSP GetArchitectureUnwindPlan( + lldb_private::Thread &thread, lldb_private::RegisterContextUnwind *regctx, + std::shared_ptr<const UnwindPlan> current_unwindplan) { + return lldb::UnwindPlanSP(); + } }; } // namespace lldb_private diff --git a/lldb/include/lldb/Core/Disassembler.h b/lldb/include/lldb/Core/Disassembler.h index 50a5d8783584..db186dd33d77 100644 --- a/lldb/include/lldb/Core/Disassembler.h +++ b/lldb/include/lldb/Core/Disassembler.h @@ -169,7 +169,7 @@ public: virtual bool IsAuthenticated() = 0; - bool CanSetBreakpoint (); + bool CanSetBreakpoint(); virtual size_t Decode(const Disassembler &disassembler, const DataExtractor &data, @@ -282,7 +282,7 @@ std::function<bool(const Instruction::Operand &)> FetchImmOp(int64_t &imm); std::function<bool(const Instruction::Operand &)> MatchOpType(Instruction::Operand::Type type); -} +} // namespace OperandMatchers class InstructionList { public: @@ -316,20 +316,19 @@ public: /// @param[in] ignore_calls /// It true, then fine the first branch instruction that isn't /// a function call (a branch that calls and returns to the next - /// instruction). If false, find the instruction index of any + /// instruction). If false, find the instruction index of any /// branch in the list. - /// + /// /// @param[out] found_calls - /// If non-null, this will be set to true if any calls were found in + /// If non-null, this will be set to true if any calls were found in /// extending the range. - /// + /// /// @return /// The instruction index of the first branch that is at or past - /// \a start. Returns UINT32_MAX if no matching branches are + /// \a start. Returns UINT32_MAX if no matching branches are /// found. //------------------------------------------------------------------ - uint32_t GetIndexOfNextBranchInstruction(uint32_t start, - bool ignore_calls, + uint32_t GetIndexOfNextBranchInstruction(uint32_t start, bool ignore_calls, bool *found_calls) const; uint32_t GetIndexOfInstructionAtLoadAddress(lldb::addr_t load_addr, @@ -399,6 +398,7 @@ public: eOptionMarkPCAddress = (1u << 3), // Mark the disassembly line the contains the PC eOptionShowControlFlowKind = (1u << 4), + eOptionVariableAnnotations = (1u << 5), }; enum HexImmediateStyle { @@ -566,6 +566,26 @@ private: const Disassembler &operator=(const Disassembler &) = delete; }; +/// Tracks live variable annotations across instructions and produces +/// per-instruction "events" like `name = RDI` or `name = <undef>`. +class VariableAnnotator { + struct VarState { + /// Display name. + std::string name; + /// Last printed location (empty means <undef>). + std::string last_loc; + }; + + // Live state from the previous instruction, keyed by Variable::GetID(). + llvm::DenseMap<lldb::user_id_t, VarState> Live_; + +public: + /// Compute annotation strings for a single instruction and update `Live_`. + /// Returns only the events that should be printed *at this instruction*. + std::vector<std::string> annotate(Instruction &inst, Target &target, + const lldb::ModuleSP &module_sp); +}; + } // namespace lldb_private #endif // LLDB_CORE_DISASSEMBLER_H diff --git a/lldb/include/lldb/Core/FormatEntity.h b/lldb/include/lldb/Core/FormatEntity.h index d602edffb4c8..40916dc48a70 100644 --- a/lldb/include/lldb/Core/FormatEntity.h +++ b/lldb/include/lldb/Core/FormatEntity.h @@ -80,6 +80,7 @@ struct Entry { FrameRegisterFlags, FrameRegisterByName, FrameIsArtificial, + FrameKind, ScriptFrame, FunctionID, FunctionDidChange, diff --git a/lldb/include/lldb/Core/Mangled.h b/lldb/include/lldb/Core/Mangled.h index eb9a58c56889..47f1c6a8d80b 100644 --- a/lldb/include/lldb/Core/Mangled.h +++ b/lldb/include/lldb/Core/Mangled.h @@ -287,6 +287,17 @@ public: /// Retrieve \c DemangledNameInfo of the demangled name held by this object. const std::optional<DemangledNameInfo> &GetDemangledInfo() const; + /// Compute the base name (without namespace/class qualifiers) from the + /// demangled name. + /// + /// For a demangled name like "ns::MyClass<int>::templateFunc", this returns + /// just "templateFunc". + /// + /// \return + /// A ConstString containing the basename, or nullptr if computation + /// fails. + ConstString GetBaseName() const; + private: /// If \c force is \c false, this function will re-use the previously /// demangled name (if any). If \c force is \c true (or the mangled name diff --git a/lldb/include/lldb/Core/Opcode.h b/lldb/include/lldb/Core/Opcode.h index f680e0b91ab8..7bbd73d039f9 100644 --- a/lldb/include/lldb/Core/Opcode.h +++ b/lldb/include/lldb/Core/Opcode.h @@ -211,7 +211,7 @@ public: if (bytes != nullptr && length > 0) { m_type = type; m_data.inst.length = length; - assert(length < sizeof(m_data.inst.bytes)); + assert(length <= sizeof(m_data.inst.bytes)); memcpy(m_data.inst.bytes, bytes, length); m_byte_order = order; } else { diff --git a/lldb/include/lldb/Core/ProtocolServer.h b/lldb/include/lldb/Core/ProtocolServer.h index 937256c10aec..fcb91ea203e1 100644 --- a/lldb/include/lldb/Core/ProtocolServer.h +++ b/lldb/include/lldb/Core/ProtocolServer.h @@ -22,6 +22,8 @@ public: static ProtocolServer *GetOrCreate(llvm::StringRef name); + static llvm::Error Terminate(); + static std::vector<llvm::StringRef> GetSupportedProtocols(); struct Connection { diff --git a/lldb/include/lldb/Expression/DWARFExpression.h b/lldb/include/lldb/Expression/DWARFExpression.h index 8fcc5d37b91c..1f8464cf5cf8 100644 --- a/lldb/include/lldb/Expression/DWARFExpression.h +++ b/lldb/include/lldb/Expression/DWARFExpression.h @@ -159,7 +159,8 @@ public: return data.GetByteSize() > 0; } - void DumpLocation(Stream *s, lldb::DescriptionLevel level, ABI *abi) const; + void DumpLocation(Stream *s, lldb::DescriptionLevel level, ABI *abi, + llvm::DIDumpOptions options = {}) const; bool MatchesOperand(StackFrame &frame, const Instruction::Operand &op) const; diff --git a/lldb/include/lldb/Expression/Expression.h b/lldb/include/lldb/Expression/Expression.h index 20067f469895..847226167d58 100644 --- a/lldb/include/lldb/Expression/Expression.h +++ b/lldb/include/lldb/Expression/Expression.h @@ -103,11 +103,15 @@ protected: /// /// The format being: /// -/// <prefix>:<module uid>:<symbol uid>:<name> +/// <prefix>:<discriminator>:<module uid>:<symbol uid>:<name> /// /// The label string needs to stay valid for the entire lifetime /// of this object. struct FunctionCallLabel { + /// Arbitrary string which language plugins can interpret for their + /// own needs. + llvm::StringRef discriminator; + /// Unique identifier of the lldb_private::Module /// which contains the symbol identified by \c symbol_id. lldb::user_id_t module_id; @@ -133,7 +137,7 @@ struct FunctionCallLabel { /// /// The representation roundtrips through \c fromString: /// \code{.cpp} - /// llvm::StringRef encoded = "$__lldb_func:0x0:0x0:_Z3foov"; + /// llvm::StringRef encoded = "$__lldb_func:blah:0x0:0x0:_Z3foov"; /// FunctionCallLabel label = *fromString(label); /// /// assert (label.toString() == encoded); diff --git a/lldb/include/lldb/Expression/IRMemoryMap.h b/lldb/include/lldb/Expression/IRMemoryMap.h index 58b95c56c1c3..56f79444764e 100644 --- a/lldb/include/lldb/Expression/IRMemoryMap.h +++ b/lldb/include/lldb/Expression/IRMemoryMap.h @@ -65,7 +65,7 @@ public: size_t size, Status &error); void WriteScalarToMemory(lldb::addr_t process_address, Scalar &scalar, size_t size, Status &error); - void WritePointerToMemory(lldb::addr_t process_address, lldb::addr_t address, + void WritePointerToMemory(lldb::addr_t process_address, lldb::addr_t pointer, Status &error); void ReadMemory(uint8_t *bytes, lldb::addr_t process_address, size_t size, Status &error); diff --git a/lldb/include/lldb/Host/File.h b/lldb/include/lldb/Host/File.h index 9e2d0abe0b1a..7402a2231735 100644 --- a/lldb/include/lldb/Host/File.h +++ b/lldb/include/lldb/Host/File.h @@ -382,15 +382,11 @@ public: Unowned = false, }; - NativeFile() : m_descriptor(kInvalidDescriptor), m_stream(kInvalidStream) {} + NativeFile(); - NativeFile(FILE *fh, bool transfer_ownership) - : m_descriptor(kInvalidDescriptor), m_own_descriptor(false), m_stream(fh), - m_options(), m_own_stream(transfer_ownership) {} + NativeFile(FILE *fh, bool transfer_ownership); - NativeFile(int fd, OpenOptions options, bool transfer_ownership) - : m_descriptor(fd), m_own_descriptor(transfer_ownership), - m_stream(kInvalidStream), m_options(options), m_own_stream(false) {} + NativeFile(int fd, OpenOptions options, bool transfer_ownership); ~NativeFile() override { Close(); } @@ -444,17 +440,19 @@ protected: return ValueGuard(m_stream_mutex, StreamIsValidUnlocked()); } - int m_descriptor; + int m_descriptor = kInvalidDescriptor; bool m_own_descriptor = false; mutable std::mutex m_descriptor_mutex; - FILE *m_stream; + FILE *m_stream = kInvalidStream; mutable std::mutex m_stream_mutex; OpenOptions m_options{}; bool m_own_stream = false; std::mutex offset_access_mutex; + bool is_windows_console = false; + private: NativeFile(const NativeFile &) = delete; const NativeFile &operator=(const NativeFile &) = delete; diff --git a/lldb/include/lldb/Host/HostInfoBase.h b/lldb/include/lldb/Host/HostInfoBase.h index b6a95fffb2db..a6aaacd9d6fe 100644 --- a/lldb/include/lldb/Host/HostInfoBase.h +++ b/lldb/include/lldb/Host/HostInfoBase.h @@ -102,11 +102,19 @@ public: /// member of the FileSpec is filled in. static FileSpec GetSystemPluginDir(); + /// Returns the directory containing the users home (e.g. `~/`). Only the + /// directory member of the FileSpec is filled in. + static FileSpec GetUserHomeDir(); + + /// Returns the directory containing the users lldb home (e.g. `~/.lldb/`). + /// Only the directory member of the FileSpec is filled in. + static FileSpec GetUserLLDBDir(); + /// Returns the directory containing the user plugins. Only the directory /// member of the FileSpec is filled in. static FileSpec GetUserPluginDir(); - /// Returns the proces temporary directory. This directory will be cleaned up + /// Returns the process temporary directory. This directory will be cleaned up /// when this process exits. Only the directory member of the FileSpec is /// filled in. static FileSpec GetProcessTempDir(); @@ -167,11 +175,13 @@ protected: static bool ComputeTempFileBaseDirectory(FileSpec &file_spec); static bool ComputeHeaderDirectory(FileSpec &file_spec); static bool ComputeSystemPluginsDirectory(FileSpec &file_spec); + static bool ComputeUserHomeDirectory(FileSpec &file_spec); + static bool ComputeUserLLDBHomeDirectory(FileSpec &file_spec); static bool ComputeUserPluginsDirectory(FileSpec &file_spec); static void ComputeHostArchitectureSupport(ArchSpec &arch_32, ArchSpec &arch_64); }; -} +} // namespace lldb_private #endif diff --git a/lldb/include/lldb/Host/JSONTransport.h b/lldb/include/lldb/Host/JSONTransport.h index 0be60a8f3f96..210f33edace6 100644 --- a/lldb/include/lldb/Host/JSONTransport.h +++ b/lldb/include/lldb/Host/JSONTransport.h @@ -100,7 +100,8 @@ public: virtual llvm::Expected<MainLoop::ReadHandleUP> RegisterMessageHandler(MainLoop &loop, MessageHandler &handler) = 0; -protected: + // FIXME: Refactor mcp::Server to not directly access log on the transport. + // protected: template <typename... Ts> inline auto Logv(const char *Fmt, Ts &&...Vals) { Log(llvm::formatv(Fmt, std::forward<Ts>(Vals)...).str()); } @@ -139,9 +140,7 @@ public: /// detail. static constexpr size_t kReadBufferSize = 1024; -protected: - virtual llvm::Expected<std::vector<std::string>> Parse() = 0; - virtual std::string Encode(const llvm::json::Value &message) = 0; + // FIXME: Write should be protected. llvm::Error Write(const llvm::json::Value &message) { this->Logv("<-- {0}", message); std::string output = Encode(message); @@ -149,6 +148,10 @@ protected: return m_out->Write(output.data(), bytes_written).takeError(); } +protected: + virtual llvm::Expected<std::vector<std::string>> Parse() = 0; + virtual std::string Encode(const llvm::json::Value &message) = 0; + llvm::SmallString<kReadBufferSize> m_buffer; private: diff --git a/lldb/include/lldb/Host/macosx/HostInfoMacOSX.h b/lldb/include/lldb/Host/macosx/HostInfoMacOSX.h index d04841885660..734a394c1867 100644 --- a/lldb/include/lldb/Host/macosx/HostInfoMacOSX.h +++ b/lldb/include/lldb/Host/macosx/HostInfoMacOSX.h @@ -56,6 +56,7 @@ protected: static std::string FindComponentInPath(llvm::StringRef path, llvm::StringRef component); }; -} + +} // namespace lldb_private #endif diff --git a/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameInterface.h b/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameInterface.h new file mode 100644 index 000000000000..8ef4b37d6ba1 --- /dev/null +++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedFrameInterface.h @@ -0,0 +1,55 @@ +//===----------------------------------------------------------------------===// +// +// 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_INTERPRETER_INTERFACES_SCRIPTEDFRAMEINTERFACE_H +#define LLDB_INTERPRETER_INTERFACES_SCRIPTEDFRAMEINTERFACE_H + +#include "ScriptedInterface.h" +#include "lldb/Core/StructuredDataImpl.h" +#include "lldb/Symbol/SymbolContext.h" +#include "lldb/lldb-private.h" +#include <optional> +#include <string> + +namespace lldb_private { +class ScriptedFrameInterface : virtual public ScriptedInterface { +public: + virtual llvm::Expected<StructuredData::GenericSP> + CreatePluginObject(llvm::StringRef class_name, ExecutionContext &exe_ctx, + StructuredData::DictionarySP args_sp, + StructuredData::Generic *script_obj = nullptr) = 0; + + virtual lldb::user_id_t GetID() { return LLDB_INVALID_FRAME_ID; } + + virtual lldb::addr_t GetPC() { return LLDB_INVALID_ADDRESS; } + + virtual std::optional<SymbolContext> GetSymbolContext() { + return std::nullopt; + } + + virtual std::optional<std::string> GetFunctionName() { return std::nullopt; } + + virtual std::optional<std::string> GetDisplayFunctionName() { + return std::nullopt; + } + + virtual bool IsInlined() { return false; } + + virtual bool IsArtificial() { return false; } + + virtual bool IsHidden() { return false; } + + virtual StructuredData::DictionarySP GetRegisterInfo() { return {}; } + + virtual std::optional<std::string> GetRegisterContext() { + return std::nullopt; + } +}; +} // namespace lldb_private + +#endif // LLDB_INTERPRETER_INTERFACES_SCRIPTEDFRAMEINTERFACE_H diff --git a/lldb/include/lldb/Interpreter/Interfaces/ScriptedThreadInterface.h b/lldb/include/lldb/Interpreter/Interfaces/ScriptedThreadInterface.h index a7cfc690b67d..bc58f344d36f 100644 --- a/lldb/include/lldb/Interpreter/Interfaces/ScriptedThreadInterface.h +++ b/lldb/include/lldb/Interpreter/Interfaces/ScriptedThreadInterface.h @@ -44,6 +44,16 @@ public: } virtual StructuredData::ArraySP GetExtendedInfo() { return {}; } + + virtual std::optional<std::string> GetScriptedFramePluginName() { + return std::nullopt; + } + +protected: + friend class ScriptedFrame; + virtual lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() { + return {}; + } }; } // namespace lldb_private diff --git a/lldb/include/lldb/Interpreter/Options.h b/lldb/include/lldb/Interpreter/Options.h index 864bda6f24c8..2d06605f5f0b 100644 --- a/lldb/include/lldb/Interpreter/Options.h +++ b/lldb/include/lldb/Interpreter/Options.h @@ -85,10 +85,10 @@ public: void OutputFormattedUsageText(Stream &strm, const OptionDefinition &option_def, - uint32_t output_max_columns); + uint32_t output_max_columns, bool use_color); void GenerateOptionUsage(Stream &strm, CommandObject &cmd, - uint32_t screen_width); + uint32_t screen_width, bool use_color); bool SupportsLongOption(const char *long_option); diff --git a/lldb/include/lldb/Interpreter/ScriptInterpreter.h b/lldb/include/lldb/Interpreter/ScriptInterpreter.h index dffb9b82abf3..024bbc90a9a3 100644 --- a/lldb/include/lldb/Interpreter/ScriptInterpreter.h +++ b/lldb/include/lldb/Interpreter/ScriptInterpreter.h @@ -26,6 +26,7 @@ #include "lldb/Host/PseudoTerminal.h" #include "lldb/Host/StreamFile.h" #include "lldb/Interpreter/Interfaces/OperatingSystemInterface.h" +#include "lldb/Interpreter/Interfaces/ScriptedFrameInterface.h" #include "lldb/Interpreter/Interfaces/ScriptedPlatformInterface.h" #include "lldb/Interpreter/Interfaces/ScriptedProcessInterface.h" #include "lldb/Interpreter/Interfaces/ScriptedThreadInterface.h" @@ -531,6 +532,10 @@ public: return {}; } + virtual lldb::ScriptedFrameInterfaceSP CreateScriptedFrameInterface() { + return {}; + } + virtual lldb::ScriptedThreadPlanInterfaceSP CreateScriptedThreadPlanInterface() { return {}; diff --git a/lldb/include/lldb/Protocol/MCP/Protocol.h b/lldb/include/lldb/Protocol/MCP/Protocol.h index 49f949022175..6e1ffcbe1f3e 100644 --- a/lldb/include/lldb/Protocol/MCP/Protocol.h +++ b/lldb/include/lldb/Protocol/MCP/Protocol.h @@ -18,6 +18,7 @@ #include <optional> #include <string> #include <variant> +#include <vector> namespace lldb_protocol::mcp { @@ -38,11 +39,24 @@ struct Request { /// The method's params. std::optional<llvm::json::Value> params; }; - llvm::json::Value toJSON(const Request &); bool fromJSON(const llvm::json::Value &, Request &, llvm::json::Path); bool operator==(const Request &, const Request &); +enum ErrorCode : signed { + /// Invalid JSON was received by the server. An error occurred on the server + /// while parsing the JSON text. + eErrorCodeParseError = -32700, + /// The JSON sent is not a valid Request object. + eErrorCodeInvalidRequest = -32600, + /// The method does not exist / is not available. + eErrorCodeMethodNotFound = -32601, + /// Invalid method parameter(s). + eErrorCodeInvalidParams = -32602, + /// Internal JSON-RPC error. + eErrorCodeInternalError = -32603, +}; + struct Error { /// The error type that occurred. int64_t code = 0; @@ -52,9 +66,8 @@ struct Error { /// Additional information about the error. The value of this member is /// defined by the sender (e.g. detailed error information, nested errors /// etc.). - std::optional<llvm::json::Value> data; + std::optional<llvm::json::Value> data = std::nullopt; }; - llvm::json::Value toJSON(const Error &); bool fromJSON(const llvm::json::Value &, Error &, llvm::json::Path); bool operator==(const Error &, const Error &); @@ -67,7 +80,6 @@ struct Response { /// response. std::variant<Error, llvm::json::Value> result; }; - llvm::json::Value toJSON(const Response &); bool fromJSON(const llvm::json::Value &, Response &, llvm::json::Path); bool operator==(const Response &, const Response &); @@ -79,7 +91,6 @@ struct Notification { /// The notification's params. std::optional<llvm::json::Value> params; }; - llvm::json::Value toJSON(const Notification &); bool fromJSON(const llvm::json::Value &, Notification &, llvm::json::Path); bool operator==(const Notification &, const Notification &); @@ -90,45 +101,9 @@ using Message = std::variant<Request, Response, Notification>; // not force it to be checked early here. static_assert(std::is_convertible_v<Message, Message>, "Message is not convertible to itself"); - bool fromJSON(const llvm::json::Value &, Message &, llvm::json::Path); llvm::json::Value toJSON(const Message &); -struct ToolCapability { - /// Whether this server supports notifications for changes to the tool list. - bool listChanged = false; -}; - -llvm::json::Value toJSON(const ToolCapability &); -bool fromJSON(const llvm::json::Value &, ToolCapability &, llvm::json::Path); - -struct ResourceCapability { - /// Whether this server supports notifications for changes to the resources - /// list. - bool listChanged = false; - - /// Whether subscriptions are supported. - bool subscribe = false; -}; - -llvm::json::Value toJSON(const ResourceCapability &); -bool fromJSON(const llvm::json::Value &, ResourceCapability &, - llvm::json::Path); - -/// Capabilities that a server may support. Known capabilities are defined here, -/// in this schema, but this is not a closed set: any server can define its own, -/// additional capabilities. -struct Capabilities { - /// Tool capabilities of the server. - ToolCapability tools; - - /// Resource capabilities of the server. - ResourceCapability resources; -}; - -llvm::json::Value toJSON(const Capabilities &); -bool fromJSON(const llvm::json::Value &, Capabilities &, llvm::json::Path); - /// A known resource that the server is capable of reading. struct Resource { /// The URI of this resource. @@ -138,17 +113,25 @@ struct Resource { std::string name; /// A description of what this resource represents. - std::string description; + std::string description = ""; /// The MIME type of this resource, if known. - std::string mimeType; + std::string mimeType = ""; }; llvm::json::Value toJSON(const Resource &); bool fromJSON(const llvm::json::Value &, Resource &, llvm::json::Path); +/// The server’s response to a resources/list request from the client. +struct ListResourcesResult { + std::vector<Resource> resources; +}; +llvm::json::Value toJSON(const ListResourcesResult &); +bool fromJSON(const llvm::json::Value &, ListResourcesResult &, + llvm::json::Path); + /// The contents of a specific resource or sub-resource. -struct ResourceContents { +struct TextResourceContents { /// The URI of this resource. std::string uri; @@ -160,34 +143,37 @@ struct ResourceContents { std::string mimeType; }; -llvm::json::Value toJSON(const ResourceContents &); -bool fromJSON(const llvm::json::Value &, ResourceContents &, llvm::json::Path); +llvm::json::Value toJSON(const TextResourceContents &); +bool fromJSON(const llvm::json::Value &, TextResourceContents &, + llvm::json::Path); -/// The server's response to a resources/read request from the client. -struct ResourceResult { - std::vector<ResourceContents> contents; +/// Sent from the client to the server, to read a specific resource URI. +struct ReadResourceParams { + /// The URI of the resource to read. The URI can use any protocol; it is up to + /// the server how to interpret it. + std::string uri; }; +llvm::json::Value toJSON(const ReadResourceParams &); +bool fromJSON(const llvm::json::Value &, ReadResourceParams &, + llvm::json::Path); -llvm::json::Value toJSON(const ResourceResult &); -bool fromJSON(const llvm::json::Value &, ResourceResult &, llvm::json::Path); +/// The server's response to a resources/read request from the client. +struct ReadResourceResult { + std::vector<TextResourceContents> contents; +}; +llvm::json::Value toJSON(const ReadResourceResult &); +bool fromJSON(const llvm::json::Value &, ReadResourceResult &, + llvm::json::Path); /// Text provided to or from an LLM. struct TextContent { /// The text content of the message. std::string text; }; - llvm::json::Value toJSON(const TextContent &); bool fromJSON(const llvm::json::Value &, TextContent &, llvm::json::Path); -struct TextResult { - std::vector<TextContent> content; - bool isError = false; -}; - -llvm::json::Value toJSON(const TextResult &); -bool fromJSON(const llvm::json::Value &, TextResult &, llvm::json::Path); - +/// Definition for a tool the client can call. struct ToolDefinition { /// Unique identifier for the tool. std::string name; @@ -198,12 +184,144 @@ struct ToolDefinition { // JSON Schema for the tool's parameters. std::optional<llvm::json::Value> inputSchema; }; - llvm::json::Value toJSON(const ToolDefinition &); bool fromJSON(const llvm::json::Value &, ToolDefinition &, llvm::json::Path); using ToolArguments = std::variant<std::monostate, llvm::json::Value>; +/// Describes the name and version of an MCP implementation, with an optional +/// title for UI representation. +struct Implementation { + /// Intended for programmatic or logical use, but used as a display name in + /// past specs or fallback (if title isn’t present). + std::string name; + + std::string version; + + /// Intended for UI and end-user contexts — optimized to be human-readable and + /// easily understood, even by those unfamiliar with domain-specific + /// terminology. + /// + /// If not provided, the name should be used for display (except for Tool, + /// where annotations.title should be given precedence over using name, if + /// present). + std::string title = ""; +}; +llvm::json::Value toJSON(const Implementation &); +bool fromJSON(const llvm::json::Value &, Implementation &, llvm::json::Path); + +/// Capabilities a client may support. Known capabilities are defined here, in +/// this schema, but this is not a closed set: any client can define its own, +/// additional capabilities. +struct ClientCapabilities {}; +llvm::json::Value toJSON(const ClientCapabilities &); +bool fromJSON(const llvm::json::Value &, ClientCapabilities &, + llvm::json::Path); + +/// Capabilities that a server may support. Known capabilities are defined here, +/// in this schema, but this is not a closed set: any server can define its own, +/// additional capabilities. +struct ServerCapabilities { + bool supportsToolsList = false; + bool supportsResourcesList = false; + bool supportsResourcesSubscribe = false; + + /// Utilities. + bool supportsCompletions = false; + bool supportsLogging = false; +}; +llvm::json::Value toJSON(const ServerCapabilities &); +bool fromJSON(const llvm::json::Value &, ServerCapabilities &, + llvm::json::Path); + +/// Initialization + +/// This request is sent from the client to the server when it first connects, +/// asking it to begin initialization. +struct InitializeParams { + /// The latest version of the Model Context Protocol that the client supports. + /// The client MAY decide to support older versions as well. + std::string protocolVersion; + + ClientCapabilities capabilities; + + Implementation clientInfo; +}; +llvm::json::Value toJSON(const InitializeParams &); +bool fromJSON(const llvm::json::Value &, InitializeParams &, llvm::json::Path); + +/// After receiving an initialize request from the client, the server sends this +/// response. +struct InitializeResult { + /// The version of the Model Context Protocol that the server wants to use. + /// This may not match the version that the client requested. If the client + /// cannot support this version, it MUST disconnect. + std::string protocolVersion; + + ServerCapabilities capabilities; + Implementation serverInfo; + + /// Instructions describing how to use the server and its features. + /// + /// This can be used by clients to improve the LLM's understanding of + /// available tools, resources, etc. It can be thought of like a "hint" to the + /// model. For example, this information MAY be added to the system prompt. + std::string instructions = ""; +}; +llvm::json::Value toJSON(const InitializeResult &); +bool fromJSON(const llvm::json::Value &, InitializeResult &, llvm::json::Path); + +/// Special case parameter or result that has no value. +using Void = std::monostate; +llvm::json::Value toJSON(const Void &); +bool fromJSON(const llvm::json::Value &, Void &, llvm::json::Path); + +/// The server's response to a `tools/list` request from the client. +struct ListToolsResult { + std::vector<ToolDefinition> tools; +}; +llvm::json::Value toJSON(const ListToolsResult &); +bool fromJSON(const llvm::json::Value &, ListToolsResult &, llvm::json::Path); + +/// Supported content types, currently only TextContent, but the spec includes +/// additional content types. +using ContentBlock = TextContent; + +/// Used by the client to invoke a tool provided by the server. +struct CallToolParams { + std::string name; + std::optional<llvm::json::Value> arguments; +}; +llvm::json::Value toJSON(const CallToolParams &); +bool fromJSON(const llvm::json::Value &, CallToolParams &, llvm::json::Path); + +/// The server’s response to a tool call. +struct CallToolResult { + /// A list of content objects that represent the unstructured result of the + /// tool call. + std::vector<ContentBlock> content; + + /// Whether the tool call ended in an error. + /// + /// If not set, this is assumed to be false (the call was successful). + /// + /// Any errors that originate from the tool SHOULD be reported inside the + /// result object, with `isError` set to true, not as an MCP protocol-level + /// error response. Otherwise, the LLM would not be able to see that an error + /// occurred and self-correct. + /// + /// However, any errors in finding the tool, an error indicating that the + /// server does not support tool calls, or any other exceptional conditions, + /// should be reported as an MCP error response. + bool isError = false; + + /// An optional JSON object that represents the structured result of the tool + /// call. + std::optional<llvm::json::Value> structuredContent = std::nullopt; +}; +llvm::json::Value toJSON(const CallToolResult &); +bool fromJSON(const llvm::json::Value &, CallToolResult &, llvm::json::Path); + } // namespace lldb_protocol::mcp #endif diff --git a/lldb/include/lldb/Protocol/MCP/Resource.h b/lldb/include/lldb/Protocol/MCP/Resource.h index 4835d340cd4c..158cffc71ea1 100644 --- a/lldb/include/lldb/Protocol/MCP/Resource.h +++ b/lldb/include/lldb/Protocol/MCP/Resource.h @@ -20,7 +20,7 @@ public: virtual ~ResourceProvider() = default; virtual std::vector<lldb_protocol::mcp::Resource> GetResources() const = 0; - virtual llvm::Expected<lldb_protocol::mcp::ResourceResult> + virtual llvm::Expected<lldb_protocol::mcp::ReadResourceResult> ReadResource(llvm::StringRef uri) const = 0; }; diff --git a/lldb/include/lldb/Protocol/MCP/Server.h b/lldb/include/lldb/Protocol/MCP/Server.h index 2ac05880de86..b674d5815955 100644 --- a/lldb/include/lldb/Protocol/MCP/Server.h +++ b/lldb/include/lldb/Protocol/MCP/Server.h @@ -9,40 +9,55 @@ #ifndef LLDB_PROTOCOL_MCP_SERVER_H #define LLDB_PROTOCOL_MCP_SERVER_H +#include "lldb/Host/JSONTransport.h" +#include "lldb/Host/MainLoop.h" #include "lldb/Protocol/MCP/Protocol.h" #include "lldb/Protocol/MCP/Resource.h" #include "lldb/Protocol/MCP/Tool.h" +#include "lldb/Protocol/MCP/Transport.h" +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Support/Error.h" -#include <mutex> +#include "llvm/Support/JSON.h" +#include "llvm/Support/Signals.h" +#include <functional> +#include <memory> +#include <string> +#include <vector> namespace lldb_protocol::mcp { -class Server { +class Server : public MCPTransport::MessageHandler { public: - Server(std::string name, std::string version); - virtual ~Server() = default; + Server(std::string name, std::string version, + std::unique_ptr<MCPTransport> transport_up, + lldb_private::MainLoop &loop); + ~Server() = default; + + using NotificationHandler = std::function<void(const Notification &)>; void AddTool(std::unique_ptr<Tool> tool); void AddResourceProvider(std::unique_ptr<ResourceProvider> resource_provider); + void AddNotificationHandler(llvm::StringRef method, + NotificationHandler handler); + + llvm::Error Run(); protected: - virtual Capabilities GetCapabilities() = 0; + ServerCapabilities GetCapabilities(); using RequestHandler = std::function<llvm::Expected<Response>(const Request &)>; - using NotificationHandler = std::function<void(const Notification &)>; void AddRequestHandlers(); void AddRequestHandler(llvm::StringRef method, RequestHandler handler); - void AddNotificationHandler(llvm::StringRef method, - NotificationHandler handler); llvm::Expected<std::optional<Message>> HandleData(llvm::StringRef data); - llvm::Expected<Response> Handle(Request request); - void Handle(Notification notification); + llvm::Expected<Response> Handle(const Request &request); + void Handle(const Notification ¬ification); llvm::Expected<Response> InitializeHandler(const Request &); @@ -52,12 +67,21 @@ protected: llvm::Expected<Response> ResourcesListHandler(const Request &); llvm::Expected<Response> ResourcesReadHandler(const Request &); - std::mutex m_mutex; + void Received(const Request &) override; + void Received(const Response &) override; + void Received(const Notification &) override; + void OnError(llvm::Error) override; + void OnClosed() override; + + void TerminateLoop(); private: const std::string m_name; const std::string m_version; + std::unique_ptr<MCPTransport> m_transport_up; + lldb_private::MainLoop &m_loop; + llvm::StringMap<std::unique_ptr<Tool>> m_tools; std::vector<std::unique_ptr<ResourceProvider>> m_resource_providers; @@ -65,6 +89,42 @@ private: llvm::StringMap<NotificationHandler> m_notification_handlers; }; +class ServerInfoHandle; + +/// Information about this instance of lldb's MCP server for lldb-mcp to use to +/// coordinate connecting an lldb-mcp client. +struct ServerInfo { + std::string connection_uri; + + /// Writes the server info into a unique file in `~/.lldb`. + static llvm::Expected<ServerInfoHandle> Write(const ServerInfo &); + /// Loads any server info saved in `~/.lldb`. + static llvm::Expected<std::vector<ServerInfo>> Load(); +}; +llvm::json::Value toJSON(const ServerInfo &); +bool fromJSON(const llvm::json::Value &, ServerInfo &, llvm::json::Path); + +/// A handle that tracks the server info on disk and cleans up the disk record +/// once it is no longer referenced. +class ServerInfoHandle { +public: + ServerInfoHandle(); + explicit ServerInfoHandle(llvm::StringRef filename); + ~ServerInfoHandle(); + + ServerInfoHandle(ServerInfoHandle &&other); + ServerInfoHandle &operator=(ServerInfoHandle &&other) noexcept; + + /// ServerIinfoHandle is not copyable. + /// @{ + ServerInfoHandle(const ServerInfoHandle &) = delete; + ServerInfoHandle &operator=(const ServerInfoHandle &) = delete; + /// @} + +private: + llvm::SmallString<128> m_filename; +}; + } // namespace lldb_protocol::mcp #endif diff --git a/lldb/include/lldb/Protocol/MCP/Tool.h b/lldb/include/lldb/Protocol/MCP/Tool.h index 96669d135716..6c9f05161f8e 100644 --- a/lldb/include/lldb/Protocol/MCP/Tool.h +++ b/lldb/include/lldb/Protocol/MCP/Tool.h @@ -10,6 +10,7 @@ #define LLDB_PROTOCOL_MCP_TOOL_H #include "lldb/Protocol/MCP/Protocol.h" +#include "llvm/Support/Error.h" #include "llvm/Support/JSON.h" #include <string> @@ -20,7 +21,7 @@ public: Tool(std::string name, std::string description); virtual ~Tool() = default; - virtual llvm::Expected<lldb_protocol::mcp::TextResult> + virtual llvm::Expected<lldb_protocol::mcp::CallToolResult> Call(const lldb_protocol::mcp::ToolArguments &args) = 0; virtual std::optional<llvm::json::Value> GetSchema() const { diff --git a/lldb/include/lldb/Protocol/MCP/Transport.h b/lldb/include/lldb/Protocol/MCP/Transport.h new file mode 100644 index 000000000000..47c2ccfc44df --- /dev/null +++ b/lldb/include/lldb/Protocol/MCP/Transport.h @@ -0,0 +1,48 @@ +//===----------------------------------------------------------------------===// +// +// 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_PROTOCOL_MCP_TRANSPORT_H +#define LLDB_PROTOCOL_MCP_TRANSPORT_H + +#include "lldb/Host/JSONTransport.h" +#include "lldb/Protocol/MCP/Protocol.h" +#include "lldb/lldb-forward.h" +#include "llvm/ADT/FunctionExtras.h" +#include "llvm/ADT/StringRef.h" + +namespace lldb_protocol::mcp { + +/// Generic transport that uses the MCP protocol. +using MCPTransport = lldb_private::Transport<Request, Response, Notification>; + +/// Generic logging callback, to allow the MCP server / client / transport layer +/// to be independent of the lldb log implementation. +using LogCallback = llvm::unique_function<void(llvm::StringRef message)>; + +class Transport final + : public lldb_private::JSONRPCTransport<Request, Response, Notification> { +public: + Transport(lldb::IOObjectSP in, lldb::IOObjectSP out, + LogCallback log_callback = {}); + virtual ~Transport() = default; + + /// Transport is not copyable. + /// @{ + Transport(const Transport &) = delete; + void operator=(const Transport &) = delete; + /// @} + + void Log(llvm::StringRef message) override; + +private: + LogCallback m_log_callback; +}; + +} // namespace lldb_protocol::mcp + +#endif diff --git a/lldb/include/lldb/Symbol/SymbolFile.h b/lldb/include/lldb/Symbol/SymbolFile.h index bbc615d9fdc3..ff67e002e5b0 100644 --- a/lldb/include/lldb/Symbol/SymbolFile.h +++ b/lldb/include/lldb/Symbol/SymbolFile.h @@ -332,12 +332,12 @@ public: /// Resolves the function corresponding to the specified LLDB function /// call \c label. /// - /// \param[in] label The FunctionCallLabel to be resolved. + /// \param[in,out] label The FunctionCallLabel to be resolved. /// /// \returns An llvm::Error if the specified \c label couldn't be resolved. /// Returns the resolved function (as a SymbolContext) otherwise. virtual llvm::Expected<SymbolContext> - ResolveFunctionCallLabel(const FunctionCallLabel &label) { + ResolveFunctionCallLabel(FunctionCallLabel &label) { return llvm::createStringError("Not implemented"); } @@ -488,13 +488,16 @@ public: return false; }; - /// Get number of loaded/parsed DWO files. This is emitted in "statistics - /// dump" + /// Retrieves statistics about DWO files associated with this symbol file. + /// This function returns a DWOStats struct containing: + /// - The number of successfully loaded/parsed DWO files. + /// - The total number of DWO files encountered. + /// - The number of DWO CUs that failed to load due to errors. + /// If this symbol file does not support DWO files, all counts will be zero. /// /// \returns - /// A pair containing (loaded_dwo_count, total_dwo_count). If this - /// symbol file doesn't support DWO files, both counts will be 0. - virtual std::pair<uint32_t, uint32_t> GetDwoFileCounts() { return {0, 0}; } + /// A DWOStats struct with loaded, total, and error counts for DWO files. + virtual DWOStats GetDwoStats() { return {}; } virtual lldb::TypeSP MakeType(lldb::user_id_t uid, ConstString name, diff --git a/lldb/include/lldb/Target/CoreFileMemoryRanges.h b/lldb/include/lldb/Target/CoreFileMemoryRanges.h index 78d01acca324..ef56a02ddee2 100644 --- a/lldb/include/lldb/Target/CoreFileMemoryRanges.h +++ b/lldb/include/lldb/Target/CoreFileMemoryRanges.h @@ -50,7 +50,7 @@ class CoreFileMemoryRanges CoreFileMemoryRange> { public: /// Finalize and merge all overlapping ranges in this collection. Ranges - /// will be seperated based on permissions. + /// will be separated based on permissions. Status FinalizeCoreFileSaveRanges(); }; } // namespace lldb_private diff --git a/lldb/include/lldb/Target/InstrumentationRuntimeStopInfo.h b/lldb/include/lldb/Target/InstrumentationRuntimeStopInfo.h index 534516085091..dafa41c11327 100644 --- a/lldb/include/lldb/Target/InstrumentationRuntimeStopInfo.h +++ b/lldb/include/lldb/Target/InstrumentationRuntimeStopInfo.h @@ -24,6 +24,9 @@ public: return lldb::eStopReasonInstrumentation; } + std::optional<uint32_t> + GetSuggestedStackFrameIndex(bool inlined_stack) override; + const char *GetDescription() override; bool DoShouldNotify(Event *event_ptr) override { return true; } diff --git a/lldb/include/lldb/Target/RegisterContextUnwind.h b/lldb/include/lldb/Target/RegisterContextUnwind.h index b10a364823b8..52c28fd76da9 100644 --- a/lldb/include/lldb/Target/RegisterContextUnwind.h +++ b/lldb/include/lldb/Target/RegisterContextUnwind.h @@ -21,6 +21,7 @@ namespace lldb_private { class UnwindLLDB; +class ArchitectureArm; class RegisterContextUnwind : public lldb_private::RegisterContext { public: @@ -72,6 +73,25 @@ public: // above asynchronous trap handlers (sigtramp) for instance. bool BehavesLikeZerothFrame() const override; +protected: + // Provide a location for where THIS function saved the CALLER's register + // value, or a frame "below" this one saved it. That is, this function doesn't + // modify the register, it may call a function that does & saved it to stack. + // + // The ConcreteRegisterLocation type may be set to eRegisterNotAvailable -- + // this will happen for a volatile register being queried mid-stack. Instead + // of floating frame 0's contents of that register up the stack (which may or + // may not be the value of that reg when the function was executing), we won't + // return any value. + // + // If a non-volatile register (a "preserved" register, a callee-preserved + // register) is requested mid-stack, and no frames "below" the requested stack + // have saved the register anywhere, it is safe to assume that frame 0's + // register value is the same. + lldb_private::UnwindLLDB::RegisterSearchResult SavedLocationForRegister( + uint32_t lldb_regnum, + lldb_private::UnwindLLDB::ConcreteRegisterLocation ®loc); + private: enum FrameType { eNormalFrame, @@ -86,6 +106,8 @@ private: // UnwindLLDB needs to pass around references to ConcreteRegisterLocations friend class UnwindLLDB; + // Architecture may need to retrieve caller register values from this frame + friend class ArchitectureArm; // Returns true if we have an unwind loop -- the same stack frame unwinding // multiple times. @@ -130,27 +152,6 @@ private: void PropagateTrapHandlerFlagFromUnwindPlan( std::shared_ptr<const UnwindPlan> unwind_plan); - // Provide a location for where THIS function saved the CALLER's register - // value - // Or a frame "below" this one saved it, i.e. a function called by this one, - // preserved a register that this - // function didn't modify/use. - // - // The ConcreteRegisterLocation type may be set to eRegisterNotAvailable -- - // this will happen for a volatile register being queried mid-stack. Instead - // of floating frame 0's contents of that register up the stack (which may or - // may not be the value of that reg when the function was executing), we won't - // return any value. - // - // If a non-volatile register (a "preserved" register) is requested mid-stack - // and no frames "below" the requested - // stack have saved the register anywhere, it is safe to assume that frame 0's - // register values are still the same - // as the requesting frame's. - lldb_private::UnwindLLDB::RegisterSearchResult SavedLocationForRegister( - uint32_t lldb_regnum, - lldb_private::UnwindLLDB::ConcreteRegisterLocation ®loc); - std::optional<UnwindPlan::Row::AbstractRegisterLocation> GetAbstractRegisterLocation(uint32_t lldb_regnum, lldb::RegisterKind &kind); @@ -202,6 +203,8 @@ private: std::shared_ptr<const UnwindPlan> GetFullUnwindPlanForFrame(); + lldb::UnwindPlanSP TryAdoptArchitectureUnwindPlan(); + void UnwindLogMsg(const char *fmt, ...) __attribute__((format(printf, 2, 3))); void UnwindLogMsgVerbose(const char *fmt, ...) diff --git a/lldb/include/lldb/Target/StackFrame.h b/lldb/include/lldb/Target/StackFrame.h index d4104bfe49d2..cdbe8ae3c677 100644 --- a/lldb/include/lldb/Target/StackFrame.h +++ b/lldb/include/lldb/Target/StackFrame.h @@ -60,10 +60,9 @@ public: /// local variables. History, - /// An artificial stack frame (e.g. a synthesized result of inferring - /// missing tail call frames from a backtrace) with limited support for - /// local variables. - Artificial + /// An synthetic stack frame (e.g. a synthesized result from script + /// resource) possibly without support for local variables or register. + Synthetic }; /// Construct a StackFrame object without supplying a RegisterContextSP. @@ -109,7 +108,8 @@ public: StackFrame(const lldb::ThreadSP &thread_sp, lldb::user_id_t frame_idx, lldb::user_id_t concrete_frame_idx, lldb::addr_t cfa, bool cfa_is_valid, lldb::addr_t pc, Kind frame_kind, - bool behaves_like_zeroth_frame, const SymbolContext *sc_ptr); + bool artificial, bool behaves_like_zeroth_frame, + const SymbolContext *sc_ptr); StackFrame(const lldb::ThreadSP &thread_sp, lldb::user_id_t frame_idx, lldb::user_id_t concrete_frame_idx, @@ -398,7 +398,10 @@ public: /// /// \return /// true if this is an inlined frame. - bool IsInlined(); + virtual bool IsInlined(); + + /// Query whether this frame is synthetic. + bool IsSynthetic() const; /// Query whether this frame is part of a historical backtrace. bool IsHistorical() const; @@ -406,12 +409,12 @@ public: /// Query whether this frame is artificial (e.g a synthesized result of /// inferring missing tail call frames from a backtrace). Artificial frames /// may have limited support for inspecting variables. - bool IsArtificial() const; + virtual bool IsArtificial() const; /// Query whether this frame should be hidden from backtraces. Frame /// recognizers can customize this behavior and hide distracting /// system implementation details this way. - bool IsHidden(); + virtual bool IsHidden(); /// Language plugins can use this API to report language-specific /// runtime information about this compile unit, such as additional @@ -422,13 +425,13 @@ public: /// /// /// \return /// A C-String containing the function demangled name. Can be null. - const char *GetFunctionName(); + virtual const char *GetFunctionName(); /// Get the frame's demangled display name. /// /// /// \return /// A C-String containing the function demangled display name. Can be null. - const char *GetDisplayFunctionName(); + virtual const char *GetDisplayFunctionName(); /// Query this frame to find what frame it is in this Thread's /// StackFrameList. @@ -540,18 +543,7 @@ protected: bool HasCachedData() const; -private: - /// Private methods, called from GetValueForVariableExpressionPath. - /// See that method for documentation of parameters and return value. - lldb::ValueObjectSP LegacyGetValueForVariableExpressionPath( - llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic, - uint32_t options, lldb::VariableSP &var_sp, Status &error); - - lldb::ValueObjectSP DILGetValueForVariableExpressionPath( - llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic, - uint32_t options, lldb::VariableSP &var_sp, Status &error); - - /// For StackFrame only. + /// For StackFrame and derived classes only. /// \{ lldb::ThreadWP m_thread_wp; uint32_t m_frame_index; @@ -571,6 +563,10 @@ private: /// Does this frame have a CFA? Different from CFA == LLDB_INVALID_ADDRESS. bool m_cfa_is_valid; Kind m_stack_frame_kind; + /// Is this an artificial stack frame (e.g. a synthesized result of inferring + /// missing tail call frames from a backtrace) with limited support for + /// local variables. Orthogonal to `StackFrame::Kind`. + bool m_artificial; /// Whether this frame behaves like the zeroth frame, in the sense /// that its pc value might not immediately follow a call (and thus might @@ -584,6 +580,17 @@ private: StreamString m_disassembly; std::recursive_mutex m_mutex; +private: + /// Private methods, called from GetValueForVariableExpressionPath. + /// See that method for documentation of parameters and return value. + lldb::ValueObjectSP LegacyGetValueForVariableExpressionPath( + llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic, + uint32_t options, lldb::VariableSP &var_sp, Status &error); + + lldb::ValueObjectSP DILGetValueForVariableExpressionPath( + llvm::StringRef var_expr, lldb::DynamicValueType use_dynamic, + uint32_t options, lldb::VariableSP &var_sp, Status &error); + StackFrame(const StackFrame &) = delete; const StackFrame &operator=(const StackFrame &) = delete; }; diff --git a/lldb/include/lldb/Target/StackFrameList.h b/lldb/include/lldb/Target/StackFrameList.h index e5a6e942d743..ea9aab86b8ea 100644 --- a/lldb/include/lldb/Target/StackFrameList.h +++ b/lldb/include/lldb/Target/StackFrameList.h @@ -46,6 +46,9 @@ public: /// Mark a stack frame as the currently selected frame and return its index. uint32_t SetSelectedFrame(lldb_private::StackFrame *frame); + /// Resets the selected frame index of this object. + void ClearSelectedFrameIndex(); + /// Get the currently selected frame index. /// We should only call SelectMostRelevantFrame if (a) the user hasn't already /// selected a frame, and (b) if this really is a user facing @@ -172,6 +175,15 @@ protected: /// The currently selected frame. An optional is used to record whether anyone /// has set the selected frame on this stack yet. We only let recognizers /// change the frame if this is the first time GetSelectedFrame is called. + /// + /// Thread-safety: + /// This member is not protected by a mutex. + /// LLDB really only should have an opinion about the selected frame index + /// when a process stops, before control gets handed back to the user. + /// After that, it's up to them to change it whenever they feel like it. + /// If two parts of lldb decided they wanted to be in control of the selected + /// frame index on stop the right way to fix it would need to be some explicit + /// negotiation for who gets to control this. std::optional<uint32_t> m_selected_frame_idx; /// Protect access to m_selected_frame_idx. Always acquire after m_list_mutex diff --git a/lldb/include/lldb/Target/StackID.h b/lldb/include/lldb/Target/StackID.h index fddbc8e48dfd..c2a5d733dcd6 100644 --- a/lldb/include/lldb/Target/StackID.h +++ b/lldb/include/lldb/Target/StackID.h @@ -10,7 +10,6 @@ #define LLDB_TARGET_STACKID_H #include "lldb/Core/AddressRange.h" -#include "lldb/lldb-private.h" namespace lldb_private { @@ -18,15 +17,11 @@ class Process; class StackID { public: - // Constructors and Destructors StackID() = default; explicit StackID(lldb::addr_t pc, lldb::addr_t cfa, SymbolContextScope *symbol_scope, Process *process); - StackID(const StackID &rhs) - : m_pc(rhs.m_pc), m_cfa(rhs.m_cfa), m_symbol_scope(rhs.m_symbol_scope) {} - ~StackID() = default; lldb::addr_t GetPC() const { return m_pc; } @@ -51,41 +46,28 @@ public: void Dump(Stream *s); - // Operators - const StackID &operator=(const StackID &rhs) { - if (this != &rhs) { - m_pc = rhs.m_pc; - m_cfa = rhs.m_cfa; - m_symbol_scope = rhs.m_symbol_scope; - } - return *this; - } - protected: friend class StackFrame; void SetPC(lldb::addr_t pc, Process *process); void SetCFA(lldb::addr_t cfa, Process *process); - lldb::addr_t m_pc = - LLDB_INVALID_ADDRESS; // The pc value for the function/symbol for this - // frame. This will - // only get used if the symbol scope is nullptr (the code where we are - // stopped is not represented by any function or symbol in any shared - // library). - lldb::addr_t m_cfa = - LLDB_INVALID_ADDRESS; // The call frame address (stack pointer) value - // at the beginning of the function that uniquely - // identifies this frame (along with m_symbol_scope - // below) - SymbolContextScope *m_symbol_scope = - nullptr; // If nullptr, there is no block or symbol for this frame. - // If not nullptr, this will either be the scope for the - // lexical block for the frame, or the scope for the - // symbol. Symbol context scopes are always be unique - // pointers since the are part of the Block and Symbol - // objects and can easily be used to tell if a stack ID - // is the same as another. + /// The pc value for the function/symbol for this frame. This will only get + /// used if the symbol scope is nullptr (the code where we are stopped is not + /// represented by any function or symbol in any shared library). + lldb::addr_t m_pc = LLDB_INVALID_ADDRESS; + + /// The call frame address (stack pointer) value at the beginning of the + /// function that uniquely identifies this frame (along with m_symbol_scope + /// below) + lldb::addr_t m_cfa = LLDB_INVALID_ADDRESS; + + /// If nullptr, there is no block or symbol for this frame. If not nullptr, + /// this will either be the scope for the lexical block for the frame, or the + /// scope for the symbol. Symbol context scopes are always be unique pointers + /// since the are part of the Block and Symbol objects and can easily be used + /// to tell if a stack ID is the same as another. + SymbolContextScope *m_symbol_scope = nullptr; }; bool operator==(const StackID &lhs, const StackID &rhs); diff --git a/lldb/include/lldb/Target/Statistics.h b/lldb/include/lldb/Target/Statistics.h index 55dff8861a9a..d6983bb0b9d2 100644 --- a/lldb/include/lldb/Target/Statistics.h +++ b/lldb/include/lldb/Target/Statistics.h @@ -123,6 +123,25 @@ struct StatsSuccessFail { uint32_t failures = 0; }; +/// Holds statistics about DWO (Debug With Object) files. +struct DWOStats { + uint32_t loaded_dwo_file_count = 0; + uint32_t dwo_file_count = 0; + uint32_t dwo_error_count = 0; + + DWOStats &operator+=(const DWOStats &rhs) { + loaded_dwo_file_count += rhs.loaded_dwo_file_count; + dwo_file_count += rhs.dwo_file_count; + dwo_error_count += rhs.dwo_error_count; + return *this; + } + + friend DWOStats operator+(DWOStats lhs, const DWOStats &rhs) { + lhs += rhs; + return lhs; + } +}; + /// A class that represents statistics for a since lldb_private::Module. struct ModuleStats { llvm::json::Value ToJSON() const; @@ -153,8 +172,7 @@ struct ModuleStats { bool symtab_stripped = false; bool debug_info_had_variable_errors = false; bool debug_info_had_incomplete_types = false; - uint32_t dwo_file_count = 0; - uint32_t loaded_dwo_file_count = 0; + DWOStats dwo_stats; }; struct ConstStringStats { diff --git a/lldb/include/lldb/Target/StopInfo.h b/lldb/include/lldb/Target/StopInfo.h index 368ec51d8189..cdd6a6fbe6aa 100644 --- a/lldb/include/lldb/Target/StopInfo.h +++ b/lldb/include/lldb/Target/StopInfo.h @@ -97,6 +97,12 @@ public: /// and silently continue again one more time. virtual bool WasContinueInterrupted(Thread &thread) { return false; } + virtual uint32_t GetStopReasonDataCount() const { return 0; } + virtual uint64_t GetStopReasonDataAtIndex(uint32_t idx) { + // Handle all the common cases that have no data. + return 0; + } + // Sometimes the thread plan logic will know that it wants a given stop to // stop or not, regardless of what the ordinary logic for that StopInfo would // dictate. The main example of this is the ThreadPlanCallFunction, which diff --git a/lldb/include/lldb/Target/Thread.h b/lldb/include/lldb/Target/Thread.h index 6ede7fa301a8..688c056da263 100644 --- a/lldb/include/lldb/Target/Thread.h +++ b/lldb/include/lldb/Target/Thread.h @@ -479,6 +479,11 @@ public: bool SetSelectedFrameByIndexNoisily(uint32_t frame_idx, Stream &output_stream); + /// Resets the selected frame index of this object. + void ClearSelectedFrameIndex() { + return GetStackFrameList()->ClearSelectedFrameIndex(); + } + void SetDefaultFileAndLineToSelectedFrame() { GetStackFrameList()->SetDefaultFileAndLineToSelectedFrame(); } diff --git a/lldb/include/lldb/Target/UnwindLLDB.h b/lldb/include/lldb/Target/UnwindLLDB.h index 88180b37fd93..29b3ab9c9029 100644 --- a/lldb/include/lldb/Target/UnwindLLDB.h +++ b/lldb/include/lldb/Target/UnwindLLDB.h @@ -22,6 +22,7 @@ namespace lldb_private { class RegisterContextUnwind; +class ArchitectureArm; class UnwindLLDB : public lldb_private::Unwind { public: @@ -37,6 +38,7 @@ public: protected: friend class lldb_private::RegisterContextUnwind; + friend class lldb_private::ArchitectureArm; /// An UnwindPlan::Row::AbstractRegisterLocation, combined with the register /// context and memory for a specific stop point, is used to create a diff --git a/lldb/include/lldb/Utility/AnsiTerminal.h b/lldb/include/lldb/Utility/AnsiTerminal.h index 5c99341ad888..7db184ad6722 100644 --- a/lldb/include/lldb/Utility/AnsiTerminal.h +++ b/lldb/include/lldb/Utility/AnsiTerminal.h @@ -260,6 +260,11 @@ inline std::string TrimAndPad(llvm::StringRef str, size_t visible_length, return result; } +inline size_t ColumnWidth(llvm::StringRef str) { + std::string stripped = ansi::StripAnsiTerminalCodes(str); + return llvm::sys::locale::columnWidth(stripped); +} + } // namespace ansi } // namespace lldb_private diff --git a/lldb/include/lldb/ValueObject/DILAST.h b/lldb/include/lldb/ValueObject/DILAST.h index 709f0639135f..1d10755c46e3 100644 --- a/lldb/include/lldb/ValueObject/DILAST.h +++ b/lldb/include/lldb/ValueObject/DILAST.h @@ -21,7 +21,9 @@ enum class NodeKind { eArraySubscriptNode, eBitExtractionNode, eErrorNode, + eFloatLiteralNode, eIdentifierNode, + eIntegerLiteralNode, eMemberOfNode, eUnaryOpNode, }; @@ -178,6 +180,52 @@ private: int64_t m_last_index; }; +enum class IntegerTypeSuffix { None, Long, LongLong }; + +class IntegerLiteralNode : public ASTNode { +public: + IntegerLiteralNode(uint32_t location, llvm::APInt value, uint32_t radix, + bool is_unsigned, IntegerTypeSuffix type) + : ASTNode(location, NodeKind::eIntegerLiteralNode), + m_value(std::move(value)), m_radix(radix), m_is_unsigned(is_unsigned), + m_type(type) {} + + llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override; + + const llvm::APInt &GetValue() const { return m_value; } + uint32_t GetRadix() const { return m_radix; } + bool IsUnsigned() const { return m_is_unsigned; } + IntegerTypeSuffix GetTypeSuffix() const { return m_type; } + + static bool classof(const ASTNode *node) { + return node->GetKind() == NodeKind::eIntegerLiteralNode; + } + +private: + llvm::APInt m_value; + uint32_t m_radix; + bool m_is_unsigned; + IntegerTypeSuffix m_type; +}; + +class FloatLiteralNode : public ASTNode { +public: + FloatLiteralNode(uint32_t location, llvm::APFloat value) + : ASTNode(location, NodeKind::eFloatLiteralNode), + m_value(std::move(value)) {} + + llvm::Expected<lldb::ValueObjectSP> Accept(Visitor *v) const override; + + const llvm::APFloat &GetValue() const { return m_value; } + + static bool classof(const ASTNode *node) { + return node->GetKind() == NodeKind::eFloatLiteralNode; + } + +private: + llvm::APFloat m_value; +}; + /// This class contains one Visit method for each specialized type of /// DIL AST node. The Visit methods are used to dispatch a DIL AST node to /// the correct function in the DIL expression evaluator for evaluating that @@ -195,6 +243,10 @@ public: Visit(const ArraySubscriptNode *node) = 0; virtual llvm::Expected<lldb::ValueObjectSP> Visit(const BitFieldExtractionNode *node) = 0; + virtual llvm::Expected<lldb::ValueObjectSP> + Visit(const IntegerLiteralNode *node) = 0; + virtual llvm::Expected<lldb::ValueObjectSP> + Visit(const FloatLiteralNode *node) = 0; }; } // namespace lldb_private::dil diff --git a/lldb/include/lldb/ValueObject/DILEval.h b/lldb/include/lldb/ValueObject/DILEval.h index 45e29b3ddcd7..5a48c2c989f4 100644 --- a/lldb/include/lldb/ValueObject/DILEval.h +++ b/lldb/include/lldb/ValueObject/DILEval.h @@ -54,6 +54,15 @@ private: Visit(const ArraySubscriptNode *node) override; llvm::Expected<lldb::ValueObjectSP> Visit(const BitFieldExtractionNode *node) override; + llvm::Expected<lldb::ValueObjectSP> + Visit(const IntegerLiteralNode *node) override; + llvm::Expected<lldb::ValueObjectSP> + Visit(const FloatLiteralNode *node) override; + + llvm::Expected<CompilerType> + PickIntegerType(lldb::TypeSystemSP type_system, + std::shared_ptr<ExecutionContextScope> ctx, + const IntegerLiteralNode *literal); // Used by the interpreter to create objects, perform casts, etc. lldb::TargetSP m_target; diff --git a/lldb/include/lldb/ValueObject/DILLexer.h b/lldb/include/lldb/ValueObject/DILLexer.h index 9c1ba9768025..4345e6ce7f26 100644 --- a/lldb/include/lldb/ValueObject/DILLexer.h +++ b/lldb/include/lldb/ValueObject/DILLexer.h @@ -28,12 +28,14 @@ public: arrow, coloncolon, eof, + float_constant, identifier, + integer_constant, l_paren, l_square, minus, - numeric_constant, period, + plus, r_paren, r_square, star, diff --git a/lldb/include/lldb/ValueObject/DILParser.h b/lldb/include/lldb/ValueObject/DILParser.h index 9eda7bac4a36..90df109337dc 100644 --- a/lldb/include/lldb/ValueObject/DILParser.h +++ b/lldb/include/lldb/ValueObject/DILParser.h @@ -96,6 +96,9 @@ private: std::string ParseIdExpression(); std::string ParseUnqualifiedId(); std::optional<int64_t> ParseIntegerConstant(); + ASTNodeUP ParseNumericLiteral(); + ASTNodeUP ParseIntegerLiteral(); + ASTNodeUP ParseFloatingPointLiteral(); void BailOut(const std::string &error, uint32_t loc, uint16_t err_len); diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h index 483dce98ea42..af5656b3dcad 100644 --- a/lldb/include/lldb/lldb-forward.h +++ b/lldb/include/lldb/lldb-forward.h @@ -187,6 +187,7 @@ class SaveCoreOptions; class Scalar; class ScriptInterpreter; class ScriptInterpreterLocker; +class ScriptedFrameInterface; class ScriptedMetadata; class ScriptedBreakpointInterface; class ScriptedPlatformInterface; @@ -408,6 +409,8 @@ typedef std::shared_ptr<lldb_private::RecognizedStackFrame> typedef std::shared_ptr<lldb_private::ScriptSummaryFormat> ScriptSummaryFormatSP; typedef std::shared_ptr<lldb_private::ScriptInterpreter> ScriptInterpreterSP; +typedef std::shared_ptr<lldb_private::ScriptedFrameInterface> + ScriptedFrameInterfaceSP; typedef std::shared_ptr<lldb_private::ScriptedMetadata> ScriptedMetadataSP; typedef std::unique_ptr<lldb_private::ScriptedPlatformInterface> ScriptedPlatformInterfaceUP; |
